From cebefc9f5d26bc5207e6ba399e67a82f76216f13 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Sun, 8 May 2022 10:46:53 +0300 Subject: [PATCH] [Export] Add one-click deploy over SSH for the desktop exports. Add one-click deploy over SSH for the desktop exports. Add ZIP export option for Linux and Windows. Change export plugin icons to SVG format. --- editor/editor_property_name_processor.cpp | 1 + editor/editor_run_native.cpp | 4 +- editor/export/editor_export.cpp | 6 + editor/export/editor_export_platform.cpp | 233 +++++++++++++ editor/export/editor_export_platform.h | 10 +- editor/export/editor_export_platform_pc.h | 1 - editor/export/editor_export_plugin.cpp | 5 + methods.py | 34 +- platform/android/export/export_plugin.cpp | 23 +- platform/android/logo.png | Bin 968 -> 0 bytes platform/android/logo.svg | 1 + platform/android/run_icon.png | Bin 324 -> 0 bytes platform/android/run_icon.svg | 1 + platform/ios/export/export_plugin.cpp | 17 +- platform/ios/export/export_plugin.h | 1 - platform/ios/logo.png | Bin 1297 -> 0 bytes platform/ios/logo.svg | 1 + platform/linuxbsd/export/export.cpp | 1 - platform/linuxbsd/export/export_plugin.cpp | 344 +++++++++++++++++- platform/linuxbsd/export/export_plugin.h | 45 ++- platform/linuxbsd/logo.png | Bin 1679 -> 0 bytes platform/linuxbsd/logo.svg | 1 + platform/linuxbsd/run_icon.svg | 1 + platform/macos/export/export_plugin.cpp | 384 ++++++++++++++------- platform/macos/export/export_plugin.h | 59 +++- platform/macos/logo.png | Bin 7195 -> 0 bytes platform/macos/logo.svg | 1 + platform/macos/run_icon.svg | 1 + platform/uwp/export/export_plugin.cpp | 18 +- platform/uwp/logo.png | Bin 1519 -> 0 bytes platform/uwp/logo.svg | 1 + platform/web/export/export_plugin.cpp | 21 +- platform/web/export/export_plugin.h | 5 +- platform/web/logo.png | Bin 1234 -> 0 bytes platform/web/logo.svg | 1 + platform/web/run_icon.png | Bin 290 -> 0 bytes platform/web/run_icon.svg | 1 + platform/windows/export/export.cpp | 1 - platform/windows/export/export_plugin.cpp | 316 ++++++++++++++++- platform/windows/export/export_plugin.h | 36 +- platform/windows/logo.png | Bin 1536 -> 0 bytes platform/windows/logo.svg | 1 + platform/windows/run_icon.svg | 1 + 43 files changed, 1391 insertions(+), 186 deletions(-) delete mode 100644 platform/android/logo.png create mode 100644 platform/android/logo.svg delete mode 100644 platform/android/run_icon.png create mode 100644 platform/android/run_icon.svg delete mode 100644 platform/ios/logo.png create mode 100644 platform/ios/logo.svg delete mode 100644 platform/linuxbsd/logo.png create mode 100644 platform/linuxbsd/logo.svg create mode 100644 platform/linuxbsd/run_icon.svg delete mode 100644 platform/macos/logo.png create mode 100644 platform/macos/logo.svg create mode 100644 platform/macos/run_icon.svg delete mode 100644 platform/uwp/logo.png create mode 100644 platform/uwp/logo.svg delete mode 100644 platform/web/logo.png create mode 100644 platform/web/logo.svg delete mode 100644 platform/web/run_icon.png create mode 100644 platform/web/run_icon.svg delete mode 100644 platform/windows/logo.png create mode 100644 platform/windows/logo.svg create mode 100644 platform/windows/run_icon.svg diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index e8a0912d66c..124a923c766 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -211,6 +211,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["rmb"] = "RMB"; capitalize_string_remaps["rpc"] = "RPC"; capitalize_string_remaps["s3tc"] = "S3TC"; + capitalize_string_remaps["scp"] = "SCP"; capitalize_string_remaps["sdf"] = "SDF"; capitalize_string_remaps["sdfgi"] = "SDFGI"; capitalize_string_remaps["sdk"] = "SDK"; diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index 47a9661bcb5..2fe594531c9 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -154,7 +154,9 @@ Error EditorRunNative::run_native(int p_idx, int p_platform) { Error err = eep->run(preset, p_idx, flags); result_dialog_log->clear(); if (eep->fill_log_messages(result_dialog_log, err)) { - result_dialog->popup_centered_ratio(0.5); + if (eep->get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_ERROR) { + result_dialog->popup_centered_ratio(0.5); + } } return err; } diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index c2aa27b34d9..0f580f6bc1c 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -170,6 +170,12 @@ void EditorExport::_notification(int p_what) { case NOTIFICATION_PROCESS: { update_export_presets(); } break; + + case NOTIFICATION_EXIT_TREE: { + for (int i = 0; i < export_platforms.size(); i++) { + export_platforms.write[i]->cleanup(); + } + } break; } } diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index d0dcbc3bfd3..d9229be9fd6 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -1320,6 +1320,121 @@ Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObj return OK; } +void EditorExportPlatform::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 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 { + bool _is_executable = is_executable(dir.path_join(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; + // 0100000: regular file type + // 0000755: permissions rwxr-xr-x + // 0000644: permissions rw-r--r-- + uint32_t _mode = (_is_executable ? 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 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(); +} + Error EditorExportPlatform::save_pack(const Ref &p_preset, bool p_debug, const String &p_path, Vector *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { EditorProgress ep("savepack", TTR("Packing"), 102, true); @@ -1640,5 +1755,123 @@ bool EditorExportPlatform::can_export(const Ref &p_preset, S return valid; } +Error EditorExportPlatform::ssh_run_on_remote(const String &p_host, const String &p_port, const Vector &p_ssh_args, const String &p_cmd_args, String *r_out, int p_port_fwd) const { + String ssh_path = EditorSettings::get_singleton()->get("export/ssh/ssh"); + if (ssh_path.is_empty()) { + ssh_path = "ssh"; + } + + List args; + args.push_back("-p"); + args.push_back(p_port); + for (const String &E : p_ssh_args) { + args.push_back(E); + } + if (p_port_fwd > 0) { + args.push_back("-R"); + args.push_back(vformat("%d:localhost:%d", p_port_fwd, p_port_fwd)); + } + args.push_back(p_host); + args.push_back(p_cmd_args); + + String out; + int exit_code = -1; + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Executing: %s", ssh_path.utf8().get_data()); + for (const String &arg : args) { + OS::get_singleton()->print(" %s", arg.utf8().get_data()); + } + OS::get_singleton()->print("\n"); + } + + Error err = OS::get_singleton()->execute(ssh_path, args, &out, &exit_code, true); + if (out.is_empty()) { + print_verbose(vformat("Exit code: %d", exit_code)); + } else { + print_verbose(vformat("Exit code: %d, Output: %s", exit_code, out.replace("\r\n", "\n"))); + } + if (r_out) { + *r_out = out.replace("\r\n", "\n").get_slice("\n", 0); + } + if (err != OK) { + return err; + } else if (exit_code != 0) { + if (!out.is_empty()) { + print_line(out); + } + return FAILED; + } + return OK; +} + +Error EditorExportPlatform::ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid, int p_port_fwd) const { + String ssh_path = EditorSettings::get_singleton()->get("export/ssh/ssh"); + if (ssh_path.is_empty()) { + ssh_path = "ssh"; + } + + List args; + args.push_back("-p"); + args.push_back(p_port); + for (const String &E : p_ssh_args) { + args.push_back(E); + } + if (p_port_fwd > 0) { + args.push_back("-R"); + args.push_back(vformat("%d:localhost:%d", p_port_fwd, p_port_fwd)); + } + args.push_back(p_host); + args.push_back(p_cmd_args); + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Executing: %s", ssh_path.utf8().get_data()); + for (const String &arg : args) { + OS::get_singleton()->print(" %s", arg.utf8().get_data()); + } + OS::get_singleton()->print("\n"); + } + + return OS::get_singleton()->create_process(ssh_path, args, r_pid); +} + +Error EditorExportPlatform::ssh_push_to_remote(const String &p_host, const String &p_port, const Vector &p_scp_args, const String &p_src_file, const String &p_dst_file) const { + String scp_path = EditorSettings::get_singleton()->get("export/ssh/scp"); + if (scp_path.is_empty()) { + scp_path = "scp"; + } + + List args; + args.push_back("-P"); + args.push_back(p_port); + for (const String &E : p_scp_args) { + args.push_back(E); + } + args.push_back(p_src_file); + args.push_back(vformat("%s:%s", p_host, p_dst_file)); + + String out; + int exit_code = -1; + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Executing: %s", scp_path.utf8().get_data()); + for (const String &arg : args) { + OS::get_singleton()->print(" %s", arg.utf8().get_data()); + } + OS::get_singleton()->print("\n"); + } + + Error err = OS::get_singleton()->execute(scp_path, args, &out, &exit_code, true); + if (err != OK) { + return err; + } else if (exit_code != 0) { + if (!out.is_empty()) { + print_line(out); + } + return FAILED; + } + return OK; +} + EditorExportPlatform::EditorExportPlatform() { } diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 5db79b98d1c..c49fca0d44f 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -35,6 +35,7 @@ class EditorFileSystemDirectory; struct EditorProgress; #include "core/io/dir_access.h" +#include "core/io/zip_io.h" #include "editor_export_preset.h" #include "editor_export_shared_object.h" #include "scene/gui/rich_text_label.h" @@ -92,7 +93,6 @@ private: void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet &p_paths); void _export_find_dependencies(const String &p_path, HashSet &p_paths); - void gen_debug_flags(Vector &r_flags, int p_flags); static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector &p_data, int p_file, int p_total, const Vector &p_enc_in_filters, const Vector &p_enc_ex_filters, const Vector &p_key); static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector &p_data, int p_file, int p_total, const Vector &p_enc_in_filters, const Vector &p_enc_ex_filters, const Vector &p_key); @@ -126,6 +126,13 @@ protected: bool exists_export_template(String template_file_name, String *err) const; String find_export_template(String template_file_name, String *err = nullptr) const; void gen_export_flags(Vector &r_flags, int p_flags); + void gen_debug_flags(Vector &r_flags, int p_flags); + + virtual void zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); + + Error ssh_run_on_remote(const String &p_host, const String &p_port, const Vector &p_ssh_args, const String &p_cmd_args, String *r_out = nullptr, int p_port_fwd = -1) const; + Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const; + Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector &p_scp_args, const String &p_src_file, const String &p_dst_file) const; public: virtual void get_preset_features(const Ref &p_preset, List *r_features) const = 0; @@ -215,6 +222,7 @@ public: DEBUG_FLAG_VIEW_NAVIGATION = 16, }; + virtual void cleanup() {} virtual Error run(const Ref &p_preset, int p_device, int p_debug_flags) { return OK; } virtual Ref get_run_icon() const { return get_logo(); } diff --git a/editor/export/editor_export_platform_pc.h b/editor/export/editor_export_platform_pc.h index cf96db6c2d7..6f0c8551309 100644 --- a/editor/export/editor_export_platform_pc.h +++ b/editor/export/editor_export_platform_pc.h @@ -62,7 +62,6 @@ public: virtual Error modify_template(const Ref &p_preset, bool p_debug, const String &p_path, int p_flags) { return OK; }; virtual Error export_project_data(const Ref &p_preset, bool p_debug, const String &p_path, int p_flags); - void set_extension(const String &p_extension, const String &p_feature_key = "default"); void set_name(const String &p_name); void set_os_name(const String &p_name); diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index f0e841f3071..d8b1316613b 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -34,6 +34,7 @@ #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "editor/editor_paths.h" +#include "editor/editor_settings.h" #include "editor/export/editor_export_platform.h" #include "scene/resources/resource_format_text.h" @@ -226,4 +227,8 @@ void EditorExportPlugin::_bind_methods() { } EditorExportPlugin::EditorExportPlugin() { + GLOBAL_DEF("editor/export/convert_text_resources_to_binary", false); + + EDITOR_DEF("export/ssh/ssh", ""); + EDITOR_DEF("export/ssh/scp", ""); } diff --git a/methods.py b/methods.py index 99a59b49e3c..be449dfa02c 100644 --- a/methods.py +++ b/methods.py @@ -484,29 +484,29 @@ def use_windows_spawn_fix(self, platform=None): def save_active_platforms(apnames, ap): for x in ap: - names = ["logo"] - if os.path.isfile(x + "/run_icon.png"): - names.append("run_icon") + svg_names = [] + if os.path.isfile(x + "/logo.svg"): + svg_names.append("logo") + if os.path.isfile(x + "/run_icon.svg"): + svg_names.append("run_icon") - for name in names: - pngf = open(x + "/" + name + ".png", "rb") - b = pngf.read(1) - str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n" - str += " static const unsigned char _" + x[9:] + "_" + name + "[]={" + for name in svg_names: + svgf = open(x + "/" + name + ".svg", "rb") + b = svgf.read(1) + svg_str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n" + svg_str += " static const char *_" + x[9:] + "_" + name + '_svg = "' while len(b) == 1: - str += hex(ord(b)) - b = pngf.read(1) - if len(b) == 1: - str += "," + svg_str += "\\" + hex(ord(b))[1:] + b = svgf.read(1) - str += "};\n" + svg_str += '";\n' - pngf.close() + svgf.close() # NOTE: It is safe to generate this file here, since this is still executed serially - wf = x + "/" + name + ".gen.h" - with open(wf, "w") as pngw: - pngw.write(str) + wf = x + "/" + name + "_svg.gen.h" + with open(wf, "w") as svgw: + svgw.write(svg_str) def no_verbose(sys, env): diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 77cfa99aeec..f44d10f96d1 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 @@ -3234,8 +3240,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 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/logo.png b/platform/android/logo.png deleted file mode 100644 index 9c8be93646491ec027e2dc75a00bcc7bdb7a39ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 968 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGTQi-V13aCb6$*;-(=u~X z85k<&)K0YZIP4&EG(P(1dM|eCj*=4rb9FkpIwprL^L@B}+Q-!T7(?jho)9Iy zlufIhu4`9aXctHmm^fqQ`OYMvd5>Sq+3mY`v|`%#o%ZhgCmBmV5bj8ynHOxTuyyP&GkVl@6RxHYz z^8WmeH3jz`t(mrZO{M?8LkWJ-k4nJFm!=1 z;}oU9+YAg0R|9-PTp1V`jPA7>-)lC!+iHBb#qe&k(Y+Rx+mjsjHyGV(F#&T8@3t7< z2Z`(7Z8N&pVtBhv_hGxy-Dcx^Eynj-jPA8qK5j630+KR%01`C12NE;{OBvm5HM$4V zYxuCu=q^YcWCB>k=w7Sg{TAZ~twwj682*E-GrHemaKGK?Zj14~79)^jK}1Ndz`byf_Dla|PUr+l3zy`A4)q69e3ni;`Rr4xlihUPMIq}B zERE{8wqo^FY2O_oQ6(AW4=%jDxYWMk>;yYEgWi|fJa?D)$N$v~*=(8G{j_V@)gs@U zX>3QQZMrMRKCAD>b&c0fg^M2P#ANK^GS_-H!B#?v^TA00eTMut3x*#x&VLsCoXjA9 XUoXTv^YUf}1_lOCS3j3^P6 diff --git a/platform/android/run_icon.png b/platform/android/run_icon.png deleted file mode 100644 index b687c9ac313901524733dec5af4395dc2a0d9a93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+a(lWshE&{2ZeTuUA;_kt zkdl(XxM>5!vS|kA4@6EC?ov|n%Gsbf;r9OLm+J*;KBpfnVY4gAJU93C`MO(wgq9?n z+8bWK{EC6WVu|w$emxTp5aL^ZFGYZ%%c=3kHBZBztMha#8ydR(ri$pUN_@a_I=r?1 z*ZwEpmj91GP_@6|=IFra8P78J!|Itf96keV>nbMH&z)cXS=>${@*n*dso8}dFC($f#lN%4k}DY fU{-m^zz})uuGc@c9WNLd7#KWV{an^LB{Ts5_4|xo diff --git a/platform/android/run_icon.svg b/platform/android/run_icon.svg new file mode 100644 index 00000000000..24d930fece3 --- /dev/null +++ b/platform/android/run_icon.svg @@ -0,0 +1 @@ + diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index ea37278309a..60b4fd63367 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 &p_preset, List *r_features) const { // Vulkan and OpenGL ES 3.0 both mandate ETC2 support. @@ -1914,7 +1921,15 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref 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 639f2416a57..64804b4baa3 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 966d8aa70a08ebb8d228779128b935169e740ff7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1297 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}oCO|{#S9EgIUvk1XXD=i$IopwK(@_!|nv}Lrg z_xyO&Y{;njpq^*XgbaZ#TaGHlUdYQ7-+n>w?#pxM&e$Gr-*h`~`RxTuPEVhc@@&rg zbKlSYoo(Hc>9I5@TK9C8`l7EcR}_>)FI)a+b$P3s+sA{M>G1 z>bAXw?^_iu3o;#CT=vPlnRxi5vFgtIOXCIF*NaMW=p_iLqiN{Y7boW&e|2i9ana&qs~q%pFwSI>a0s7t@mdbcDdUB@R`MrfWctnTv?u=8 zuQ9C=xc>KGbe8lB#u*GA1!qOCT)%UR?WP2$;#p4t4(sV}T<%zQ#~ZOP(Clk{mUTz8 zj)&Xnd$T}|Vz7&X(ax}=0vwDF4JMowz3$~W;h@b?*1X~+yk{qxF*F6%3WndEUtqbL zf34{50P(7fO>;diFz_&T2&`t?CwEPNv+Pny$`^)JXWJytURx1%C1lH?>CX!j|L~q! zETPr2tcg)oN#L2&2E~M>8@I8&klA`LIBTiWJlpQ23#$#DE%hqm*|_1xoxYy`pHt#k zxXwJ#RP0jpayWG3#(^JYFWdrat75*hJ$xbEvC8#FVh+E_Z`GYFjY1;t4)Po|Z(ktL z=5VI-wRw2x!^hs7M{;-1C^p_y#dF;K+oOxG!WEPvByTp@7tKF+;_95kGs-!pW+f{g zS2Ezv;}ULGoKX;&D)8<8?UwD2_a_zCvdn%pZQ;E+{Ncw6ZvN%9J?ELTu1c^pOLB%} z_8Hk1MfVTCKPP80(|=*m+6E84zL*0u>{_B8XHGC_N_Fa=>9|-~x%Oee3f24t>Wdu} z)~uZs)5LS|=SK^nIQ0c=f{NT5FG#K7$#3wSEWg$!XJ?1W{I-5=j>d#Q?q{pr7YOJ+ z+Br{r`NfG7_f5#$#xgI6tzw_Nxoh^>Rd3Xd7#cFx`ljvE+dS9d*(HY08(#jb>rb4; ze~*({u6X-PWBJ5q(=(RwU%u0{x$m+g!vq diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp index 990351d13f6..6737d777c66 100644 --- a/platform/linuxbsd/export/export.cpp +++ b/platform/linuxbsd/export/export.cpp @@ -36,7 +36,6 @@ void register_linuxbsd_exporter() { Ref 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 8277bb15056..92f37508db7 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 &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) { Ref f = FileAccess::open(p_path, FileAccess::WRITE); @@ -49,26 +58,47 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref &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); + bool export_as_zip = p_path.ends_with("zip"); + String pkg_name; + if (String(GLOBAL_GET("application/config/name")) != "") { + pkg_name = String(GLOBAL_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 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; } - String app_name; - if (String(GLOBAL_GET("application/config/name")) != "") { - app_name = String(GLOBAL_GET("application/config/name")); - } else { - app_name = "Unnamed"; - } - 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 } } + // ZIP project. + if (export_as_zip) { + if (FileAccess::exists(p_path)) { + OS::get_singleton()->move_to_trash(p_path); + } + + Ref 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 EditorExportPlatformLinuxBSD::get_binary_extensions(const Ref &p_preset) const { List list; list.push_back(p_preset->get("binary_format/architecture")); + list.push_back("zip"); + return list; } void EditorExportPlatformLinuxBSD::get_export_options(List *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 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 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 EditorExportPlatformLinuxBSD::get_run_icon() const { + return run_icon; +} + +bool EditorExportPlatformLinuxBSD::poll_export() { + Ref preset; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref 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 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 &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 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 extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" "); + Vector 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 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 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 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 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 = 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 4d6737498b3..bfdb70ca707 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 extensions; + + struct SSHCleanupCommand { + String host; + String port; + Vector ssh_args; + String cmd_args; + bool wait = false; + + SSHCleanupCommand(){}; + SSHCleanupCommand(const String &p_host, const String &p_port, const Vector &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 run_icon; + Ref stop_icon; + + Vector 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 &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 get_binary_extensions(const Ref &p_preset) const override; virtual void get_export_options(List *r_options) override; + virtual List get_binary_extensions(const Ref &p_preset) const override; virtual Error export_project(const Ref &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 get_run_icon() const override; + virtual bool poll_export() override; + virtual Ref 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 &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/logo.png b/platform/linuxbsd/logo.png deleted file mode 100644 index 078654b757f2707aa8f1333c368ea48f73cc2cd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1679 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANN9VV*9IAr-gQPG_E?>@R+F zb#isi?a$kDZ*R-Jz3uw8T)u1D)*jqG`QYEY^DEz+Tr|D%`JbkDhiX?`@3im=R;fx- zNlQ{S`yr1hQRB_hTr)Zv#=6hYMx#!kje{KA8XPbgc zQj$_vSJ##E-4**MxSYOa;NsQLIPYe!Te6O8%!0gCtjG5rD?BNqzF0t!QEl=`N1>e` z?#8VbSD#;F^h4Ir&~V4=b-M$Cf`o(&rd;2waY#MeO`Ma_ZS&1HTOxE$^1Nq0Z}XYQ ztw-Xb+^@X5yB^-o-+y%f|9{d#2D2Z3(sc4|{(7W4kZVKKTD?h5=ilx4cuczRzwYw~ z%daOJK3H#^xP8{eh0g608npVf7dBY9otEpg3fQsW;=_U+bNte^_U?YS?sV{m>K?`w z-xh8?)pG15=Z}|b?*z!^Ji9SjVq?lYp6OcuT38AMV?#owXj)m_5|);pEyuy4SNUJg zHY$x{!LbHg;l&&CHpuPYo7Nm9Z8x7^q-OWsyz@ySf7EXYi-^s%v$VW9Yw~1a4GoPG zC(oSO^K07db@|d?Totc<5skQYxb>*8{oyN`ldtv$1_m--^VFYyI`ojn5o2CINmkjw zKGQF|zI-W3j*FW&Idp!-y~^iPiyr#z|MyFKneXhjzWSQxqukFIuU*T#%J#>D=7lCK zS2ZT+baBe$8*0_9c3M{OtdJSrY7L`81}j`1tMo{k_+&g_)R|E?l>cZ5m_>ydnW z@A9qImV4WCZZ>rat3PVyw_|wTB)wWGYG%`I4=wv@lf?@HYmU8nlOt{$UR*CFzP9?^ z&gTUxxA#;U&oDEn&5{Z^W<0C1X8QSdo{Ge>NbKs5;J5^bCujWOEK4&js=bP71;kKZqH}&Am^m&~H!OrVa=hy$+xoe}A zQpg1P&3r+rhEn}14(BIC?pUd>sHix1XZ^v)A0Mn-KF?{#+L)a}QQt26+c)-dyqUGI z>5UuX%$o06_6wdoJK}Ow&#x~%T>F&Q#=2h@&RXsH^=UK5c2VsxhtvChe9|^(eXAM3 zJ?Zsdr=p$P($YhgXe~Yy%(HmH;|;gZ#h9K5Y>u>)_%b=T_LHn3>%pb|i++oLR6Jk! z@}}IM53}A&*XgMkMzuQpLF{sS?ltoU-pt5eu5FVy%Vl`C_kU77!exs|Ka0x)jP_n zDr8%_1D0rsd%pX)twMaOXVGJWw5JO{t6SYEd>(5t#dofm?B9D^8LpNU*qyv(zJ2Mk zErpNnfAlZ<>#%~)gq7#FTTREUFU_nIwq>zTJNHBGp%@R_$NTmF6$L&d|N7qKWXwP7 zdQ|kan9HUgT_ZbYG0jm)EzG|tcr|T-+O+Pa6P0!bsP6r|;8nQox;>9ReQPT|DjL3} z-1c1aeZ38fx}DB%e)>abOGtWB-t`@?)bqC7S!3mH6QyZzg3EW!8KLPl|2Mq2@Y>Wc z|JC+IiLvsm)#}j~jSlzi+Q@aRRwu#m)wa(|be><%vy%?z-F^2EOaG0~vm9?ElU)v1 zSWhdt#2&vh?$2q{^{P`{J}(sccmB9@(aE1r!uBf3*((Rvd_MU$>Cc1hN6s;>@7gun z-|(N)`uEQw_lLUvJ#*~ylB+pYUuRn%X>wfOnehGjkz?N6N8Mu{vlX# diff --git a/platform/linuxbsd/run_icon.svg b/platform/linuxbsd/run_icon.svg new file mode 100644 index 00000000000..56465a0df3c --- /dev/null +++ b/platform/linuxbsd/run_icon.svg @@ -0,0 +1 @@ + diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 49c8c7758d0..99d077984d6 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 &p_preset, List *r_features) const { if (p_preset->get("texture_format/s3tc")) { @@ -207,6 +213,23 @@ void EditorExportPlatformMacOS::get_export_options(List *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 &p_source, Vector &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 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 &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 &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 &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 &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 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 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 &p_preset, String &r_error, bool &r_missing_templates) const { String err; bool valid = false; @@ -2008,9 +1917,242 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref EditorExportPlatformMacOS::get_run_icon() const { + return run_icon; } -EditorExportPlatformMacOS::~EditorExportPlatformMacOS() { +bool EditorExportPlatformMacOS::poll_export() { + Ref preset; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref 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 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 &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 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 extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" "); + Vector 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 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 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 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() { +#ifdef MODULE_SVG_ENABLED + Ref 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 = 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 af7570c3943..c4529d1af8f 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 @@ -52,6 +50,30 @@ class EditorExportPlatformMacOS : public EditorExportPlatform { Ref logo; + struct SSHCleanupCommand { + String host; + String port; + Vector ssh_args; + String cmd_args; + bool wait = false; + + SSHCleanupCommand(){}; + SSHCleanupCommand(const String &p_host, const String &p_port, const Vector &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 run_icon; + Ref stop_icon; + + Vector cleanup_commands; + OS::ProcessID ssh_pid = 0; + int menu_options = 0; + void _fix_plist(const Ref &p_preset, Vector &plist, const String &p_binary); void _make_icon(const Ref &p_preset, const Ref &p_icon, Vector &p_data); @@ -65,14 +87,17 @@ class EditorExportPlatformMacOS : public EditorExportPlatform { Ref &dir_access, bool p_sign_enabled, const Ref &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 &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 &p_preset, List *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 &p_options) const override; public: - virtual String get_name() const override { return "macOS"; } - virtual String get_os_name() const override { return "macOS"; } - virtual Ref 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 get_logo() const override { + return logo; + } virtual bool is_executable(const String &p_path) const override; virtual List get_binary_extensions(const Ref &p_preset) const override { @@ -133,8 +164,16 @@ public: virtual void resolve_platform_feature_priorities(const Ref &p_preset, HashSet &p_features) override { } + virtual Ref get_run_icon() const override; + virtual bool poll_export() override; + virtual Ref 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 &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/logo.png b/platform/macos/logo.png deleted file mode 100644 index b5a660b16576baa62f065654ced18439bbbba1d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7195 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANNv*Q!DyN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsHWvo;gX|vajX;XOZHT2TV;ieBqmSI?C7E892>6RrBiA%1zUJ zxVj#(?KsT1>+Aaeci+`l9DU8D*EubwcwfcRSy>q-r%Ly~TfYC&PrLOW{v3TT|M%VN zeg|>y`TXnm%=_3|_jBHYmoMMj|DM~IKKGN(mp>n)3~rs3lAr9F+WtNOLK=G*;`mwtEF{J3SgXa0)T)v@o_oBw}n zd-~q%ZR?i4dEi&qQ#G?Fis#J^<9V4gw|^46RdsJ_dE#uwEaBkYyRGb3@!Q4SR=2PH ze0%+8nYw}_B4GjZ6ng~x<2$9YB-osNzJ0S^!Q9JfrN|f1UDCdO^N-E>TmRlzv*GiX z9-ete9o{{^{rBCE$Kr=9n18y%YuCutBIan6}2Txd*c!%RdL;=S1x6(5R?=SFTNU;wRY{sOz+;U zQjeu&ZNJw1H5BkU&3^MA_wM{B|8(>C-v5ieb4dNk9LMkX z9nMLX`S0bC|JR*%GrpZ-TH4I1 z_IB}J)fW=i@|?f$N7i?DSvb2OqywPrARl`mXUdTal^XtR6t=8-AygF|| zVE@WDhD#40Q)v`b7WB55Ix(O3`_)rE`&>%7SEg^{O05p=6@JyWDKd0_7Q{gOJqTmX?#O?5ECbyc6H)*nBNT<81Ml*OR_4-`wVRrTVzv zf{Ll8(<+pbPUe*!kJ`So?n~W^e71xIYmU}GylwXV;GN|MAG}MAc;LTn=GFs&u|~=1 zj&EXwyzkxYzx%8x$M&IN*r68&JyuEDGYh$ID9PBL4dDOywNQX>X4>utJON?qzq8o# zmMoN4FxhqT3ga(n*~52DUmfwe8>^&zK zrMVS31@})ccDx~A+%@$N>jSfu)^0VX{h4iB=kYGeJ|EGziD~z0(<-f>lY1GZw?{nY zm%eM|xbyI>Ewk#^1njVQ)_rOwaC&9yAKFNOuRjH zVqJ`LnZfdcS6455nR&{W!|6!GAxkdn^+(NAB@O>wIJ=LFeeG-x?SC@X@7FD9n^_i{ zY#pIhym*1d2erFl|D+_o$@J!2XDc&T(my=WOQHOV#;li@UtE1I!B-Wn#+`fBvDPTc z-nyZ`i|0aSUDn;L$JRZ}kyzeyYY|hQVq2DkonOxu>t6Fqdy^R6mo59^S@k+5@{ri) zW5P!3Ur3(vl(MnfcUe7XSwwQ1jQ(fe75PaEYyEYfpIjb(=W~+am4oi`mRt769C`MV zp=$4hr56>w<#WGkE}XnuJ+g%*Z_1G`rddb6db3w=n!kPNlpM45=2>;?SMs>lc5}M&{+y7)wO8U-?MR5<8vHNO zxczR~4)+NLU#}%G6e*Ml^BsNde9&W~mD2WU6&^*`ugP6Hw)X3V^`{qG?ps%TGTu{H z>|ekMp3B)4J^1N1ESuC9Yj_s$liq*lZ?GHDt`TOqw3EAwKC+(9K-Z!(Z zxPInJxx%|8CFj_A46fhXnDIr7Eyc#dFZ__Evfd-B)fSr?^GvpX$X9=TzfQ|0mFJRR zszy#FhZ9Fq-jb3P6F#1Jc~UY}efqseHg{uo^cQ=t;c|Wb?0u6(h4;fZjaweOc@&&q zyDy@t=A-L(LBPmHaI^?u~e?i-sG z_e!mPGWFcXHz%$HKK*c=gRA(C;x&y}J+HDU?NS**HjfkbFmbQgp;`Y~SMP^Wq=jPi z`7~Ck^`2+naB5Wnfo-_A2Li>CUvlE*(Cej7J0d;6JOq%_hh+U#nyAYWkH*tz7>sa zzqc@ciNcincpdNFqovK(egdIezVgpcj+zD_mf}mRZX~Y!G6a*>%77+$-70;$JXkHasT_W@2c!~&R6<3E0s>& zRdlt>ntf*Djecv1(sMHw-`l0;viX^M_rsRWBC|wJKDr-yrgqh`p6U4Z%VCi-TbCFYsJa6c3x^CPadA=Jvga+)x&jri+bMs?ek@6*?s=+nQ%U^M)RA^|sy~baLNK)dkUW7!@U(S8G)^RA`o;d|)wWkJh!z8E>cP9x^()DxdM8 zPRvdJe$3uRs5Q(M!*(7x zlRmhUFRuPh!vBcdVRi2|#%teyotJ)RL5yV3dA7zSX1%SVvx56`cW<8RcT9^r=^9Jf z>V^KPP5w4VCAUVs=$lh-#8}h4vR8Wc1AFJs;{M;u)&JaWee){z?tjKR_1rPuS3XW+ zU|{UebaoE#basZd@E8~>=G0EK^*$UR(sqBDS9gd^iNMMU3#~d>H!ASyg>B>zby}Nn z_283b@=lZ6RE{1EEYP*+(5vJ9#q1K!9_}b0;&|j>{DbRCs`F;EL@oK+cDnM@z2dlc zmf`~EPWx=OI>z9;{j86crrz_gBNwC(3A~VYaY~zJsOy=zCH6i)$A=2}=W%a8N3F{< z%{Np~5IS#ocuS}E5#ia}4*u-e(LXh1>W`+1D@(YA_7}x|{4u%sVyfbqn`hF}Iu;*E z+SoZ&Elo98d7e_=%2|H@4(&Rw^*r}n)Xr0Xsz325SJq}ue8llqBS<5eS&Ap(gmX)i z*5B!g%6@_BN1fU>O)|NC&5n6pze{P?>!T~u#6&+$iDmz={fBk(>Io9x=O{h7^Yf8~ zyt6}w_=@W?&!68Ye5LO2L2}16>93-*9zPL(xwTEqnx$&R`O~x8mc3yNw|4kHS#kCz zZPrZgR;CYiZs(^mSG{jo7n)!9&(cr6@r~GoeXQU38y+fEuHX97`px-#IqnJn&VE$? z!TsWN$;NrrhFcjJ7}%1$-CY>|gW!U_%O^81FmM)lL>4nJh^c}wqi2xH2L=WP_7YED zSN10yOiYTRmis~_7#IY!JzX3_A~+cp1n$3Q7zG0z0*p8uz$mrjt0Mz5O9&$qqdCL> z|BU|`7%?ZP?;otv%4FCRv z^8zS8K-f?*kQ`X%|9?>ExC;vltq0kGY$u8XWY@g*XZ+9P!tj@Yfe>c-^@~AWkdfj4 zf3WXBK4kd+?>`uW9RzX|i2wgT!#}VZknfmWUcG+fi_-!BSvaB)zW$FT1YlzS88Y~O zGqABTfg=@U0T_b<4P-IYLXg;hIR8J`k!k?9}9|Nl%3OiUo3Gl6pnNFIh!9RN}T%7QRiDE(g5k>NKhCnO3O z7#IZE{xJOi4;qz%DnO$6_yrhZ;*-FE1xkD%d;ft_AJ`M1G6P950|Nu913;F86eFnt zC9E&3JP;9(1ONYL5akBNImkqaFpLDJct%DBPA)ERu>WUdWB>&e)Bk^v?7;+zMO1xg z4*1UiOO_yuK?b8L|Ife(F4g`qfYLQ66{5<6N_&V1C_2DqfT9AF=0LRxsGI=vK_){m ziUSz`F)%QKLIENNCP9XPj0Djja~S^pW8mdr1}8$00A^B)7G zLI8yTC>RlnQ5^99FQ~YLS_h^;sR)#7L56^;Lq-NrX#V{Dmw|-^q7|0t5JncOzo(32}!2=EfknIR{jNk$nIIR|7Vb5{s>mU{P!C}V}LlA zjZT1~>mL)S2m%$6AfGcafO7|uLWnnD0wBvFo`s2^&>&xc;`RZHD8mC#bTcq8h%tPE zLW`f7u4-wN1!?YqzVcDLG{2t zP@x2iPq6)H<-k8iMsPa-A_pe#OFA&f@hdWDUz^47ib;~;y@WBtFJ4IoW^Q3nf?!}~ zWMz2u>M6skmoFL4pE=3E#LUbPpOVQSC@2K>J;)G{-)k2vHR81%~-lzu*C5PEl$f#cB;hR+{gG4QZ_Wcd0|gkkS50R}ZS6$UT= zAO-;eA+S#L+yP2IAc^0<|DFZWFpTPeKmTqqF#p$vNg`-Sy1B<9#lRp2!j7OO7O2q& zrcJG0FepiJLaHTnn_-54>Nf@khSzUiuYn1HXp}VY|Hrot;1EDch!8$VHwZJJBu0im z{~7-O`N#0=9jL(h56)sB`*Go~U%qZ~wKY8rvKWR@9Pq3~dd2@=KhJ{G0Vqo%W00fJ z_@EpC$_dw>LGm{)i$UT)zW=y!`qc4Ms25=N!%BWQ|JV0BhXVfp`f(oObEtBd diff --git a/platform/macos/run_icon.svg b/platform/macos/run_icon.svg new file mode 100644 index 00000000000..c7067bb4b65 --- /dev/null +++ b/platform/macos/run_icon.svg @@ -0,0 +1 @@ + diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index ab0b20762f2..b7c4ed9bdb8 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 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 9017a30636d9b9d7bd6fc819a904fca2d2c50a5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1519 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}oCO|{#S9GGLLkg|>2BR0 z1_suJo-U3d5$;>VV>4tzMeNGc%FI@JnssJgI67P+&U>-)t`CHU39v9Kx_HRLe zW6dgGxi6lMpAv-QSr+nlEnT`cVWaDsTfY`;ap725vVCGy&<0<_sL87g4R7B4`PsU> zZj<$HYe}ZK9mdwy-{-xzem-x%ph)o&{<@W1x;H(Ws!lAFbvdV&bGIoo@t>2*vcmI8 zekpwK_X!5;99e60sePXxr|a$JsplDYxNBr}iLm~-@#4n2yvB5oqMU+_Z+HK&ZTo&) z_Vf>>-~WP72MSDNx#TSVjbq^+)`s?00R{uf1I)a+r3W~&E1EWVx4o#obN_7Lm%8X) z7gwg22lIoRRvZ@ire$(%0jDKHf>2@G{;(CCYMWY4bG$oz^Wwjniu#!@4cnbx_qQ(l zZmq_Adt>9_L#l!@)3=!Ase?DfgnIQS{?9bB=ZQs3++O^=(H#w7dV~ggSGp4G`dT?G3H|O`f*$p$bdCNAL@*iu^cQ~~8!0lBZ_~-1^n=#Y;k=mTClP2tRy*g`QhWY9p z!CYN4pI+mND&m>5QM9khYcqFlvfI(-{)YB`mC&=DKf9kYrv&(w+5= z#dG$D28lgXnxY+1^5~Mu)x(m9li314d_JR8`|0jkkLHy8jhn;zg#0rF8vNUq8knnH zytE+rvTc>5$Gr`ibJv}k&?9A@9@wrVcHsIBFN^Sp0(YNG^S8W_!#U%Br|jA*v$WV} z?y>c+Kl6M={q;5vrvJ}$txU^iPuP8mZNtO|XEOVmUjGTO`NMK(<+e)(y61jQyzxo3 zJ^N}$(cHRO6V>mQrMWK3DYGa)&Ja1*?I)L}bT97-U!z9DCGM9sxtz~&&Jq8={zOW* z^2fi-Z#dNoB;_i3Wdjme&7{M3|5Od0cICqZE5Y^Vs}khRw4XB^mpp!3lUFoT@ae~3 zmA1KNmkhbn&Q2F-WA9IU&~kEtL~hQyrN0fHi+!5XUcaF~seXNr)~zV z4^}>8e#iY(`a5CW$L

4j*CiKE$>D;nQ}X18;Y(Ru!1e6My4$ZBgC+1H7KGy>8!| zSfXECyEEyGren2w2=nv(;?t(HbX~sVp0F+2^9s)mH@>hHK23j}gAS|4^hF+Ks(JP< zsWGk4XHTEBxcr>ig11k|AH1LbH<{CXsVsv<^Rg}Myi3Gcv&+oI4`-j;&RhV5A6brE3qjtk2@Ymtob>2L&vIbzWWRP)J+Q+JRb`zO4epsVEy#m_Npr0 zeT)usOxu<09~$L67xRnzo3J1$F7|7~$E901JzufTITly(uWX@ez=0n*x!YJ5oHwU&wPi@#QnHbf7eodZ@J(KZ+&vUE{FV8shh)cqU(S*6k mp21?W?Az`?THl`jV^4av@TpWn@g)WZ1_n=8KbLh*2~7Y=B+`Ze diff --git a/platform/uwp/logo.svg b/platform/uwp/logo.svg new file mode 100644 index 00000000000..5bcbdcfcd40 --- /dev/null +++ b/platform/uwp/logo.svg @@ -0,0 +1 @@ + diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 3087b12c400..80a6a10f83d 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 io_fa; @@ -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 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 = 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 f11e38df095..c5c15322862 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/logo.png b/platform/web/logo.png deleted file mode 100644 index c046d87dc4c1a0675d03ddd40e56dc39f69bff4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1234 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANMUGd*1#Ln>~anZ~?9CtRRy zzuo5RJ-N5H<=!siDh%qGTeS2G*T&r2+lq7V@7`T(6LZAaw3zZ+V!Q!>R9&X#`s*Adu%U@ zXvo*}1mS1XeR6v{wJ+rNocXLResMwV+ zQzUYay*&0*_58lPFOs)cXZ7(sJu%I^&WO)wSFihH!vhNn7kuP%Tz;f@zC^bqyR`4d z+@mudxVxSDA9%>-SMB^z;e_PJWs48oUv2a_=JDU9^1sdIu3&XOwk%k8W|i7hi$&oQ zv(7MQnFvquN&Wasbu#B7;Ek+`zSL?yQO|i<1L@!=x=@F(UZ?Ro`$=-w+nuq(6st|>MVma zk2f=ze8qO3@rjs`#a(oS<&CaGPNMFUiKb>wR?!Zk^R@C;avk|!St$GNaJiZA)oF+qOmn?2G8I2BfW4>7+oap@oK%@TXxyIm1?D9FfpZ6Y)yd(S(w z0RBuvfu(T^_oc1lygTP)^ZOsXa~=e<_J+=NWAYVvYWwT3_>b@LOrc)S9d@NCZ(|jl zlzQ!e?nb2!=jvMmBEF2vW;RGJy~^Vp!}|a2@g?u~F(?JZHD0;G{2*8&&aAUrBUC-6 zJ36P*LuTJ{(;q4CB=R=TnDo=tkiTDF6_S(SM;YG{kp>HmDZg{r=90aGCOoJ#M{^Qsi%j@ zqf2aEEroAvceJETKkV_%c4ADBTdZ&DnsCM=t*=(_E8k&$(7SAA*a2yeP}7&2%bR^2 zR%Y{D-B2t0ihYr^t|Wu3q{{z4IxE)9{86vq)p6>F*`9kAw>8wg*ndxG6;r>NlYU3d zT2VUhv{I?5$o`7ChkpA9*Sz_rp^V#1S=$Q$NEx*;Ik~Y^MTxS=IlhQ=oM4KOVESk`@z~S6BCY!W9 znLjkE6@DFvJYRDsp4H-v#g&Nm1x*u!`@0rf^oQFSzB{_%`F{Q0ug5v882lHpNPdz& z^_?N8(culJ?uIgkk5vo)=q)ij8oyyF3j@R9FT3V)8H>DPU|?YIboFyt=akR{0RNFa Ai2wiq diff --git a/platform/web/logo.svg b/platform/web/logo.svg new file mode 100644 index 00000000000..567b6f3c776 --- /dev/null +++ b/platform/web/logo.svg @@ -0,0 +1 @@ + diff --git a/platform/web/run_icon.png b/platform/web/run_icon.png deleted file mode 100644 index 574abb0150137f38807f1e07c76876d0e4d81e47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 290 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+y!3Q&45_$PdsewYo`J#b z|%q_A2MLwei2$bK7daKYhRV`ev`|uRDM8y{_O_{eDE`IpYVe z14V^j+a(wz_!Q!M=5;hJKIN$N;_)oOMJCdlL}zr^CF}@dmgZf$iPzMn{`*Jv3A)jl ze;ZdUZ1XDOdzsK?y2wQSnZ&Xq%6}N!*JRW_SmHeG#oP5Vzj?x!{#0mtuD2_9-Mvo+ zG5iOwwLP>*-C1(qAm;tH&QiXguRHCRStqvE diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 8f91756c023..e76d719b57b 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -51,7 +51,6 @@ void register_windows_exporter() { Ref 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 89f56ffd938..2add00c2974 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 &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 } Error EditorExportPlatformWindows::export_project(const Ref &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 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 } } - if (p_preset->get("binary_format/embed_pck") && err == OK) { + if (embedded && err == OK) { Ref 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 } } + // ZIP project. + if (export_as_zip) { + if (FileAccess::exists(p_path)) { + OS::get_singleton()->move_to_trash(p_path); + } + + Ref 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 EditorExportPlatformWindows::get_binary_extensions(const Ref &p_preset) const { List 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 *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 *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 &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 EditorExportPlatformWindows::get_run_icon() const { + return run_icon; +} + +bool EditorExportPlatformWindows::poll_export() { + Ref preset; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref 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 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 &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 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 extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" "); + Vector 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 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 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 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 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 = 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 a9e6d51b9d5..cae8ca6e69f 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 ssh_args; + String cmd_args; + bool wait = false; + + SSHCleanupCommand(){}; + SSHCleanupCommand(const String &p_host, const String &p_port, const Vector &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 run_icon; + Ref stop_icon; + + Vector cleanup_commands; + OS::ProcessID ssh_pid = 0; + int menu_options = 0; + Error _process_icon(const Ref &p_preset, const String &p_src_path, const String &p_dst_path); Error _rcedit_add_data(const Ref &p_preset, const String &p_path, bool p_console_icon); Error _code_sign(const Ref &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 &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 get_run_icon() const override; + virtual bool poll_export() override; + virtual Ref 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 &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/logo.png b/platform/windows/logo.png deleted file mode 100644 index f06b4638506f55324fa982720312a18ed73c02c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1536 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2uxz6bb(xH2FCR$hK)PLLJ` z1_pK^F=j3v1_mYu1~v&XF?lO{(STId>e*7A>lKRo7#JA1gd~|cxfvMP7#P?Y7+84u z1!OFpB!be_8W%})ZWf)ePkjCbp2c^#*1Te`oDZ>tQ$&JGPKAMig@J*Cfq|Kumsil- zL9e7+DmXFAK+Q>m}AXr;mWzP=}im_Ormqn$YwM#FffSBJOlDh`5cj1 zr-du$$!0WjRLmBcb56K!xny<+XYEo31_q7Jb>i(CMOxR2w62kAT_x4JRLUx6eBX_gHvVv6D55UBrRM;W}OwQ zTc()T#Z$Lbc;;!L%6Y1}9USGeg)8TZ%mR5A9rgaw^E)rz~NTi46wwaa8Q2rRtL zv+O?S>Q`*M0-VZv3~am%3=HBjQp(;@(rGo)O^d}R>=u}MM0oxs&c$~)RzGJipU)vB z$08uYz`!8qdGTJwfs%?HM{?-|#=V_5x;Va;2{HSZYL zd|+Dpfoa`)<~8q_*Su$5^OkANJLa|Tm{-4NUi+2>#D32LQvaTF&1N{%zdWS=Jdn&e^=&jV4wH(-|8Z+y+z!6 zxHjxrU9@5Mikvm#c?%aVoI87RZ%=nukbdvCgw$9GZj<%NO z+J<0_f|8Q*(xQT#+`{bCgnPbQgN&0 zB4hiMK#}&3&+inUpPQYxJFC0NrMAVv!DB&A6UzpU7n4;u4uox3=;EPRJC(snWKDAV zyUP1BZ-keH-Kt}F&iH)(kNz|ECpIR2EmzPy-5Fo`xhT%8R%E9+Yf-FUrLvN?Tj)-O zlqbvne5~@1J2^>EZ)kp-HS^Mx=D$nyi^jYHkt^4h@XkKAzlg^oCV~5CgYX zNdwCRm)tlWi3OKeh^pUcV%gO?^K!#ao(u+yyyr$L`%><|`}$~8zP4q$@!D1EtgWqQ zed2Ao$!vM}^O<1F!{J8y^K8WgQ?@F}pHS|e!k#lTEc95j<;>kb(^U!oL zc*5C_cjBz-jHYc{cFVvagV$$mX1344cUQSWGYt(6FAx>UGGN{9sTUu)qcQbec0+ki zq0#rr)8D6j3i$Tql2Df;t5pL-{?jSe_xss0{C1Xk*S*(K4mx)8+=&;vS=XI=#KwL2 zw#@>jGi%CcY}~nSy~LVVYqgc%ov9XEao~Zx^1?U#3?I+V+kC?IkQ*oud%F6$taD0e F0s#KQOilm* diff --git a/platform/windows/logo.svg b/platform/windows/logo.svg new file mode 100644 index 00000000000..77a0b207664 --- /dev/null +++ b/platform/windows/logo.svg @@ -0,0 +1 @@ + diff --git a/platform/windows/run_icon.svg b/platform/windows/run_icon.svg new file mode 100644 index 00000000000..0897276ef73 --- /dev/null +++ b/platform/windows/run_icon.svg @@ -0,0 +1 @@ +