diff options
Diffstat (limited to 'platform')
| -rw-r--r-- | platform/iphone/export/export.cpp | 347 | ||||
| -rw-r--r-- | platform/iphone/export/export.h | 30 | ||||
| -rw-r--r-- | platform/osx/export/export.cpp | 4 | ||||
| -rw-r--r-- | platform/osx/os_osx.h | 2 | ||||
| -rw-r--r-- | platform/osx/os_osx.mm | 60 | ||||
| -rw-r--r-- | platform/x11/os_x11.cpp | 30 | ||||
| -rw-r--r-- | platform/x11/os_x11.h | 3 |
7 files changed, 463 insertions, 13 deletions
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp new file mode 100644 index 0000000000..0960ec8791 --- /dev/null +++ b/platform/iphone/export/export.cpp @@ -0,0 +1,347 @@ +/*************************************************************************/ +/* export.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "export.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "global_config.h" +#include "io/marshalls.h" +#include "io/resource_saver.h" +#include "io/zip_io.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/osx/logo.gen.h" +#include "string.h" +#include "version.h" + +#include <sys/stat.h> + +class EditorExportPlatformIOS : public EditorExportPlatform { + + GDCLASS(EditorExportPlatformIOS, EditorExportPlatform); + + int version_code; + + Ref<ImageTexture> logo; + + void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary); + +protected: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); + virtual void get_export_options(List<ExportOption> *r_options); + +public: + virtual String get_name() const { return "iOS"; } + virtual Ref<Texture> get_logo() const { return logo; } + + virtual String get_binary_extension() const { return "xcodeproj"; } + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; + + EditorExportPlatformIOS(); + ~EditorExportPlatformIOS(); +}; + +void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + + // what does this need to do? +} + +void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) { + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); + // r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier"), "org.godotengine.iosgame")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "godotiosgame")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits"), 1)); + + /* probably need some more info */ +} + +void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary) { + + String str; + String strnew; + str.parse_utf8((const char *)pfile.ptr(), pfile.size()); + Vector<String> lines = str.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (lines[i].find("$binary") != -1) { + strnew += lines[i].replace("$binary", p_binary) + "\n"; + } else if (lines[i].find("$name") != -1) { + strnew += lines[i].replace("$name", p_name) + "\n"; + } else if (lines[i].find("$info") != -1) { + strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; + } else if (lines[i].find("$identifier") != -1) { + strnew += lines[i].replace("$identifier", p_preset->get("application/identifier")) + "\n"; + } else if (lines[i].find("$short_version") != -1) { + strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; + } else if (lines[i].find("$version") != -1) { + strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + } else if (lines[i].find("$signature") != -1) { + strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; + } else if (lines[i].find("$copyright") != -1) { + strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; + } else { + strnew += lines[i] + "\n"; + } + } + + // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero... + // should apply the same fix in our OSX export. + CharString cs = strnew.utf8(); + pfile.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + pfile[i] = cs[i]; + } +} + +Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + String src_pkg_name; + String dest_dir = p_path.get_base_dir() + "/"; + String binary_name = p_path.get_file().get_basename(); + + EditorProgress ep("export", "Exporting for iOS", 3); + + if (p_debug) + src_pkg_name = p_preset->get("custom_package/debug"); + else + src_pkg_name = p_preset->get("custom_package/release"); + + if (src_pkg_name == "") { + String err; + src_pkg_name = find_export_template("iphone.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Creating app", 0); + + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(src_pkg_zip); + + String binary_to_use = "godot.iphone." + String(p_debug ? "debug" : "release") + "."; + int bits_mode = p_preset->get("application/bits_mode"); + binary_to_use += String(bits_mode == 0 ? "fat" : bits_mode == 1 ? "arm64" : "armv7"); + + print_line("binary: " + binary_to_use); + String pkg_name; + if (p_preset->get("application/name") != "") + pkg_name = p_preset->get("application/name"); // app_name + else if (String(GlobalConfig::get_singleton()->get("application/name")) != "") + pkg_name = String(GlobalConfig::get_singleton()->get("application/name")); + else + pkg_name = "Unnamed"; + + DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + + /* Now process our template */ + bool found_binary = false; + int total_size = 0; + + while (ret == UNZ_OK) { + bool is_execute = false; + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); + + String file = fname; + + print_line("READ: " + file); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + //write + + file = file.replace_first("iphone/", ""); + + if (file == "godot_ios.xcodeproj/project.pbxproj") { + print_line("parse pbxproj"); + _fix_config_file(p_preset, data, pkg_name, binary_name); + } else if (file == "godot_ios/godot_ios-Info.plist") { + print_line("parse plist"); + _fix_config_file(p_preset, data, pkg_name, binary_name); + } else if (file.begins_with("godot.iphone")) { + if (file != binary_to_use) { + ret = unzGoToNextFile(src_pkg_zip); + continue; //ignore! + } + found_binary = true; + is_execute = true; + file = "godot_ios.iphone"; + } + + ///@TODO need to parse logo files + + if (data.size() > 0) { + file = file.replace("godot_ios", binary_name); + + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + /* write it into our folder structure */ + file = dest_dir + file; + + /* make sure this folder exists */ + String dir_name = file.get_base_dir(); + if (!tmp_app_path->dir_exists(dir_name)) { + print_line("Creating " + dir_name); + Error dir_err = tmp_app_path->make_dir_recursive(dir_name); + if (dir_err) { + ERR_PRINTS("Can't create '" + dir_name + "'."); + unzClose(src_pkg_zip); + return ERR_CANT_CREATE; + } + } + + /* write the file */ + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + if (!f) { + ERR_PRINTS("Can't write '" + file + "'."); + unzClose(src_pkg_zip); + return ERR_CANT_CREATE; + }; + f->store_buffer(data.ptr(), data.size()); + f->close(); + memdelete(f); + +#ifdef OSX_ENABLED + if (is_execute) { + // we need execute rights on this file + chmod(file.utf8().get_data(), 0755); + } +#endif + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + /* we're done with our source zip */ + unzClose(src_pkg_zip); + + if (!found_binary) { + ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); + unzClose(src_pkg_zip); + return ERR_FILE_NOT_FOUND; + } + + ep.step("Making PKG", 1); + + String pack_path = dest_dir + binary_name + ".pck"; + Error err = save_pack(p_preset, pack_path); + + if (err) { + return err; + } + +#ifdef OSX_ENABLED + /* and open up xcode with our new project.... */ + List<String> args; + args.push_back(p_path); + err = OS::get_singleton()->execute("/usr/bin/open", args, false); + ERR_FAIL_COND_V(err, err); + +#endif + + return OK; +} + +bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + + bool valid = true; + String err; + + if (!exists_export_template("iphone.zip", &err)) { + valid = false; + } + + if (p_preset->get("custom_package/debug") != "" && !FileAccess::exists(p_preset->get("custom_package/debug"))) { + valid = false; + err += "Custom debug package not found.\n"; + } + + if (p_preset->get("custom_package/release") != "" && !FileAccess::exists(p_preset->get("custom_package/release"))) { + valid = false; + err += "Custom release package not found.\n"; + } + + if (!err.empty()) + r_error = err; + + return valid; +} + +EditorExportPlatformIOS::EditorExportPlatformIOS() { + + ///@TODO need to create the correct logo + // Ref<Image> img = memnew(Image(_iphone_logo)); + Ref<Image> img = memnew(Image(_osx_logo)); + logo.instance(); + logo->create_from_image(img); +} + +EditorExportPlatformIOS::~EditorExportPlatformIOS() { +} + +void register_iphone_exporter() { + + Ref<EditorExportPlatformIOS> platform; + platform.instance(); + + EditorExport::get_singleton()->add_export_platform(platform); +} diff --git a/platform/iphone/export/export.h b/platform/iphone/export/export.h new file mode 100644 index 0000000000..6e9324aed7 --- /dev/null +++ b/platform/iphone/export/export.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* export.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +void register_iphone_exporter(); diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 9d3493cb49..c81fb00b36 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -185,8 +185,8 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset } CharString cs = strnew.utf8(); - plist.resize(cs.size()); - for (int i = 9; i < cs.size(); i++) { + plist.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { plist[i] = cs[i]; } } diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index dc3e88df2c..a3036b1a8a 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -117,6 +117,8 @@ public: return 1.0; } + void _update_window(); + float display_scale; protected: diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index ad1e308ae0..87c25a5356 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -921,6 +921,8 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au [NSApp activateIgnoringOtherApps:YES]; + _update_window(); + [window_object makeKeyAndOrderFront:nil]; if (p_desired.fullscreen) @@ -980,7 +982,8 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au displayScale = [[screenArray objectAtIndex:i] backingScaleFactor]; } - NSRect nsrect = [[screenArray objectAtIndex:i] visibleFrame]; + // Note: Use frame to get the whole screen size + NSRect nsrect = [[screenArray objectAtIndex:i] frame]; Rect2 rect = Rect2(nsrect.origin.x, nsrect.origin.y, nsrect.size.width, nsrect.size.height); rect.position *= displayScale; rect.size *= displayScale; @@ -1299,6 +1302,31 @@ Size2 OS_OSX::get_screen_size(int p_screen) const { return screens[p_screen].size; }; +void OS_OSX::_update_window() { + bool borderless_full = false; + + if (get_borderless_window()) { + NSRect frameRect = [window_object frame]; + NSRect screenRect = [[window_object screen] frame]; + + // Check if our window covers up the screen + if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y && + frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) { + borderless_full = true; + } + } + + if (borderless_full) { + // If the window covers up the screen set the level to above the main menu and hide on deactivate + [window_object setLevel:NSMainMenuWindowLevel + 1]; + [window_object setHidesOnDeactivate:YES]; + } else { + // Reset these when our window is not a borderless window that covers up the screen + [window_object setLevel:NSNormalWindowLevel]; + [window_object setHidesOnDeactivate:NO]; + } +} + Point2 OS_OSX::get_window_position() const { Size2 wp([window_object frame].origin.x, [window_object frame].origin.y); @@ -1311,6 +1339,8 @@ void OS_OSX::set_window_position(const Point2 &p_position) { Point2 size = p_position; size /= display_scale; [window_object setFrame:NSMakeRect(size.x, size.y, [window_object frame].size.width, [window_object frame].size.height) display:YES]; + + _update_window(); }; Size2 OS_OSX::get_window_size() const { @@ -1322,17 +1352,22 @@ void OS_OSX::set_window_size(const Size2 p_size) { Size2 size = p_size; - // NSRect used by setFrame includes the title bar, so add it to our size.y - CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; - if (menuBarHeight != 0.f) { - size.y += menuBarHeight; + if (get_borderless_window() == false) { + // NSRect used by setFrame includes the title bar, so add it to our size.y + CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; + if (menuBarHeight != 0.f) { + size.y += menuBarHeight; #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101104 - } else { - size.y += [[NSStatusBar systemStatusBar] thickness]; + } else { + size.y += [[NSStatusBar systemStatusBar] thickness]; #endif + } } + NSRect frame = [window_object frame]; [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES]; + + _update_window(); }; void OS_OSX::set_window_fullscreen(bool p_enabled) { @@ -1416,9 +1451,12 @@ void OS_OSX::request_attention() { void OS_OSX::set_borderless_window(int p_borderless) { - if (p_borderless) + // OrderOut prevents a lose focus bug with the window + [window_object orderOut:nil]; + + if (p_borderless) { [window_object setStyleMask:NSWindowStyleMaskBorderless]; - else { + } else { [window_object setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask]; // Force update of the window styles @@ -1429,6 +1467,10 @@ void OS_OSX::set_borderless_window(int p_borderless) { // Restore the window title [window_object setTitle:[NSString stringWithUTF8String:title.utf8().get_data()]]; } + + _update_window(); + + [window_object makeKeyAndOrderFront:nil]; } bool OS_OSX::get_borderless_window() { diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 383abffe46..790182794e 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -278,6 +278,13 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au xev.xclient.data.l[2] = 0; XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureNotifyMask, &xev); + } else if (current_videomode.borderless_window) { + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = 0; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); } // disable resizable window @@ -1018,6 +1025,25 @@ bool OS_X11::is_window_maximized() const { return false; } +void OS_X11::set_borderless_window(int p_borderless) { + + if (current_videomode.borderless_window == p_borderless) + return; + + current_videomode.borderless_window = p_borderless; + + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = current_videomode.borderless_window ? 0 : 1; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); +} + +bool OS_X11::get_borderless_window() { + return current_videomode.borderless_window; +} + void OS_X11::request_attention() { // Using EWMH -- Extended Window Manager Hints // @@ -1234,7 +1260,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { // Echo characters in X11 are a keyrelease and a keypress // one after the other with the (almot) same timestamp. // To detect them, i use XPeekEvent and check that their - // difference in time is below a treshold. + // difference in time is below a threshold. if (xkeyevent->type != KeyPress) { @@ -1246,7 +1272,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { XEvent peek_event; XPeekEvent(x11_display, &peek_event); - // I'm using a treshold of 5 msecs, + // I'm using a threshold of 5 msecs, // since sometimes there seems to be a little // jitter. I'm still not convinced that all this approach // is correct, but the xorg developers are diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index b253934a05..39c512b6bd 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -251,6 +251,9 @@ public: virtual bool is_window_maximized() const; virtual void request_attention(); + virtual void set_borderless_window(int p_borderless); + virtual bool get_borderless_window(); + virtual void move_window_to_foreground(); virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); |