Merge pull request #63312 from bruvzg/one_click
[Export] Add one-click deploy over SSH for the desktop exports.
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1322,6 +1322,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<DirAccess> da = DirAccess::open(dir);
|
||||
da->list_dir_begin();
|
||||
String f = da->get_next();
|
||||
while (!f.is_empty()) {
|
||||
if (f == "." || f == "..") {
|
||||
f = da->get_next();
|
||||
continue;
|
||||
}
|
||||
if (da->is_link(f)) {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime();
|
||||
|
||||
zip_fileinfo zipfi;
|
||||
zipfi.tmz_date.tm_year = dt.year;
|
||||
zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
|
||||
zipfi.tmz_date.tm_mday = dt.day;
|
||||
zipfi.tmz_date.tm_hour = dt.hour;
|
||||
zipfi.tmz_date.tm_min = dt.minute;
|
||||
zipfi.tmz_date.tm_sec = dt.second;
|
||||
zipfi.dosDate = 0;
|
||||
// 0120000: symbolic link type
|
||||
// 0000755: permissions rwxr-xr-x
|
||||
// 0000644: permissions rw-r--r--
|
||||
uint32_t _mode = 0120644;
|
||||
zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
|
||||
zipfi.internal_fa = 0;
|
||||
|
||||
zipOpenNewFileInZip4(p_zip,
|
||||
p_folder.path_join(f).utf8().get_data(),
|
||||
&zipfi,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
Z_DEFLATED,
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
0,
|
||||
-MAX_WBITS,
|
||||
DEF_MEM_LEVEL,
|
||||
Z_DEFAULT_STRATEGY,
|
||||
nullptr,
|
||||
0,
|
||||
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
|
||||
0);
|
||||
|
||||
String target = da->read_link(f);
|
||||
zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
|
||||
zipCloseFileInZip(p_zip);
|
||||
} else if (da->current_is_dir()) {
|
||||
zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
|
||||
} else {
|
||||
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<FileAccess> fa = FileAccess::open(dir.path_join(f), FileAccess::READ);
|
||||
if (fa.is_null()) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.path_join(f)));
|
||||
return;
|
||||
}
|
||||
const int bufsize = 16384;
|
||||
uint8_t buf[bufsize];
|
||||
|
||||
while (true) {
|
||||
uint64_t got = fa->get_buffer(buf, bufsize);
|
||||
if (got == 0) {
|
||||
break;
|
||||
}
|
||||
zipWriteInFileInZip(p_zip, buf, got);
|
||||
}
|
||||
|
||||
zipCloseFileInZip(p_zip);
|
||||
}
|
||||
f = da->get_next();
|
||||
}
|
||||
da->list_dir_end();
|
||||
}
|
||||
|
||||
Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
|
||||
EditorProgress ep("savepack", TTR("Packing"), 102, true);
|
||||
|
||||
|
@ -1642,5 +1757,123 @@ bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, S
|
|||
return valid;
|
||||
}
|
||||
|
||||
Error EditorExportPlatform::ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &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<String> 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<String> &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<String> 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<String> &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<String> 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() {
|
||||
}
|
||||
|
|
|
@ -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<String> &p_paths);
|
||||
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
|
||||
|
||||
void gen_debug_flags(Vector<String> &r_flags, int p_flags);
|
||||
static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
|
||||
static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &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<String> &r_flags, int p_flags);
|
||||
void gen_debug_flags(Vector<String> &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<String> &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<String> &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<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const;
|
||||
|
||||
public:
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const = 0;
|
||||
|
@ -215,6 +222,7 @@ public:
|
|||
DEBUG_FLAG_VIEW_NAVIGATION = 16,
|
||||
};
|
||||
|
||||
virtual void cleanup() {}
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
|
||||
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ public:
|
|||
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { return OK; };
|
||||
virtual Error export_project_data(const Ref<EditorExportPreset> &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);
|
||||
|
||||
|
|
|
@ -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", "");
|
||||
}
|
||||
|
|
34
methods.py
|
@ -488,29 +488,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):
|
||||
|
|
|
@ -43,10 +43,16 @@
|
|||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "main/splash.gen.h"
|
||||
#include "platform/android/logo.gen.h"
|
||||
#include "platform/android/run_icon.gen.h"
|
||||
#include "platform/android/logo_svg.gen.h"
|
||||
#include "platform/android/run_icon_svg.gen.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -3238,8 +3244,17 @@ void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<
|
|||
}
|
||||
|
||||
EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
|
||||
logo = ImageTexture::create_from_image(memnew(Image(_android_logo)));
|
||||
run_icon = ImageTexture::create_from_image(memnew(Image(_android_run_icon)));
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false);
|
||||
logo = ImageTexture::create_from_image(img);
|
||||
|
||||
img_loader.create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false);
|
||||
run_icon = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
|
||||
devices_changed.set();
|
||||
plugins_changed.set();
|
||||
|
|
Before Width: | Height: | Size: 968 B |
1
platform/android/logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M22.904 20.192a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m-13.808 0a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m14.256-7.525 2.496-4.323a.52.52 0 1 0-.899-.52l-2.528 4.378a15.69 15.69 0 0 0-12.842 0L7.051 7.823a.52.52 0 1 0-.9.521l2.497 4.323C4.361 15 1.43 19.34 1 24.467h30c-.43-5.128-3.361-9.468-7.648-11.8" fill="#56d881"/></svg>
|
After Width: | Height: | Size: 422 B |
Before Width: | Height: | Size: 324 B |
1
platform/android/run_icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M11.187 9.936a.577.577 0 1 1 .578-.578.577.577 0 0 1-.578.578m-6.374 0a.577.577 0 1 1 .577-.578.577.577 0 0 1-.577.578m6.581-3.475 1.153-1.996a.24.24 0 1 0-.415-.24l-1.167 2.021a7.244 7.244 0 0 0-5.93 0L3.868 4.225a.24.24 0 1 0-.415.24l1.153 1.996a6.806 6.806 0 0 0-3.532 5.448h13.852a6.807 6.807 0 0 0-3.532-5.448" fill="#56d881" style="fill:#e0e0e0;fill-opacity:1"/></svg>
|
After Width: | Height: | Size: 468 B |
|
@ -32,6 +32,13 @@
|
|||
|
||||
#include "core/string/translation.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "platform/ios/logo_svg.gen.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
|
||||
// Vulkan and OpenGL ES 3.0 both mandate ETC2 support.
|
||||
|
@ -1926,7 +1933,15 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx
|
|||
}
|
||||
|
||||
EditorExportPlatformIOS::EditorExportPlatformIOS() {
|
||||
logo = ImageTexture::create_from_image(memnew(Image(_ios_logo)));
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.create_image_from_string(img, _ios_logo_svg, EDSCALE, upsample, false);
|
||||
logo = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
|
||||
plugins_changed.set();
|
||||
#ifndef ANDROID_ENABLED
|
||||
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
||||
|
|
|
@ -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"
|
||||
|
|
Before Width: | Height: | Size: 1.3 KiB |
1
platform/ios/logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M1 23.27h2.504V12.61H1zm1.247-12.057c.784 0 1.398-.603 1.398-1.358 0-.764-.614-1.367-1.398-1.367-.774 0-1.388.603-1.388 1.367 0 .755.614 1.358 1.388 1.358zm9.594-2.695c-4.233 0-6.888 2.886-6.888 7.502s2.654 7.492 6.888 7.492c4.224 0 6.88-2.876 6.88-7.492s-2.656-7.502-6.88-7.502zm0 2.212c2.585 0 4.234 2.052 4.234 5.29 0 3.228-1.649 5.28-4.234 5.28-2.594 0-4.233-2.052-4.233-5.28 0-3.238 1.639-5.29 4.233-5.29zm7.936 8.458c.11 2.675 2.303 4.324 5.641 4.324 3.51 0 5.723-1.73 5.723-4.485 0-2.162-1.247-3.379-4.194-4.053l-1.67-.382c-1.78-.422-2.513-.985-2.513-1.95 0-1.208 1.106-2.012 2.745-2.012 1.66 0 2.796.814 2.916 2.172H30.9c-.06-2.554-2.172-4.284-5.37-4.284-3.158 0-5.4 1.74-5.4 4.314 0 2.072 1.267 3.36 3.942 3.973l1.88.442c1.83.433 2.575 1.036 2.575 2.082 0 1.207-1.217 2.072-2.967 2.072-1.77 0-3.107-.875-3.268-2.213h-2.514z" fill="#bfbfbf"/></svg>
|
After Width: | Height: | Size: 929 B |
|
@ -36,7 +36,6 @@
|
|||
void register_linuxbsd_exporter() {
|
||||
Ref<EditorExportPlatformLinuxBSD> platform;
|
||||
platform.instantiate();
|
||||
platform->set_logo(ImageTexture::create_from_image(memnew(Image(_linuxbsd_logo))));
|
||||
platform->set_name("Linux/X11");
|
||||
platform->set_os_name("Linux");
|
||||
platform->set_chmod_flags(0755);
|
||||
|
|
|
@ -32,6 +32,15 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "platform/linuxbsd/logo_svg.gen.h"
|
||||
#include "platform/linuxbsd/run_icon_svg.gen.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
|
@ -49,26 +58,47 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP
|
|||
}
|
||||
|
||||
Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
|
||||
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
|
||||
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<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
|
||||
if (export_as_zip) {
|
||||
if (tmp_app_dir.is_null()) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
if (DirAccess::exists(tmp_dir_path)) {
|
||||
if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
|
||||
tmp_app_dir->erase_contents_recursive();
|
||||
}
|
||||
}
|
||||
tmp_app_dir->make_dir_recursive(tmp_dir_path);
|
||||
path = tmp_dir_path.path_join(p_path.get_file().get_basename());
|
||||
}
|
||||
|
||||
// Export project.
|
||||
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
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<EditorExportPreset>
|
|||
}
|
||||
}
|
||||
|
||||
// ZIP project.
|
||||
if (export_as_zip) {
|
||||
if (FileAccess::exists(p_path)) {
|
||||
OS::get_singleton()->move_to_trash(p_path);
|
||||
}
|
||||
|
||||
Ref<FileAccess> io_fa_dst;
|
||||
zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
|
||||
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
|
||||
|
||||
zip_folder_recursive(zip, tmp_dir_path, "", pkg_name);
|
||||
|
||||
zipClose(zip, nullptr);
|
||||
|
||||
if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
|
||||
tmp_app_dir->erase_contents_recursive();
|
||||
tmp_app_dir->change_dir("..");
|
||||
tmp_app_dir->remove(pkg_name);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -86,12 +137,51 @@ String EditorExportPlatformLinuxBSD::get_template_file_name(const String &p_targ
|
|||
List<String> EditorExportPlatformLinuxBSD::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
|
||||
List<String> list;
|
||||
list.push_back(p_preset->get("binary_format/architecture"));
|
||||
list.push_back("zip");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void EditorExportPlatformLinuxBSD::get_export_options(List<ExportOption> *r_options) {
|
||||
EditorExportPlatformPC::get_export_options(r_options);
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64,arm32,rv64,ppc64,ppc32"), "x86_64"));
|
||||
|
||||
String run_script = "#!/usr/bin/env bash\n"
|
||||
"export DISPLAY=:0\n"
|
||||
"unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n"
|
||||
"\"{temp_dir}/{exe_name}\" {cmd_args}";
|
||||
|
||||
String cleanup_script = "#!/usr/bin/env bash\n"
|
||||
"kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")\n"
|
||||
"rm -rf \"{temp_dir}\"";
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
|
||||
}
|
||||
|
||||
bool EditorExportPlatformLinuxBSD::is_elf(const String &p_path) const {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
|
||||
uint32_t magic = fb->get_32();
|
||||
return (magic == 0x464c457f);
|
||||
}
|
||||
|
||||
bool EditorExportPlatformLinuxBSD::is_shebang(const String &p_path) const {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
|
||||
uint16_t magic = fb->get_16();
|
||||
return (magic == 0x2123);
|
||||
}
|
||||
|
||||
bool EditorExportPlatformLinuxBSD::is_executable(const String &p_path) const {
|
||||
return is_elf(p_path) || is_shebang(p_path);
|
||||
}
|
||||
|
||||
Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
|
||||
|
@ -200,3 +290,235 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
|
|||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorExportPlatformLinuxBSD::get_run_icon() const {
|
||||
return run_icon;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformLinuxBSD::poll_export() {
|
||||
Ref<EditorExportPreset> preset;
|
||||
|
||||
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
|
||||
Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
|
||||
if (ep->is_runnable() && ep->get_platform() == this) {
|
||||
preset = ep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int prev = menu_options;
|
||||
menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
|
||||
if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
|
||||
if (menu_options == 0) {
|
||||
cleanup();
|
||||
} else {
|
||||
menu_options += 1;
|
||||
}
|
||||
}
|
||||
return menu_options != prev;
|
||||
}
|
||||
|
||||
Ref<ImageTexture> EditorExportPlatformLinuxBSD::get_option_icon(int p_index) const {
|
||||
return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
|
||||
}
|
||||
|
||||
int EditorExportPlatformLinuxBSD::get_options_count() const {
|
||||
return menu_options;
|
||||
}
|
||||
|
||||
String EditorExportPlatformLinuxBSD::get_option_label(int p_index) const {
|
||||
return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote Linux/BSD system");
|
||||
}
|
||||
|
||||
String EditorExportPlatformLinuxBSD::get_option_tooltip(int p_index) const {
|
||||
return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote Linux/BSD system");
|
||||
}
|
||||
|
||||
void EditorExportPlatformLinuxBSD::cleanup() {
|
||||
if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
|
||||
print_line("Terminating connection...");
|
||||
OS::get_singleton()->kill(ssh_pid);
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
}
|
||||
|
||||
if (!cleanup_commands.is_empty()) {
|
||||
print_line("Stopping and deleting previous version...");
|
||||
for (const SSHCleanupCommand &cmd : cleanup_commands) {
|
||||
if (cmd.wait) {
|
||||
ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
|
||||
} else {
|
||||
ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
ssh_pid = 0;
|
||||
cleanup_commands.clear();
|
||||
}
|
||||
|
||||
Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
|
||||
cleanup();
|
||||
if (p_device) { // Stop command, cleanup only.
|
||||
return OK;
|
||||
}
|
||||
|
||||
EditorProgress ep("run", TTR("Running..."), 5);
|
||||
|
||||
const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("linuxbsd");
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
if (!da->dir_exists(dest)) {
|
||||
Error err = da->make_dir_recursive(dest);
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
String host = p_preset->get("ssh_remote_deploy/host").operator String();
|
||||
String port = p_preset->get("ssh_remote_deploy/port").operator String();
|
||||
if (port.is_empty()) {
|
||||
port = "22";
|
||||
}
|
||||
Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
|
||||
Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
|
||||
|
||||
const String basepath = dest.path_join("tmp_linuxbsd_export");
|
||||
|
||||
#define CLEANUP_AND_RETURN(m_err) \
|
||||
{ \
|
||||
if (da->file_exists(basepath + ".zip")) { \
|
||||
da->remove(basepath + ".zip"); \
|
||||
} \
|
||||
if (da->file_exists(basepath + "_start.sh")) { \
|
||||
da->remove(basepath + "_start.sh"); \
|
||||
} \
|
||||
if (da->file_exists(basepath + "_clean.sh")) { \
|
||||
da->remove(basepath + "_clean.sh"); \
|
||||
} \
|
||||
return m_err; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
if (ep.step(TTR("Exporting project..."), 1)) {
|
||||
return ERR_SKIP;
|
||||
}
|
||||
Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
|
||||
if (err != OK) {
|
||||
DirAccess::remove_file_or_error(basepath + ".zip");
|
||||
return err;
|
||||
}
|
||||
|
||||
String cmd_args;
|
||||
{
|
||||
Vector<String> cmd_args_list;
|
||||
gen_debug_flags(cmd_args_list, p_debug_flags);
|
||||
for (int i = 0; i < cmd_args_list.size(); i++) {
|
||||
if (i != 0) {
|
||||
cmd_args += " ";
|
||||
}
|
||||
cmd_args += cmd_args_list[i];
|
||||
}
|
||||
}
|
||||
|
||||
const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
|
||||
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
|
||||
|
||||
print_line("Creating temporary directory...");
|
||||
ep.step(TTR("Creating temporary directory..."), 2);
|
||||
String temp_dir;
|
||||
err = ssh_run_on_remote(host, port, extra_args_ssh, "mktemp -d", &temp_dir);
|
||||
if (err != OK || temp_dir.is_empty()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
print_line("Uploading archive...");
|
||||
ep.step(TTR("Uploading archive..."), 3);
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
{
|
||||
String run_script = p_preset->get("ssh_remote_deploy/run_script");
|
||||
run_script = run_script.replace("{temp_dir}", temp_dir);
|
||||
run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
|
||||
run_script = run_script.replace("{exe_name}", basepath.get_file());
|
||||
run_script = run_script.replace("{cmd_args}", cmd_args);
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(basepath + "_start.sh", FileAccess::WRITE);
|
||||
if (f.is_null()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
f->store_string(run_script);
|
||||
}
|
||||
|
||||
{
|
||||
String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
|
||||
clean_script = clean_script.replace("{temp_dir}", temp_dir);
|
||||
clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
|
||||
clean_script = clean_script.replace("{exe_name}", basepath.get_file());
|
||||
clean_script = clean_script.replace("{cmd_args}", cmd_args);
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(basepath + "_clean.sh", FileAccess::WRITE);
|
||||
if (f.is_null()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
f->store_string(clean_script);
|
||||
}
|
||||
|
||||
print_line("Uploading scripts...");
|
||||
ep.step(TTR("Uploading scripts..."), 4);
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.sh", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"));
|
||||
if (err != OK || temp_dir.is_empty()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.sh", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh"));
|
||||
if (err != OK || temp_dir.is_empty()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
print_line("Starting project...");
|
||||
ep.step(TTR("Starting project..."), 5);
|
||||
err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"), &ssh_pid, (use_remote) ? dbg_port : -1);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
cleanup_commands.clear();
|
||||
cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh")));
|
||||
|
||||
print_line("Project started.");
|
||||
|
||||
CLEANUP_AND_RETURN(OK);
|
||||
#undef CLEANUP_AND_RETURN
|
||||
}
|
||||
|
||||
EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() {
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
|
||||
set_logo(ImageTexture::create_from_image(img));
|
||||
|
||||
img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
|
||||
run_icon = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
|
||||
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
||||
if (theme.is_valid()) {
|
||||
stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
|
||||
} else {
|
||||
stop_icon.instantiate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,19 +34,58 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/export/editor_export_platform_pc.h"
|
||||
#include "platform/linuxbsd/logo.gen.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
|
||||
HashMap<String, String> extensions;
|
||||
|
||||
struct SSHCleanupCommand {
|
||||
String host;
|
||||
String port;
|
||||
Vector<String> ssh_args;
|
||||
String cmd_args;
|
||||
bool wait = false;
|
||||
|
||||
SSHCleanupCommand(){};
|
||||
SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
|
||||
host = p_host;
|
||||
port = p_port;
|
||||
ssh_args = p_ssh_arg;
|
||||
cmd_args = p_cmd_args;
|
||||
wait = p_wait;
|
||||
};
|
||||
};
|
||||
|
||||
Ref<ImageTexture> run_icon;
|
||||
Ref<ImageTexture> stop_icon;
|
||||
|
||||
Vector<SSHCleanupCommand> cleanup_commands;
|
||||
OS::ProcessID ssh_pid = 0;
|
||||
int menu_options = 0;
|
||||
|
||||
bool is_elf(const String &p_path) const;
|
||||
bool is_shebang(const String &p_path) const;
|
||||
|
||||
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
|
||||
|
||||
public:
|
||||
void set_extension(const String &p_extension, const String &p_feature_key = "default");
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
|
||||
virtual void get_export_options(List<ExportOption> *r_options) override;
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
|
||||
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
|
||||
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
|
||||
virtual bool is_executable(const String &p_path) const override;
|
||||
|
||||
virtual Ref<Texture2D> get_run_icon() const override;
|
||||
virtual bool poll_export() override;
|
||||
virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
|
||||
virtual int get_options_count() const override;
|
||||
virtual String get_option_label(int p_index) const override;
|
||||
virtual String get_option_tooltip(int p_index) const override;
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
|
||||
virtual void cleanup() override;
|
||||
|
||||
EditorExportPlatformLinuxBSD();
|
||||
};
|
||||
|
||||
#endif // LINUXBSD_EXPORT_PLUGIN_H
|
||||
|
|
Before Width: | Height: | Size: 1.6 KiB |
1
platform/linuxbsd/logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="32" width="32"><path d="M13 31h6s3 0 6-6c2.864-5.727-6-17-6-17h-6S3.775 18.55 7 25c3 6 6 6 6 6z" fill="#fff"/><path d="M15.876 28.636c-.05.322-.116.637-.204.941-.142.496-.35.993-.659 1.416.32.02.649.023.985.007a9.1 9.1 0 0 0 .985-.007c-.309-.423-.516-.92-.659-1.416a7.666 7.666 0 0 1-.203-.94l-.123.003c-.04 0-.081-.003-.122-.004z" fill="#333"/><path d="M21.693 21.916c-.629.01-.934.633-1.497.7-.694.08-1.128-.722-2.11-.123-.98.6-1.826 7.473.45 8.409 2.274.935 6.506-4.545 6.23-5.662-.275-1.116-1.146-.853-1.582-1.399-.436-.545.003-1.41-.995-1.82a1.246 1.246 0 0 0-.496-.105zm-11.461 0a1.315 1.315 0 0 0-.421.105c-.998.41-.56 1.275-.995 1.82-.436.546-1.31.283-1.586 1.4-.275 1.116 3.956 6.596 6.232 5.66 2.275-.935 1.429-7.808.448-8.408-.981-.6-1.415.204-2.11.122-.584-.068-.888-.739-1.568-.7z" fill="#f4bb37"/><path d="M15.998.99c-2.934 0-4.657 1.79-4.982 4.204-.324 2.414.198 2.856-.614 5.328-.813 2.472-4.456 6.71-4.37 10.62.026 1.217.166 2.27.41 3.192.3-.496.743-.846 1.066-.995.253-.117.375-.173.432-.194.008-.062.04-.205.098-.485.08-.386.387-.99.91-1.386-.005-.12-.01-.239-.013-.363-.06-3.033 3.073-6.318 3.65-8.236.577-1.917.326-2.114.421-2.59.096-.477.463-1.032.992-1.475a.23.23 0 0 1 .15-.06c.482-.005.965 1.75 1.898 1.752.933 0 1.419-2.141 1.956-1.692.529.443.896.998.992 1.474.095.477-.156.674.42 2.591.578 1.918 3.708 5.203 3.648 8.236-.003.123-.008.24-.014.36.526.396.834 1.002.914 1.389.058.28.09.423.098.485.057.021.18.08.432.197.323.15.764.499 1.063.995.244-.922.387-1.976.414-3.195.085-3.91-3.562-8.148-4.374-10.62-.813-2.472-.287-2.914-.611-5.328C20.659 2.78 18.933.99 15.998.99z" fill="#333"/></svg>
|
After Width: | Height: | Size: 1.6 KiB |
1
platform/linuxbsd/run_icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M7.941 13.966a3.62 3.62 0 0 1-.096.444 2.129 2.129 0 0 1-.31.668c.15.01.305.01.464.003.16.008.314.007.465-.003a2.129 2.129 0 0 1-.31-.668 3.62 3.62 0 0 1-.097-.444l-.058.001-.058-.001z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M10.688 10.793c-.297.005-.441.299-.707.33-.328.038-.533-.34-.996-.058-.463.283-.862 3.528.212 3.97 1.074.442 3.072-2.146 2.942-2.673-.13-.527-.542-.403-.747-.66-.206-.258 0-.666-.47-.86a.588.588 0 0 0-.234-.05zm-5.411 0a.62.62 0 0 0-.199.05c-.47.193-.264.601-.47.859-.205.257-.618.133-.748.66s1.867 3.115 2.942 2.673c1.074-.442.674-3.687.211-3.97-.463-.283-.668.096-.995.058-.277-.032-.42-.349-.741-.33z" fill="#f4bb37" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M8 .914c-1.386 0-2.2.845-2.353 1.985-.153 1.14.094 1.348-.29 2.515s-2.103 3.168-2.063 5.013c.012.575.078 1.072.194 1.507a1.25 1.25 0 0 1 .503-.47 4.37 4.37 0 0 1 .204-.09c.004-.03.019-.098.046-.23.038-.182.183-.467.43-.654a4.773 4.773 0 0 1-.006-.172c-.029-1.431 1.45-2.982 1.723-3.888.272-.905.154-.998.199-1.223.045-.225.218-.487.468-.696a.11.11 0 0 1 .07-.028c.228-.003.456.826.897.827.44 0 .67-1.01.923-.799.25.21.423.471.468.696.045.225-.073.318.199 1.223.272.906 1.75 2.457 1.722 3.888-.001.058-.004.114-.007.17a1.2 1.2 0 0 1 .432.656c.027.132.042.2.046.23.027.01.085.037.204.092.153.07.36.236.502.47.115-.435.183-.933.195-1.509.04-1.845-1.681-3.846-2.065-5.013-.383-1.167-.135-1.376-.288-2.515C10.2 1.759 9.385.914 7.999.914Z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/></svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -38,8 +38,14 @@
|
|||
#include "core/string/translation.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "platform/macos/logo_svg.gen.h"
|
||||
#include "platform/macos/run_icon_svg.gen.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
#include "modules/modules_enabled.gen.h" // For svg and regex.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
|
||||
if (p_preset->get("texture_format/s3tc")) {
|
||||
|
@ -207,6 +213,23 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
|
|||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
|
||||
|
||||
String run_script = "#!/usr/bin/env bash\n"
|
||||
"unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n"
|
||||
"open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}";
|
||||
|
||||
String cleanup_script = "#!/usr/bin/env bash\n"
|
||||
"kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\")\n"
|
||||
"rm -rf \"{temp_dir}\"";
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
|
||||
}
|
||||
|
||||
void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) {
|
||||
|
@ -993,7 +1016,7 @@ Error EditorExportPlatformMacOS::_create_dmg(const String &p_dmg_path, const Str
|
|||
return OK;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
|
||||
bool EditorExportPlatformMacOS::is_shebang(const String &p_path) const {
|
||||
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
|
||||
uint16_t magic = fb->get_16();
|
||||
|
@ -1001,7 +1024,7 @@ bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
|
|||
}
|
||||
|
||||
bool EditorExportPlatformMacOS::is_executable(const String &p_path) const {
|
||||
return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shbang(p_path);
|
||||
return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shebang(p_path);
|
||||
}
|
||||
|
||||
Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
|
||||
|
@ -1082,7 +1105,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
} else {
|
||||
pkg_name = "Unnamed";
|
||||
}
|
||||
|
||||
pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
|
||||
|
||||
String export_format;
|
||||
|
@ -1684,7 +1706,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
|
||||
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
|
||||
|
||||
_zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
|
||||
zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
|
||||
|
||||
zipClose(zip, nullptr);
|
||||
}
|
||||
|
@ -1723,119 +1745,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
return err;
|
||||
}
|
||||
|
||||
void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
|
||||
String dir = p_folder.is_empty() ? p_root_path : p_root_path.path_join(p_folder);
|
||||
|
||||
Ref<DirAccess> da = DirAccess::open(dir);
|
||||
da->list_dir_begin();
|
||||
String f = da->get_next();
|
||||
while (!f.is_empty()) {
|
||||
if (f == "." || f == "..") {
|
||||
f = da->get_next();
|
||||
continue;
|
||||
}
|
||||
if (da->is_link(f)) {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime();
|
||||
|
||||
zip_fileinfo zipfi;
|
||||
zipfi.tmz_date.tm_year = dt.year;
|
||||
zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
|
||||
zipfi.tmz_date.tm_mday = dt.day;
|
||||
zipfi.tmz_date.tm_hour = dt.hour;
|
||||
zipfi.tmz_date.tm_min = dt.minute;
|
||||
zipfi.tmz_date.tm_sec = dt.second;
|
||||
zipfi.dosDate = 0;
|
||||
// 0120000: symbolic link type
|
||||
// 0000755: permissions rwxr-xr-x
|
||||
// 0000644: permissions rw-r--r--
|
||||
uint32_t _mode = 0120644;
|
||||
zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
|
||||
zipfi.internal_fa = 0;
|
||||
|
||||
zipOpenNewFileInZip4(p_zip,
|
||||
p_folder.path_join(f).utf8().get_data(),
|
||||
&zipfi,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
Z_DEFLATED,
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
0,
|
||||
-MAX_WBITS,
|
||||
DEF_MEM_LEVEL,
|
||||
Z_DEFAULT_STRATEGY,
|
||||
nullptr,
|
||||
0,
|
||||
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
|
||||
0);
|
||||
|
||||
String target = da->read_link(f);
|
||||
zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
|
||||
zipCloseFileInZip(p_zip);
|
||||
} else if (da->current_is_dir()) {
|
||||
_zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
|
||||
} else {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime();
|
||||
|
||||
zip_fileinfo zipfi;
|
||||
zipfi.tmz_date.tm_year = dt.year;
|
||||
zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
|
||||
zipfi.tmz_date.tm_mday = dt.day;
|
||||
zipfi.tmz_date.tm_hour = dt.hour;
|
||||
zipfi.tmz_date.tm_min = dt.minute;
|
||||
zipfi.tmz_date.tm_sec = dt.second;
|
||||
zipfi.dosDate = 0;
|
||||
// 0100000: regular file type
|
||||
// 0000755: permissions rwxr-xr-x
|
||||
// 0000644: permissions rw-r--r--
|
||||
uint32_t _mode = (is_executable(dir.path_join(f)) ? 0100755 : 0100644);
|
||||
zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
|
||||
zipfi.internal_fa = 0;
|
||||
|
||||
zipOpenNewFileInZip4(p_zip,
|
||||
p_folder.path_join(f).utf8().get_data(),
|
||||
&zipfi,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
Z_DEFLATED,
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
0,
|
||||
-MAX_WBITS,
|
||||
DEF_MEM_LEVEL,
|
||||
Z_DEFAULT_STRATEGY,
|
||||
nullptr,
|
||||
0,
|
||||
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
|
||||
0);
|
||||
|
||||
Ref<FileAccess> fa = FileAccess::open(dir.path_join(f), FileAccess::READ);
|
||||
if (fa.is_null()) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.path_join(f)));
|
||||
return;
|
||||
}
|
||||
const int bufsize = 16384;
|
||||
uint8_t buf[bufsize];
|
||||
|
||||
while (true) {
|
||||
uint64_t got = fa->get_buffer(buf, bufsize);
|
||||
if (got == 0) {
|
||||
break;
|
||||
}
|
||||
zipWriteInFileInZip(p_zip, buf, got);
|
||||
}
|
||||
|
||||
zipCloseFileInZip(p_zip);
|
||||
}
|
||||
f = da->get_next();
|
||||
}
|
||||
da->list_dir_end();
|
||||
}
|
||||
|
||||
bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
|
||||
String err;
|
||||
bool valid = false;
|
||||
|
@ -2012,9 +1921,242 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
|
|||
return valid;
|
||||
}
|
||||
|
||||
EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
|
||||
logo = ImageTexture::create_from_image(memnew(Image(_macos_logo)));
|
||||
Ref<Texture2D> EditorExportPlatformMacOS::get_run_icon() const {
|
||||
return run_icon;
|
||||
}
|
||||
|
||||
EditorExportPlatformMacOS::~EditorExportPlatformMacOS() {
|
||||
bool EditorExportPlatformMacOS::poll_export() {
|
||||
Ref<EditorExportPreset> preset;
|
||||
|
||||
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
|
||||
Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
|
||||
if (ep->is_runnable() && ep->get_platform() == this) {
|
||||
preset = ep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int prev = menu_options;
|
||||
menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
|
||||
if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
|
||||
if (menu_options == 0) {
|
||||
cleanup();
|
||||
} else {
|
||||
menu_options += 1;
|
||||
}
|
||||
}
|
||||
return menu_options != prev;
|
||||
}
|
||||
|
||||
Ref<ImageTexture> EditorExportPlatformMacOS::get_option_icon(int p_index) const {
|
||||
return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
|
||||
}
|
||||
|
||||
int EditorExportPlatformMacOS::get_options_count() const {
|
||||
return menu_options;
|
||||
}
|
||||
|
||||
String EditorExportPlatformMacOS::get_option_label(int p_index) const {
|
||||
return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote macOS system");
|
||||
}
|
||||
|
||||
String EditorExportPlatformMacOS::get_option_tooltip(int p_index) const {
|
||||
return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote macOS system");
|
||||
}
|
||||
|
||||
void EditorExportPlatformMacOS::cleanup() {
|
||||
if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
|
||||
print_line("Terminating connection...");
|
||||
OS::get_singleton()->kill(ssh_pid);
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
}
|
||||
|
||||
if (!cleanup_commands.is_empty()) {
|
||||
print_line("Stopping and deleting previous version...");
|
||||
for (const SSHCleanupCommand &cmd : cleanup_commands) {
|
||||
if (cmd.wait) {
|
||||
ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
|
||||
} else {
|
||||
ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
ssh_pid = 0;
|
||||
cleanup_commands.clear();
|
||||
}
|
||||
|
||||
Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
|
||||
cleanup();
|
||||
if (p_device) { // Stop command, cleanup only.
|
||||
return OK;
|
||||
}
|
||||
|
||||
EditorProgress ep("run", TTR("Running..."), 5);
|
||||
|
||||
const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("macos");
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
if (!da->dir_exists(dest)) {
|
||||
Error err = da->make_dir_recursive(dest);
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
String pkg_name;
|
||||
if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
|
||||
pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
|
||||
} else {
|
||||
pkg_name = "Unnamed";
|
||||
}
|
||||
pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
|
||||
|
||||
String host = p_preset->get("ssh_remote_deploy/host").operator String();
|
||||
String port = p_preset->get("ssh_remote_deploy/port").operator String();
|
||||
if (port.is_empty()) {
|
||||
port = "22";
|
||||
}
|
||||
Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
|
||||
Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
|
||||
|
||||
const String basepath = dest.path_join("tmp_macos_export");
|
||||
|
||||
#define CLEANUP_AND_RETURN(m_err) \
|
||||
{ \
|
||||
if (da->file_exists(basepath + ".zip")) { \
|
||||
da->remove(basepath + ".zip"); \
|
||||
} \
|
||||
if (da->file_exists(basepath + "_start.sh")) { \
|
||||
da->remove(basepath + "_start.sh"); \
|
||||
} \
|
||||
if (da->file_exists(basepath + "_clean.sh")) { \
|
||||
da->remove(basepath + "_clean.sh"); \
|
||||
} \
|
||||
return m_err; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
if (ep.step(TTR("Exporting project..."), 1)) {
|
||||
return ERR_SKIP;
|
||||
}
|
||||
Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
|
||||
if (err != OK) {
|
||||
DirAccess::remove_file_or_error(basepath + ".zip");
|
||||
return err;
|
||||
}
|
||||
|
||||
String cmd_args;
|
||||
{
|
||||
Vector<String> cmd_args_list;
|
||||
gen_debug_flags(cmd_args_list, p_debug_flags);
|
||||
for (int i = 0; i < cmd_args_list.size(); i++) {
|
||||
if (i != 0) {
|
||||
cmd_args += " ";
|
||||
}
|
||||
cmd_args += cmd_args_list[i];
|
||||
}
|
||||
}
|
||||
|
||||
const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
|
||||
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
|
||||
|
||||
print_line("Creating temporary directory...");
|
||||
ep.step(TTR("Creating temporary directory..."), 2);
|
||||
String temp_dir;
|
||||
err = ssh_run_on_remote(host, port, extra_args_ssh, "mktemp -d", &temp_dir);
|
||||
if (err != OK || temp_dir.is_empty()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
print_line("Uploading archive...");
|
||||
ep.step(TTR("Uploading archive..."), 3);
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
{
|
||||
String run_script = p_preset->get("ssh_remote_deploy/run_script");
|
||||
run_script = run_script.replace("{temp_dir}", temp_dir);
|
||||
run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
|
||||
run_script = run_script.replace("{exe_name}", pkg_name);
|
||||
run_script = run_script.replace("{cmd_args}", cmd_args);
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(basepath + "_start.sh", FileAccess::WRITE);
|
||||
if (f.is_null()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
f->store_string(run_script);
|
||||
}
|
||||
|
||||
{
|
||||
String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
|
||||
clean_script = clean_script.replace("{temp_dir}", temp_dir);
|
||||
clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
|
||||
clean_script = clean_script.replace("{exe_name}", pkg_name);
|
||||
clean_script = clean_script.replace("{cmd_args}", cmd_args);
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(basepath + "_clean.sh", FileAccess::WRITE);
|
||||
if (f.is_null()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
f->store_string(clean_script);
|
||||
}
|
||||
|
||||
print_line("Uploading scripts...");
|
||||
ep.step(TTR("Uploading scripts..."), 4);
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.sh", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"));
|
||||
if (err != OK || temp_dir.is_empty()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.sh", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh"));
|
||||
if (err != OK || temp_dir.is_empty()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
print_line("Starting project...");
|
||||
ep.step(TTR("Starting project..."), 5);
|
||||
err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"), &ssh_pid, (use_remote) ? dbg_port : -1);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
cleanup_commands.clear();
|
||||
cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh")));
|
||||
|
||||
print_line("Project started.");
|
||||
|
||||
CLEANUP_AND_RETURN(OK);
|
||||
#undef CLEANUP_AND_RETURN
|
||||
}
|
||||
|
||||
EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false);
|
||||
logo = ImageTexture::create_from_image(img);
|
||||
|
||||
img_loader.create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false);
|
||||
run_icon = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
|
||||
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
||||
if (theme.is_valid()) {
|
||||
stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
|
||||
} else {
|
||||
stop_icon.instantiate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,12 +36,10 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
#include "platform/macos/logo.gen.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
@ -52,6 +50,30 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
|
|||
|
||||
Ref<ImageTexture> logo;
|
||||
|
||||
struct SSHCleanupCommand {
|
||||
String host;
|
||||
String port;
|
||||
Vector<String> ssh_args;
|
||||
String cmd_args;
|
||||
bool wait = false;
|
||||
|
||||
SSHCleanupCommand(){};
|
||||
SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
|
||||
host = p_host;
|
||||
port = p_port;
|
||||
ssh_args = p_ssh_arg;
|
||||
cmd_args = p_cmd_args;
|
||||
wait = p_wait;
|
||||
};
|
||||
};
|
||||
|
||||
Ref<ImageTexture> run_icon;
|
||||
Ref<ImageTexture> stop_icon;
|
||||
|
||||
Vector<SSHCleanupCommand> cleanup_commands;
|
||||
OS::ProcessID ssh_pid = 0;
|
||||
int menu_options = 0;
|
||||
|
||||
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
|
||||
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
|
||||
|
||||
|
@ -65,14 +87,17 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
|
|||
Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
|
||||
const String &p_ent_path);
|
||||
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
|
||||
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
|
||||
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
|
||||
|
||||
bool use_codesign() const { return true; }
|
||||
#ifdef MACOS_ENABLED
|
||||
bool use_dmg() const { return true; }
|
||||
bool use_dmg() const {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool use_dmg() const { return false; }
|
||||
bool use_dmg() const {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
|
||||
|
@ -97,7 +122,7 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
|
|||
|
||||
return true;
|
||||
}
|
||||
bool is_shbang(const String &p_path) const;
|
||||
bool is_shebang(const String &p_path) const;
|
||||
|
||||
protected:
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
|
||||
|
@ -105,9 +130,15 @@ protected:
|
|||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return "macOS"; }
|
||||
virtual String get_os_name() const override { return "macOS"; }
|
||||
virtual Ref<Texture2D> get_logo() const override { return logo; }
|
||||
virtual String get_name() const override {
|
||||
return "macOS";
|
||||
}
|
||||
virtual String get_os_name() const override {
|
||||
return "macOS";
|
||||
}
|
||||
virtual Ref<Texture2D> get_logo() const override {
|
||||
return logo;
|
||||
}
|
||||
|
||||
virtual bool is_executable(const String &p_path) const override;
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
|
||||
|
@ -133,8 +164,16 @@ public:
|
|||
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override {
|
||||
}
|
||||
|
||||
virtual Ref<Texture2D> get_run_icon() const override;
|
||||
virtual bool poll_export() override;
|
||||
virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
|
||||
virtual int get_options_count() const override;
|
||||
virtual String get_option_label(int p_index) const override;
|
||||
virtual String get_option_tooltip(int p_index) const override;
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
|
||||
virtual void cleanup() override;
|
||||
|
||||
EditorExportPlatformMacOS();
|
||||
~EditorExportPlatformMacOS();
|
||||
};
|
||||
|
||||
#endif // MACOS_EXPORT_PLUGIN_H
|
||||
|
|
Before Width: | Height: | Size: 7 KiB |
1
platform/macos/logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="32" viewBox="0 0 25.6 25.6" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M.948 19.474V6.126c0-2.767 2.42-5.178 5.178-5.178h6.904s-2.992 7.506-2.992 13.233c0 .346.337.69.69.69h2.877c0 6.648.906 8.436 1.266 9.781H6.126c-2.75 0-5.178-2.407-5.178-5.178z" fill="#1c9ef8"/><path d="M7.162 8.312v1.496" fill="none" stroke="#323232" stroke-linecap="round" stroke-width="1.036"/><path d="M10.729 14.871c-.352 0-.69-.344-.69-.69C10.038 8.414 13.03.948 13.03.948h6.444c2.77 0 5.178 2.416 5.178 5.178v13.348c0 2.754-2.407 5.178-5.178 5.178h-4.603c-.357-1.333-1.266-2.887-1.266-9.78z" fill="#e1e6ed"/><g fill="none" stroke="#323232" stroke-linecap="round"><path d="M17.575 8.255V9.75" stroke-width="1.036"/><path d="M5.55 16.943c1.037 1.794 3.907 2.876 6.79 2.876 2.877 0 5.745-1.068 6.789-2.876" stroke-width=".69"/></g></svg>
|
After Width: | Height: | Size: 838 B |
1
platform/macos/run_icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-width:.93168;-inkscape-stroke:none" d="M4.418 1.055c-1.82 0-3.365 1.537-3.365 3.363v7.164c0 1.829 1.548 3.363 3.365 3.363h7.164c1.828 0 3.363-1.544 3.363-3.363V4.418c0-1.822-1.537-3.363-3.363-3.363H7.729Zm3.875 1.164h3.291c1.149 0 2.2 1.053 2.2 2.199v7.164c0 1.14-1.052 2.2-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a3.947 3.947 0 0 1-.049.004l-.008-.002a7.345 7.345 0 0 1-1.205-.004 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.558.558 0 0 1-.059-.424.564.564 0 0 1 .881-.299.56.56 0 0 1 .145.164c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM4.973 5.03h.002a.562.562 0 0 1 .558.559v.805a.556.556 0 0 1-.558.558.56.56 0 0 1-.397-.162.565.565 0 0 1-.164-.396V5.59a.561.561 0 0 1 .559-.559Z"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-linecap:round;-inkscape-stroke:none" d="M26.117 11.467c.008.11.014.225.022.328.022.283.052.565.088.846l.012.053c1.238-.252 2.448-.829 3.011-1.803a.6.6 0 1 0-1.039-.6c-.28.486-1.161.936-2.094 1.176z" transform="translate(-15.37 .357) scale(.93168)"/><g style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-opacity:1"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 5.585v.862" transform="translate(-15.37 .357) scale(.93168)"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 4.984a.6.6 0 0 0-.6.6v.863a.6.6 0 0 0 .6.6.6.6 0 0 0 .6-.6v-.863a.6.6 0 0 0-.6-.6Z" transform="translate(-15.37 .357) scale(.93168)"/></g></svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -30,8 +30,14 @@
|
|||
|
||||
#include "export_plugin.h"
|
||||
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "platform/uwp/logo.gen.h"
|
||||
#include "platform/uwp/logo_svg.gen.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg and regex.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
String EditorExportPlatformUWP::get_name() const {
|
||||
return "UWP";
|
||||
|
@ -504,5 +510,13 @@ void EditorExportPlatformUWP::resolve_platform_feature_priorities(const Ref<Edit
|
|||
}
|
||||
|
||||
EditorExportPlatformUWP::EditorExportPlatformUWP() {
|
||||
logo = ImageTexture::create_from_image(memnew(Image(_uwp_logo)));
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false);
|
||||
|
||||
logo = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 1.5 KiB |
1
platform/uwp/logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="32" width="32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(8.981 1.816)"><circle style="fill:#2f75bb;fill-opacity:1;stroke:none;stroke-width:.815427" cx="7.019" cy="14.184" r="13.825"/><path d="m-1.192 8.234 6.73-.927v6.503h-6.73Zm0 11.899 6.73.927v-6.422h-6.73Zm7.47 1.026 8.952 1.236v-7.757H6.278Zm0-13.951v6.602h8.952V5.973Z" fill="#00abed" style="fill:#fff;fill-opacity:1"/></g></svg>
|
After Width: | Height: | Size: 438 B |
|
@ -31,7 +31,15 @@
|
|||
#include "export_plugin.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "platform/web/logo_svg.gen.h"
|
||||
#include "platform/web/run_icon_svg.gen.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
Error EditorExportPlatformWeb::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) {
|
||||
Ref<FileAccess> io_fa;
|
||||
|
@ -651,8 +659,17 @@ EditorExportPlatformWeb::EditorExportPlatformWeb() {
|
|||
server.instantiate();
|
||||
server_thread.start(_server_thread_poll, this);
|
||||
|
||||
logo = ImageTexture::create_from_image(memnew(Image(_web_logo)));
|
||||
run_icon = ImageTexture::create_from_image(memnew(Image(_web_run_icon)));
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false);
|
||||
logo = ImageTexture::create_from_image(img);
|
||||
|
||||
img_loader.create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false);
|
||||
run_icon = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
|
||||
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
||||
if (theme.is_valid()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB |
1
platform/web/logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M7 5h18v21H7z" fill="#fff"/><path d="M3.143 1 5.48 27.504 15.967 31l10.553-3.496L28.857 1zM23.78 9.565H11.473l.275 3.308h11.759l-.911 9.937-6.556 1.808v.02h-.073l-6.61-1.828-.402-5.076h3.195l.234 2.552 3.583.97 3.595-.97.402-4.165H8.788L7.93 6.37h16.145z" fill="#eb6428"/></svg>
|
After Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 290 B |
1
platform/web/run_icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M3.143 1 5.48 27.504 15.967 31l10.553-3.496L28.857 1ZM23.78 9.565H11.473l.275 3.308h11.759l-.911 9.937-6.556 1.808v.02h-.073l-6.61-1.828-.402-5.076h3.195l.234 2.552 3.583.97 3.595-.97.402-4.165H8.788L7.93 6.37h16.145Z" fill="#eb6428" style="fill:#e0e0e0;fill-opacity:1" transform="translate(.586 .586) scale(.46337)"/></svg>
|
After Width: | Height: | Size: 418 B |
|
@ -51,7 +51,6 @@ void register_windows_exporter() {
|
|||
|
||||
Ref<EditorExportPlatformWindows> platform;
|
||||
platform.instantiate();
|
||||
platform->set_logo(ImageTexture::create_from_image(memnew(Image(_windows_logo))));
|
||||
platform->set_name("Windows Desktop");
|
||||
platform->set_os_name("Windows");
|
||||
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
#include "core/io/image_loader.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "platform/windows/logo_svg.gen.h"
|
||||
#include "platform/windows/run_icon_svg.gen.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
Error EditorExportPlatformWindows::_process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path) {
|
||||
static const uint8_t icon_size[] = { 16, 32, 48, 64, 128, 0 /*256*/ };
|
||||
|
@ -168,9 +176,39 @@ Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset>
|
|||
}
|
||||
|
||||
Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
|
||||
String pck_path = p_path;
|
||||
if (p_preset->get("binary_format/embed_pck")) {
|
||||
pck_path = p_path.get_basename() + ".tmp";
|
||||
bool export_as_zip = p_path.ends_with("zip");
|
||||
bool embedded = p_preset->get("binary_format/embed_pck");
|
||||
|
||||
String pkg_name;
|
||||
if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
|
||||
pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
|
||||
} else {
|
||||
pkg_name = "Unnamed";
|
||||
}
|
||||
|
||||
pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
|
||||
|
||||
// Setup temp folder.
|
||||
String path = p_path;
|
||||
String tmp_dir_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name);
|
||||
Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
|
||||
if (export_as_zip) {
|
||||
if (tmp_app_dir.is_null()) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
if (DirAccess::exists(tmp_dir_path)) {
|
||||
if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
|
||||
tmp_app_dir->erase_contents_recursive();
|
||||
}
|
||||
}
|
||||
tmp_app_dir->make_dir_recursive(tmp_dir_path);
|
||||
path = tmp_dir_path.path_join(p_path.get_file().get_basename() + ".exe");
|
||||
}
|
||||
|
||||
// Export project.
|
||||
String pck_path = path;
|
||||
if (embedded) {
|
||||
pck_path = pck_path.get_basename() + ".tmp";
|
||||
}
|
||||
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
|
||||
if (p_preset->get("codesign/enable") && err == OK) {
|
||||
|
@ -181,7 +219,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
|
|||
}
|
||||
}
|
||||
|
||||
if (p_preset->get("binary_format/embed_pck") && err == OK) {
|
||||
if (embedded && err == OK) {
|
||||
Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
|
||||
err = tmp_dir->rename(pck_path, p_path);
|
||||
if (err != OK) {
|
||||
|
@ -189,6 +227,27 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
|
|||
}
|
||||
}
|
||||
|
||||
// ZIP project.
|
||||
if (export_as_zip) {
|
||||
if (FileAccess::exists(p_path)) {
|
||||
OS::get_singleton()->move_to_trash(p_path);
|
||||
}
|
||||
|
||||
Ref<FileAccess> io_fa_dst;
|
||||
zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
|
||||
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
|
||||
|
||||
zip_folder_recursive(zip, tmp_dir_path, "", pkg_name);
|
||||
|
||||
zipClose(zip, nullptr);
|
||||
|
||||
if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
|
||||
tmp_app_dir->erase_contents_recursive();
|
||||
tmp_app_dir->change_dir("..");
|
||||
tmp_app_dir->remove(pkg_name);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -199,6 +258,7 @@ String EditorExportPlatformWindows::get_template_file_name(const String &p_targe
|
|||
List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
|
||||
List<String> list;
|
||||
list.push_back("exe");
|
||||
list.push_back("zip");
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -212,6 +272,7 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExpor
|
|||
|
||||
void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
|
||||
EditorExportPlatformPC::get_export_options(r_options);
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64"), "x86_64"));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
|
||||
|
@ -235,6 +296,29 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
|
|||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
|
||||
|
||||
String run_script = "Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'\n"
|
||||
"$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'\n"
|
||||
"$trigger = New-ScheduledTaskTrigger -Once -At 00:00\n"
|
||||
"$settings = New-ScheduledTaskSettingsSet\n"
|
||||
"$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings\n"
|
||||
"Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true\n"
|
||||
"Start-ScheduledTask -TaskName godot_remote_debug\n"
|
||||
"while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }\n"
|
||||
"Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue";
|
||||
|
||||
String cleanup_script = "Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue\n"
|
||||
"Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue\n"
|
||||
"Remove-Item -Recurse -Force '{temp_dir}'";
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon) {
|
||||
|
@ -659,3 +743,227 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6
|
|||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorExportPlatformWindows::get_run_icon() const {
|
||||
return run_icon;
|
||||
}
|
||||
|
||||
bool EditorExportPlatformWindows::poll_export() {
|
||||
Ref<EditorExportPreset> preset;
|
||||
|
||||
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
|
||||
Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
|
||||
if (ep->is_runnable() && ep->get_platform() == this) {
|
||||
preset = ep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int prev = menu_options;
|
||||
menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
|
||||
if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
|
||||
if (menu_options == 0) {
|
||||
cleanup();
|
||||
} else {
|
||||
menu_options += 1;
|
||||
}
|
||||
}
|
||||
return menu_options != prev;
|
||||
}
|
||||
|
||||
Ref<ImageTexture> EditorExportPlatformWindows::get_option_icon(int p_index) const {
|
||||
return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
|
||||
}
|
||||
|
||||
int EditorExportPlatformWindows::get_options_count() const {
|
||||
return menu_options;
|
||||
}
|
||||
|
||||
String EditorExportPlatformWindows::get_option_label(int p_index) const {
|
||||
return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote Windows system");
|
||||
}
|
||||
|
||||
String EditorExportPlatformWindows::get_option_tooltip(int p_index) const {
|
||||
return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote Windows system");
|
||||
}
|
||||
|
||||
void EditorExportPlatformWindows::cleanup() {
|
||||
if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
|
||||
print_line("Terminating connection...");
|
||||
OS::get_singleton()->kill(ssh_pid);
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
}
|
||||
|
||||
if (!cleanup_commands.is_empty()) {
|
||||
print_line("Stopping and deleting previous version...");
|
||||
for (const SSHCleanupCommand &cmd : cleanup_commands) {
|
||||
if (cmd.wait) {
|
||||
ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
|
||||
} else {
|
||||
ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
ssh_pid = 0;
|
||||
cleanup_commands.clear();
|
||||
}
|
||||
|
||||
Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
|
||||
cleanup();
|
||||
if (p_device) { // Stop command, cleanup only.
|
||||
return OK;
|
||||
}
|
||||
|
||||
EditorProgress ep("run", TTR("Running..."), 5);
|
||||
|
||||
const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("windows");
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
if (!da->dir_exists(dest)) {
|
||||
Error err = da->make_dir_recursive(dest);
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
String host = p_preset->get("ssh_remote_deploy/host").operator String();
|
||||
String port = p_preset->get("ssh_remote_deploy/port").operator String();
|
||||
if (port.is_empty()) {
|
||||
port = "22";
|
||||
}
|
||||
Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
|
||||
Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
|
||||
|
||||
const String basepath = dest.path_join("tmp_windows_export");
|
||||
|
||||
#define CLEANUP_AND_RETURN(m_err) \
|
||||
{ \
|
||||
if (da->file_exists(basepath + ".zip")) { \
|
||||
da->remove(basepath + ".zip"); \
|
||||
} \
|
||||
if (da->file_exists(basepath + "_start.ps1")) { \
|
||||
da->remove(basepath + "_start.ps1"); \
|
||||
} \
|
||||
if (da->file_exists(basepath + "_clean.ps1")) { \
|
||||
da->remove(basepath + "_clean.ps1"); \
|
||||
} \
|
||||
return m_err; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
if (ep.step(TTR("Exporting project..."), 1)) {
|
||||
return ERR_SKIP;
|
||||
}
|
||||
Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
|
||||
if (err != OK) {
|
||||
DirAccess::remove_file_or_error(basepath + ".zip");
|
||||
return err;
|
||||
}
|
||||
|
||||
String cmd_args;
|
||||
{
|
||||
Vector<String> cmd_args_list;
|
||||
gen_debug_flags(cmd_args_list, p_debug_flags);
|
||||
for (int i = 0; i < cmd_args_list.size(); i++) {
|
||||
if (i != 0) {
|
||||
cmd_args += " ";
|
||||
}
|
||||
cmd_args += cmd_args_list[i];
|
||||
}
|
||||
}
|
||||
|
||||
const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
|
||||
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
|
||||
|
||||
print_line("Creating temporary directory...");
|
||||
ep.step(TTR("Creating temporary directory..."), 2);
|
||||
String temp_dir;
|
||||
err = ssh_run_on_remote(host, port, extra_args_ssh, "powershell -command \\\"\\$tmp = Join-Path \\$Env:Temp \\$(New-Guid); New-Item -Type Directory -Path \\$tmp | Out-Null; Write-Output \\$tmp\\\"", &temp_dir);
|
||||
if (err != OK || temp_dir.is_empty()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
print_line("Uploading archive...");
|
||||
ep.step(TTR("Uploading archive..."), 3);
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
{
|
||||
String run_script = p_preset->get("ssh_remote_deploy/run_script");
|
||||
run_script = run_script.replace("{temp_dir}", temp_dir);
|
||||
run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
|
||||
run_script = run_script.replace("{exe_name}", basepath.get_file() + ".exe");
|
||||
run_script = run_script.replace("{cmd_args}", cmd_args);
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(basepath + "_start.ps1", FileAccess::WRITE);
|
||||
if (f.is_null()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
f->store_string(run_script);
|
||||
}
|
||||
|
||||
{
|
||||
String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
|
||||
clean_script = clean_script.replace("{temp_dir}", temp_dir);
|
||||
clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
|
||||
clean_script = clean_script.replace("{exe_name}", basepath.get_file() + ".exe");
|
||||
clean_script = clean_script.replace("{cmd_args}", cmd_args);
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(basepath + "_clean.ps1", FileAccess::WRITE);
|
||||
if (f.is_null()) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
f->store_string(clean_script);
|
||||
}
|
||||
|
||||
print_line("Uploading scripts...");
|
||||
ep.step(TTR("Uploading scripts..."), 4);
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.ps1", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.ps1", temp_dir);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
print_line("Starting project...");
|
||||
ep.step(TTR("Starting project..."), 5);
|
||||
err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_start.ps1"), &ssh_pid, (use_remote) ? dbg_port : -1);
|
||||
if (err != OK) {
|
||||
CLEANUP_AND_RETURN(err);
|
||||
}
|
||||
|
||||
cleanup_commands.clear();
|
||||
cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_clean.ps1")));
|
||||
|
||||
print_line("Project started.");
|
||||
|
||||
CLEANUP_AND_RETURN(OK);
|
||||
#undef CLEANUP_AND_RETURN
|
||||
}
|
||||
|
||||
EditorExportPlatformWindows::EditorExportPlatformWindows() {
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
ImageLoaderSVG img_loader;
|
||||
img_loader.create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false);
|
||||
set_logo(ImageTexture::create_from_image(img));
|
||||
|
||||
img_loader.create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false);
|
||||
run_icon = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
|
||||
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
||||
if (theme.is_valid()) {
|
||||
stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
|
||||
} else {
|
||||
stop_icon.instantiate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,32 @@
|
|||
#include "core/os/os.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/export/editor_export_platform_pc.h"
|
||||
#include "platform/windows/logo.gen.h"
|
||||
|
||||
class EditorExportPlatformWindows : public EditorExportPlatformPC {
|
||||
struct SSHCleanupCommand {
|
||||
String host;
|
||||
String port;
|
||||
Vector<String> ssh_args;
|
||||
String cmd_args;
|
||||
bool wait = false;
|
||||
|
||||
SSHCleanupCommand(){};
|
||||
SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
|
||||
host = p_host;
|
||||
port = p_port;
|
||||
ssh_args = p_ssh_arg;
|
||||
cmd_args = p_cmd_args;
|
||||
wait = p_wait;
|
||||
};
|
||||
};
|
||||
|
||||
Ref<ImageTexture> run_icon;
|
||||
Ref<ImageTexture> stop_icon;
|
||||
|
||||
Vector<SSHCleanupCommand> cleanup_commands;
|
||||
OS::ProcessID ssh_pid = 0;
|
||||
int menu_options = 0;
|
||||
|
||||
Error _process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path);
|
||||
Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon);
|
||||
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
|
@ -53,6 +76,17 @@ public:
|
|||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
|
||||
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
|
||||
|
||||
virtual Ref<Texture2D> get_run_icon() const override;
|
||||
virtual bool poll_export() override;
|
||||
virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
|
||||
virtual int get_options_count() const override;
|
||||
virtual String get_option_label(int p_index) const override;
|
||||
virtual String get_option_tooltip(int p_index) const override;
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
|
||||
virtual void cleanup() override;
|
||||
|
||||
EditorExportPlatformWindows();
|
||||
};
|
||||
|
||||
#endif // WINDOWS_EXPORT_PLUGIN_H
|
||||
|
|
Before Width: | Height: | Size: 1.5 KiB |
1
platform/windows/logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="m1 5.132 12.295-1.694v11.879H1zm0 21.736 12.295 1.695V16.83H1zm13.647 1.875L31 31V16.83H14.647zm0-25.486v12.06H31V1z" fill="#00abed"/></svg>
|
After Width: | Height: | Size: 213 B |
1
platform/windows/run_icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m1.095 2.997 5.66-.78v5.469h-5.66zm0 10.006 5.66.78v-5.4h-5.66zm6.282.863 7.528 1.04V8.381H7.377Zm0-11.732v5.552h7.528V1.095Z" fill="#00abed" style="stroke-width:.460341;fill:#e0e0e0;fill-opacity:1"/></svg>
|
After Width: | Height: | Size: 300 B |