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 | 36 +- 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, 1392 insertions(+), 187 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 e8a0912d66c7..124a923c7668 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 47a9661bcb5c..2fe594531c96 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 c2aa27b34d9a..0f580f6bc1cf 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 d0dcbc3bfd3c..d9229be9fd67 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 5db79b98d1c6..c49fca0d44f0 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 @@ class EditorExportPlatform : public RefCounted { 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 @@ class EditorExportPlatform : public RefCounted { 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 @@ class EditorExportPlatform : public RefCounted { 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 cf96db6c2d76..6f0c85513098 100644 --- a/editor/export/editor_export_platform_pc.h +++ b/editor/export/editor_export_platform_pc.h @@ -62,7 +62,6 @@ class EditorExportPlatformPC : public EditorExportPlatform { 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 f0e841f30710..d8b1316613b9 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 99a59b49e3c1..be449dfa02ca 100644 --- a/methods.py +++ b/methods.py @@ -484,29 +484,29 @@ def mySpawn(sh, escape, cmd, args, env): def save_active_platforms(apnames, ap): for x in ap: - names = ["logo"] - if os.path.isfile(x + "/run_icon.png"): - 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 + "[]={" + 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 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 77cfa99aeec3..f44d10f96d1b 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!ia0vp^3LwnE3?yBabRA=0U~J8Fb`J1#c2+1T%1_J8No8QD zm{U8^*5j~)%+dJhqwBrctvgCi1kBax=<1jpwva>AY3+qo%_o<}Us$5(Sy(6%!_8Nx z6DAuor?j*{l;t45fVsK4tGMRJde4QfJ?zK-R+PUl{Lc5``e`3i>thU|n|nf(^inph zcDk-zb)j7#O<>}TmFGK?gyubdEoZmy-qDI_-*?)(@1JBW`9QcMeP&+75$Pkrv!|V_ zoNsZaYm!rqpH0>U)=v4Idb|G|+xcL_{PecolB{_aJ~|?|BJz^MOhO)cK3cITYs&lc zJJuB3d$eZS>NS=A{|+ViMMplfI?EvxaYV?4V?v-SXKF;p#Z?oNlw5xo^8cUkSp1~w zcAKEms_Wa*)loZK5(?ud-C^&)3OO4Jg4)VoXBMI_ovN=I=A~%SlRL#@0o7j{>;u# zzQLq)>K;ZuzQ&16sq3rv?B2b1d;NKn-@h4WeJ$F~6_~{ej2F%VkH}&m-37voQ4M3Imffls@ zx&QzFXXkVJ0`!<$NswPK!xT5?=`Od~SuT5RDazli_DWPRsCn6DA>o&ry?_31-8A9Q zVXf$IKa#7eGmMOxPKPU84LuwAmwUmY;|G8KI&e}YH*bFZs-|qQ&R0Ma8I!!-UEIWX z{d^1Ltn_qo45_%4BoWv!ku$bsCTr{IQ^)$W&L2F$&L;b-NpQRH|KKs<_WH()RQOLRjOQSljtyq0k+IL4tR7pnpg9~pjF12qs zJHgJ)p!a1q&)p^d@qaZ#He054KkZs}waE8o8r#uno9@c7&+5BzUE{S=;i5-6F&VqK y%(dQ4u$55ad~i}gpCP}^g5gJv^PdGjCo{<3*9-B^yu2CcXa-MLKbLh*2~7aQYP? 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 zcmV-K0lWT*P)eDi0)bOt_jY0jPN&wkqc z5~N_ty;pyy)G#ok7|)>k<`+;B4zJ#15CDoof!NkbG5M=*EqQ=|i%yjkEvjMQ1j|>2 zfBL`V_NV_}zD%89K703vK>?y5=F|U0_mM0BE7<=Es6Yut!51WW7hS=Udl)9X zge!10y8okMPX~YfTSM-Rv-{#%fP`PkYEEO;Q# diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index ea37278309a8..60b4fd633673 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 639f2416a574..64804b4baa38 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 zcmV+s1@8KZP)FjfrT~gb0a=KKO!f z`eHPOXoLrh_~L^x0V&{r47`wN5VW+#B3;mKX&1N9F5T(Q&dfc>hqBvlr`w>U%a@#F z=A7@&_s#vAJA`RSrBYih%W5a0^+ePlA{Em){{=+k07x-&_xSjDM`L5-@fnGUL}H0; z+sBxhWsbni+P3YE{{H@0K-_VhuDKGP4##oM6`8PY`)Iku^RR9ENPf)QnOT?H05j`U zDz!yfmbHzD)a^{P{{tdYmSt^KN~v|_7SBT|wO$cX?VU0}MD+@QF*V6N5v7oJ1T42E zkYf*mS;>YuJ@ft-IYdIh>)RuR+^Ii(e0b7@7Y1tU6h;jo7bfgjMR>L;44Ts3)*e0| zID;#0Kk6esOiCkcr15Zc6YjP)1}!*_(^ot@CnAr1-#fD7pe?CP@&4p!k$1Y%^6Ke9 z(Kkz3?aRxZhviKAauL^MM^25HC46f$(+aP%B3@M zqsFR0F1P`i0vJG7lF`;|1j;j^Ejk~`TpW)#+=pTRFMKt85ZC>|TWTBV0hjcqq0pT!^ z%@x;5L6E^b#RYD2qz%oHHUNTAeGymPpKv+553LouP#1b=vYklK01N?$5UU2h9@Y>E zcG7fY^Z=^Oh8fM)s8-Zew8EF?abf%o%%d18jHZGCB_a^!La-rVrLndK=p41dQ);Cm zo;{1Dp?fgqrAl%Pv9Q?Oj*S2FWL^Xc%-|^@iXlos!r0ir_;%<o-?b;p0mQ#BIBnb2GAf497?I;?e3?AR<^9 z*?>QCpU%kDoWq!R2$gDMA;%&x3vLP%gCUr3S!EFR-`j+@E}^Z-7aYAFVuWxrj8MhL0-< zfnZS!=Br1b5G~@lo)@Rlk&(WTX|@EOQU-XwA2&s7&8qAtG5~;RtxaaWF0-9L=F$N3 zu<7}Jk71e*-U$O9bGPX;A7SQ~Xr>R-+=8=?(?I}`92)8f8^+dhs(ZeFWMM4!qzM3{ z>GZB>b@d9*Fig0+FB3cKL1$>*o=ZeT-qJK!FE9<%)EiEnIOs)S5Z5SL0F*=X300000NkvXX Hu0mjfevwxk diff --git a/platform/ios/logo.svg b/platform/ios/logo.svg new file mode 100644 index 000000000000..47a72bcf49a4 --- /dev/null +++ b/platform/ios/logo.svg @@ -0,0 +1 @@ + diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp index 990351d13f6c..6737d777c66a 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 8277bb150565..92f37508db76 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); - - 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 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 } } + // 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 4d6737498b36..bfdb70ca7078 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 zcmV;A25|X_P)lQ61ni*UauD? zpL{a-9WgO6xa+RFP*PG75-^n4vnawRYep9d0Yf7o8$5+LQRA9#EVf1uv)Dyl0wh!xbemtasMsn;HTGPG2pK|VYiym zp+kp|fGUnFp@29>%N~h3P`IGc;c&Q|PG>5;yY8;bQ?PrC0jTz&waSFX=?VDht=v!@ zZ06XL7_nrY43{eYgamLAT~t(*DLOjZ6B`?w9tZ?3dH)_gT4o5K#(+H&qp@zV9>2Y2 zgIXItpAQs#yWMWjVif!*+7lEOnLar=*_xA+6DTMs$jQviy!w`#u5TOkMIqMo6cE#S7=7t*s5JJ^0{*p)>^5D3C0Q2^?=RDt^CTPmir-Wo3a2F1R2& zB_-uqdf3|1(gKzG`g(+0RaI4ZKUhKmr?6;~WP3iQ&ZwxlPfAKM<>uzPPCxzhq*G2g zvyG&MD$uC5NMH8wV4@#4i;wrm-grhOtfz6;Zk$M=|H?TiWwN>7ii zrKP1&7hZT_E<@kNX0u_&j2Z3T)7piE-nVSof{7C+;)4%90Ox`mt0G#Ng4;+cKYKKz zpiz9r?Cfk8Jy&yo8W*j5?z!i1B-^}sGnh6oeQFw1#xt6Ee3#FMBiDjL^gMEwtgI|Q z!=FVlZ}a$JaF&d8~K+lj_xT!B8At9Z)f5GGN;HjscLb$D5xe{9T z(@#HvjR@?Tp@Qs20hxUFYCoXl=EOwBE>4bTS1Za&v3~l{%{sjL^0NrH6)RRi%f9&X zD=>xaDNqZN>-|Dz0#5B3tvp0u0joxQV}r&rur-)FyND zQc^+6v15WA(LRB@@4g#{vK>2jV){QB2u=}L+e={ANF(Q!Ay`#Z|Ka1Vd$@LbcpZd` zP^2msN$&Bsco(%va^o;&<)QN@I^1#RT{x6Yoi-i)-n9VLc5pt)+BdhQrnGV5-|=|Q3!+1NEbkYZ3xnBIsHLoR=MDw8( z{?Erla>@DRR=pw~KO<9o^U3XE{NT65&H=BAx|>fi{z9+s=2^d0Mg7di^Q6^mdi9$- z#DYPuiD38V#Kubt#YbG@266^Tl|^pSI)P!C|seAQMjp`b1o?%3EG<<+>8vwEJNic(IkE?H2b-+ zM}^*dDJq)xLgUg%lAP<7YlUx4j{Gm!ASYSm0`@Sh^wz=}FcY diff --git a/platform/linuxbsd/run_icon.svg b/platform/linuxbsd/run_icon.svg new file mode 100644 index 000000000000..56465a0df3c1 --- /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 49c8c7758d07..99d077984d65 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; +} + +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() { +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 af7570c3943c..c4529d1af8fe 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 @@ class EditorExportPlatformMacOS : public EditorExportPlatform { 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 @@ class EditorExportPlatformMacOS : public EditorExportPlatform { 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 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 diff --git a/platform/macos/logo.svg b/platform/macos/logo.svg new file mode 100644 index 000000000000..759583d76b5e --- /dev/null +++ b/platform/macos/logo.svg @@ -0,0 +1 @@ + diff --git a/platform/macos/run_icon.svg b/platform/macos/run_icon.svg new file mode 100644 index 000000000000..c7067bb4b658 --- /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 ab0b20762f23..b7c4ed9bdb8e 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 zcmVip%03srLADGMXcKTptM8?sdTrIT2io0FezH5eI~f9AxHCID_nz-N=bpb26mz5xeyIvA*-3(W$e|rX&L(W#f@xv?LL{bf z&tgtw4)4AZQ!K=-GSY{>P6b=a%^z1?YsCrhWE!E z%lIPu{!`0Q5RnAZLl^c4p}YlvhlLOTFd4uD4Q+M62y1wPuuF#Md)(j6j`V(8jYLHP z>A`POLa4(K_9`^ipb0quU=neLzgDOTCbERf2=2q#(f)jRf0;ypw?pfXg{JpACIj2C zfy2Tj5geDah-DnZ^Xst2Lh-s10_nl?P0;d`Q1xEND&SB(v&K=o%9<5x=wzGu;{ZI6 z8R^aW%i%rl=o-49;`SaiUo$v^*_f8?kYU+mV#;H~f2@$A>CZ!@H;cIj1hDy8nCfN6 ztgg8aU;}_>!L%uFSd_!$I~%Mfp;Bu@k{;UFKo7LlNkY;&B?%M9p$@^)kNYI#5kUL( z5PlI()5#7eJsulOfbf16!V6VJP!a&TCP-5idJq6@jY(>6tdVmA=K55Un(K5*)h6id z#0XPuAcHZG@YR2MLhPwAC@)m&3>4t(}wmwID>6#ucwYCS8zM-Jo)$Uf92@X z^*MUyne9jk)g!}~6==(fv4V>B7()7K{C`@5_O)QPk5%*}|K=?^HFlejyUGTzk>Jc}j)Lp_P(1tu z!l|~>FfGpck=XJjhilb{a-Dvfktf}DW<{cGb~ty(09lzMCHY?`<#~H`l zDGe2A5#{kyB!-7ZEUWk`!MGg@|1^vu#Z&B6~zu+OoJ`$ zkw2C>{G{av`BB>{p#Rf0qiqQ);%|TF=+{4&WjXdklamnF2nN1!XPnv)C0r#tnref} z*;21lBf>Y_N6KgJ6D{LMkT}D{0!zXQui@o~Ou+5At0fSZ3}4vGeR6)kzzs=VjYju^ z1Y79V+>*>GL3<}u1LwaNmX`#I)7(d3wp&Tm4A@2vR;WyZ{X9?*W?Cs2}s z@L~oF_QMk6u!K~zaxsY){cw{&t}yP{8 diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 3087b12c4001..80a6a10f83d5 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 f11e38df0958..c5c153228626 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 zcmV;@1TFiCP)JEpq1lxLnC1VIVRVo#(o67ZCp&yoyNTc z6;$gMP8%?oo#hBnn zM#}$D!aVwYpH&lJW8-$Cz~8Gf<6PtYr62n?ou~yv#->v(nR+IbIHFe=n#==gG!v9e zW%2qYlL?a$fuSysFDfy%PV^S2m8YAjN8(eCyTsYIF@b%IZ` z%i|VY0tD1&tugUyIAT;HB6y|h^OXO1sHi|wyd;cGXAb;stcYr& zeVtHHw6A*}*Gb&k{nb^(0eBTOR! z8%AaEW({|gK~Fo_;^gzVNAeEkJBx zE##3kHbOdEKoy@VZm9~y|9Np8?!$LB6O+pszfH9Tv0S)tM-FQND3Bke?7SPm2_UHV zR5#R-rTh!C7`^XC)E7W-Xl&~=5}0ee?lw>lX)zF`UZK8bt_j_o$%F6s4V>Up1&vjm zMgmO`iKrcuem$4K?E^~VCQ7P#lt?}tpdafWXGQTlPnDp6g4+9Rz&-Ikn&3KDYyg#K*09Z z5^xQR&YhCIF#(SP#hH8){!UB)n}j5}P>rQlAb0eIxE0q#2m4{Dk7UL5s5^MAG~&_*iiC0OJ-CEqm%~W*J&{~eMqN#1tXBcVh3cpeBisYvji#Aa zz#B+aHR-c=gH1rGYYf$}eI4otq8lw4038`5|NJbdteNbc1|~eH;ku@y$C8AGH+F4W)wyhgW`yykcyx{&b9(P zX1r8S)#- w_W)9XKHq)$07*qoM6N<$g8z{{i2wiq diff --git a/platform/web/logo.svg b/platform/web/logo.svg new file mode 100644 index 000000000000..567b6f3c7769 --- /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 zcmV+-0p0$IP)YNn{n!Gd38wZaW?8~+dDJxuae>v#_(_rw(E z0q_dIa&h&C7yuX!AYY80h=QZaK_ckmnh~Nj8?qIch(2JrQUe4raNJUer*#IoIy~$(I_xltoe}WlXZGxf`7uMS>%Vr o=0m2<2HR$j-R61evUhTUYMN3 diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 8f91756c0238..e76d719b57b5 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 89f56ffd9386..2add00c29742 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 a9e6d51b9d54..cae8ca6e69f7 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 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC { 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 zcmX|>c~Fx_6u|e(osgL8%SkwcN~IO5jtW)lvxVn`VP z09HhJSPbkvry7k2b=Z;0O8^kwMMth%4u}5>nZ+j3r$V4mc|szC2`K<5VxiC@XsIB2 z`}D#lsiD&|uNndjo`gtufnLZ0Kw_~uvcOPrOxg_HVX@(q;J|s2=^E4Wkb%9RYD^!N z1awr)aQA^Q05kxIE-V&zLGbMSDrszr`-U8T-S>Rcb*ANki*<~uYWfg@NW8t!g<;;Q zMP53ywCc2|{u00SI^FVsj{U|jvd9$WA4rOO?EtgmR~q(`uW4~h)5Bv6nup!ep#f0i zFw|64Guk-B*R;B&@1d!i(B`ZBl4Fvr3VQKT==C+&MCILR8IG1=(y|lMGMrm{l&Nl` ztIZ6Jg;CHhNZjojo9eZ{XQts4M{VI09_AF8_^M`6fmy6ExfZsH)J;NlvslyWQFO$k z$RbQo!Zb-Vx-4fuTXAq&W;HjfnwMG4%i1T*sFY+@NHWXBsf8ZdduMIRkj!7^tu$~q zovZ>!tNS_iKeA1?sYgf1)-hi3 zVe!=EAk(~OGX3TOgbbsgFi}1?G-5_xm1|k2yRuxCuH~5TFxyAy))y2Ohc2HDC@hGH zWK#L+jjpLhu6m2;Kp*E2EZhycFN>!O?Qg;#ug_wKjDqn?8UfRJd?-n&&cKgyq z7fKO<6z&TPM8;kM+=1AfWa~?SIRHL^;1dA1BbbwT!cKrLIEmQ_*eifRmjl7=z-kBB zID$cDf`CsDYz`u3Ct^^K6QF_HiB<;@A16)O4$>6wpkpryRy)D!B)~x2iP*-8)>mX~ zyq}$R5FYVneoRy>KnO%CgHC2l<4I>OiPyF4=1@o^wyST*Cd1)#kL;5+35mjF3*CG} zvm%1NdE}hzmU2E>o>_wp{orh}KrP*w5X{z17 zuc|V}e}8>tW#wK&#a^BEOT8|VL5|;RFu-Byo?>mRZ%%%`YFBQKV#m&`?c0)<^dHZ` z`>|bJSU@6O%*=iS521M$>Te-q_zbna1^x zRqZ=nN}|wdH;fnZ;59#s2n$)KzF&GBDX&|LmODou=Ka)?rR>YB(uWn71_y_?DfFZ+ z+E}d*Z6LnO92V|Xd*J)nkg2WycY z=jE;+?gnXjz-3mxmh?DmM-o%qcC$k;^RAwBw#;}_`<|H&7Ai;Q_?%A}vA=zCGTSdO zZ7zPowrJ6!#@DRUd&IztZ-!$7FRq{C-?~`HO*!rDF(|L9qbiK?ahLW48vEX-O%K@G zoF{a$m&k%?W38+wECe zm15_dceVA7l-JRJJiWoI3?T<;LH4t{MI$wo^r+t5t4kao%VRFxyE-`5N48ykLUFnH z;IlTuFs5qg?zMG_u@|_X+&)|=>==0LAveEb1Lu|2Q-h1YTLIrQh*%yKW(!T${|ElV BOilm* diff --git a/platform/windows/logo.svg b/platform/windows/logo.svg new file mode 100644 index 000000000000..77a0b2076640 --- /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 000000000000..0897276ef739 --- /dev/null +++ b/platform/windows/run_icon.svg @@ -0,0 +1 @@ +