diff options
Diffstat (limited to 'platform')
-rw-r--r-- | platform/javascript/export/export_plugin.cpp | 9 | ||||
-rw-r--r-- | platform/javascript/package-lock.json | 12 | ||||
-rw-r--r-- | platform/linuxbsd/display_server_x11.cpp | 6 | ||||
-rw-r--r-- | platform/linuxbsd/display_server_x11.h | 2 | ||||
-rw-r--r-- | platform/linuxbsd/export/export.cpp | 114 | ||||
-rw-r--r-- | platform/linuxbsd/export/export_plugin.cpp | 130 | ||||
-rw-r--r-- | platform/linuxbsd/export/export_plugin.h | 5 | ||||
-rw-r--r-- | platform/osx/display_server_osx.h | 41 | ||||
-rw-r--r-- | platform/osx/display_server_osx.mm | 545 | ||||
-rw-r--r-- | platform/osx/godot_menu_item.h | 11 | ||||
-rw-r--r-- | platform/uwp/export/export_plugin.cpp | 8 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 10 | ||||
-rw-r--r-- | platform/windows/display_server_windows.h | 2 | ||||
-rw-r--r-- | platform/windows/export/export.cpp | 79 | ||||
-rw-r--r-- | platform/windows/export/export_plugin.cpp | 81 | ||||
-rw-r--r-- | platform/windows/export/export_plugin.h | 3 |
16 files changed, 817 insertions, 241 deletions
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp index 4448acccc2..84694461cf 100644 --- a/platform/javascript/export/export_plugin.cpp +++ b/platform/javascript/export/export_plugin.cpp @@ -360,6 +360,15 @@ Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const { } bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { +#ifndef DEV_ENABLED + // We don't provide export templates for the HTML5 platform currently as there + // is no suitable renderer to use with them. So we forbid exporting and tell + // users why. This is skipped in DEV_ENABLED so that contributors can still test + // the pipeline once we start having WebGL or WebGPU support. + r_error = "The HTML5 platform is currently not supported in Godot 4.0, as there is no suitable renderer for it.\n"; + return false; +#endif + String err; bool valid = false; ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json index 35f864f01a..f72cde955a 100644 --- a/platform/javascript/package-lock.json +++ b/platform/javascript/package-lock.json @@ -1884,9 +1884,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/mkdirp": { @@ -4444,9 +4444,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mkdirp": { diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 07cb6a23e8..2ddccdb8bf 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -363,15 +363,15 @@ DisplayServerX11::MouseMode DisplayServerX11::mouse_get_mode() const { return mouse_mode; } -void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) { +void DisplayServerX11::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ if (mouse_mode == MOUSE_MODE_CAPTURED) { - last_mouse_pos = p_to; + last_mouse_pos = p_position; } else { WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; XWarpPointer(x11_display, None, windows[window_id].x11_window, - 0, 0, 0, 0, (int)p_to.x, (int)p_to.y); + 0, 0, 0, 0, (int)p_position.x, (int)p_position.y); } } diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 63d32d939d..0f44aa4b11 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -301,7 +301,7 @@ public: virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; - virtual void mouse_warp_to_position(const Point2i &p_to) override; + virtual void warp_mouse(const Point2i &p_position) override; virtual Point2i mouse_get_position() const override; virtual MouseButton mouse_get_button_state() const override; diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp index 796e82594a..ec83e52f09 100644 --- a/platform/linuxbsd/export/export.cpp +++ b/platform/linuxbsd/export/export.cpp @@ -32,8 +32,6 @@ #include "export_plugin.h" -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); - void register_linuxbsd_exporter() { Ref<EditorExportPlatformLinuxBSD> platform; platform.instantiate(); @@ -44,120 +42,10 @@ void register_linuxbsd_exporter() { logo->create_from_image(img); platform->set_logo(logo); platform->set_name("Linux/X11"); - platform->set_extension("x86"); + platform->set_extension("x86_32"); platform->set_extension("x86_64", "binary_format/64_bits"); - platform->set_release_32("linux_x11_32_release"); - platform->set_debug_32("linux_x11_32_debug"); - platform->set_release_64("linux_x11_64_release"); - platform->set_debug_64("linux_x11_64_debug"); platform->set_os_name("LinuxBSD"); platform->set_chmod_flags(0755); - platform->set_fixup_embedded_pck_func(&fixup_embedded_pck); EditorExport::get_singleton()->add_export_platform(platform); } - -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { - // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data - - FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE); - if (!f) { - return ERR_CANT_OPEN; - } - - // Read and check ELF magic number - { - uint32_t magic = f->get_32(); - if (magic != 0x464c457f) { // 0x7F + "ELF" - f->close(); - return ERR_FILE_CORRUPT; - } - } - - // Read program architecture bits from class field - - int bits = f->get_8() * 32; - - if (bits == 32 && p_embedded_size >= 0x100000000) { - f->close(); - ERR_FAIL_V_MSG(ERR_INVALID_DATA, "32-bit executables cannot have embedded data >= 4 GiB."); - } - - // Get info about the section header table - - int64_t section_table_pos; - int64_t section_header_size; - if (bits == 32) { - section_header_size = 40; - f->seek(0x20); - section_table_pos = f->get_32(); - f->seek(0x30); - } else { // 64 - section_header_size = 64; - f->seek(0x28); - section_table_pos = f->get_64(); - f->seek(0x3c); - } - int num_sections = f->get_16(); - int string_section_idx = f->get_16(); - - // Load the strings table - uint8_t *strings; - { - // Jump to the strings section header - f->seek(section_table_pos + string_section_idx * section_header_size); - - // Read strings data size and offset - int64_t string_data_pos; - int64_t string_data_size; - if (bits == 32) { - f->seek(f->get_position() + 0x10); - string_data_pos = f->get_32(); - string_data_size = f->get_32(); - } else { // 64 - f->seek(f->get_position() + 0x18); - string_data_pos = f->get_64(); - string_data_size = f->get_64(); - } - - // Read strings data - f->seek(string_data_pos); - strings = (uint8_t *)memalloc(string_data_size); - if (!strings) { - f->close(); - return ERR_OUT_OF_MEMORY; - } - f->get_buffer(strings, string_data_size); - } - - // Search for the "pck" section - - bool found = false; - for (int i = 0; i < num_sections; ++i) { - int64_t section_header_pos = section_table_pos + i * section_header_size; - f->seek(section_header_pos); - - uint32_t name_offset = f->get_32(); - if (strcmp((char *)strings + name_offset, "pck") == 0) { - // "pck" section found, let's patch! - - if (bits == 32) { - f->seek(section_header_pos + 0x10); - f->store_32(p_embedded_start); - f->store_32(p_embedded_size); - } else { // 64 - f->seek(section_header_pos + 0x18); - f->store_64(p_embedded_start); - f->store_64(p_embedded_size); - } - - found = true; - break; - } - } - - memfree(strings); - f->close(); - - return found ? OK : ERR_FILE_CORRUPT; -} diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 99f5b82b34..24906fa3fb 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -72,3 +72,133 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> return err; } + +void EditorExportPlatformLinuxBSD::set_extension(const String &p_extension, const String &p_feature_key) { + extensions[p_feature_key] = p_extension; +} + +String EditorExportPlatformLinuxBSD::get_template_file_name(const String &p_target, const String &p_arch) const { + return "linux_x11_" + p_arch + "_" + p_target; +} + +List<String> EditorExportPlatformLinuxBSD::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + for (const KeyValue<String, String> &E : extensions) { + if (p_preset->get(E.key)) { + list.push_back(extensions[E.key]); + return list; + } + } + + if (extensions.has("default")) { + list.push_back(extensions["default"]); + return list; + } + + return list; +} + +Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const { + // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE); + if (!f) { + return ERR_CANT_OPEN; + } + + // Read and check ELF magic number + { + uint32_t magic = f->get_32(); + if (magic != 0x464c457f) { // 0x7F + "ELF" + f->close(); + return ERR_FILE_CORRUPT; + } + } + + // Read program architecture bits from class field + + int bits = f->get_8() * 32; + + if (bits == 32 && p_embedded_size >= 0x100000000) { + f->close(); + ERR_FAIL_V_MSG(ERR_INVALID_DATA, "32-bit executables cannot have embedded data >= 4 GiB."); + } + + // Get info about the section header table + + int64_t section_table_pos; + int64_t section_header_size; + if (bits == 32) { + section_header_size = 40; + f->seek(0x20); + section_table_pos = f->get_32(); + f->seek(0x30); + } else { // 64 + section_header_size = 64; + f->seek(0x28); + section_table_pos = f->get_64(); + f->seek(0x3c); + } + int num_sections = f->get_16(); + int string_section_idx = f->get_16(); + + // Load the strings table + uint8_t *strings; + { + // Jump to the strings section header + f->seek(section_table_pos + string_section_idx * section_header_size); + + // Read strings data size and offset + int64_t string_data_pos; + int64_t string_data_size; + if (bits == 32) { + f->seek(f->get_position() + 0x10); + string_data_pos = f->get_32(); + string_data_size = f->get_32(); + } else { // 64 + f->seek(f->get_position() + 0x18); + string_data_pos = f->get_64(); + string_data_size = f->get_64(); + } + + // Read strings data + f->seek(string_data_pos); + strings = (uint8_t *)memalloc(string_data_size); + if (!strings) { + f->close(); + return ERR_OUT_OF_MEMORY; + } + f->get_buffer(strings, string_data_size); + } + + // Search for the "pck" section + + bool found = false; + for (int i = 0; i < num_sections; ++i) { + int64_t section_header_pos = section_table_pos + i * section_header_size; + f->seek(section_header_pos); + + uint32_t name_offset = f->get_32(); + if (strcmp((char *)strings + name_offset, "pck") == 0) { + // "pck" section found, let's patch! + + if (bits == 32) { + f->seek(section_header_pos + 0x10); + f->store_32(p_embedded_start); + f->store_32(p_embedded_size); + } else { // 64 + f->seek(section_header_pos + 0x18); + f->store_64(p_embedded_start); + f->store_64(p_embedded_size); + } + + found = true; + break; + } + } + + memfree(strings); + f->close(); + + return found ? OK : ERR_FILE_CORRUPT; +} diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h index f482ddce3d..f46fc68e1d 100644 --- a/platform/linuxbsd/export/export_plugin.h +++ b/platform/linuxbsd/export/export_plugin.h @@ -38,10 +38,15 @@ #include "scene/resources/texture.h" class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC { + Map<String, String> extensions; 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 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) const override; }; #endif diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h index cc9ac162ea..fa3091ff81 100644 --- a/platform/osx/display_server_osx.h +++ b/platform/osx/display_server_osx.h @@ -186,6 +186,7 @@ private: void _process_key_events(); void _update_keyboard_layouts(); static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info); + NSImage *_convert_to_nsimg(Ref<Image> &p_image) const; static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil); @@ -217,24 +218,46 @@ public: virtual bool has_feature(Feature p_feature) const override; virtual String get_name() const override; - virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant()) override; - virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant()) override; - virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) override; - virtual void global_menu_add_separator(const String &p_menu_root) override; + virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual void global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual void global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual void global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual void global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual void global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override; + virtual void global_menu_add_separator(const String &p_menu_root, int p_index = -1) override; + + virtual int global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const override; + virtual int global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const override; virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const override; virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const override; - virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) override; - virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) override; - virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) override; - virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) override; + virtual bool global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const override; + virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) const override; + virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) const override; + virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) const override; + virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const override; + virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const override; + virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const override; + virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const override; + virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const override; + virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const override; + virtual Ref<Texture2D> global_menu_get_item_icon(const String &p_menu_root, int p_idx) const override; virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) override; virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; + virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override; virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override; virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override; virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override; + virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) override; + virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) override; + virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) override; + virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) override; + virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) override; + virtual void global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon) override; virtual int global_menu_get_item_count(const String &p_menu_root) const override; @@ -248,7 +271,7 @@ public: virtual MouseMode mouse_get_mode() const override; bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp); - virtual void mouse_warp_to_position(const Point2i &p_to) override; + virtual void warp_mouse(const Point2i &p_position) override; virtual Point2i mouse_get_position() const override; void mouse_set_button_state(MouseButton p_state); virtual MouseButton mouse_get_button_state() const override; diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index 89ca6e50ec..6cdadcae39 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -91,6 +91,7 @@ NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) { // Submenu. if (!submenu.has(p_menu_root)) { NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; + [n_menu setAutoenablesItems:NO]; submenu[p_menu_root] = n_menu; } menu = submenu[p_menu_root]; @@ -472,6 +473,40 @@ void DisplayServerOSX::_keyboard_layout_changed(CFNotificationCenterRef center, } } +NSImage *DisplayServerOSX::_convert_to_nsimg(Ref<Image> &p_image) const { + p_image->convert(Image::FORMAT_RGBA8); + NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:p_image->get_width() + pixelsHigh:p_image->get_height() + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:int(p_image->get_width()) * 4 + bitsPerPixel:32]; + ERR_FAIL_COND_V(imgrep == nil, nil); + uint8_t *pixels = [imgrep bitmapData]; + + int len = p_image->get_width() * p_image->get_height(); + const uint8_t *r = p_image->get_data().ptr(); + + /* Premultiply the alpha channel */ + for (int i = 0; i < len; i++) { + uint8_t alpha = r[i * 4 + 3]; + pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); + pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); + pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); + pixels[i * 4 + 3] = alpha; + } + + NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(p_image->get_width(), p_image->get_height())]; + ERR_FAIL_COND_V(nsimg == nil, nil); + [nsimg addRepresentation:imgrep]; + return nsimg; +} + NSCursor *DisplayServerOSX::_cursor_from_selector(SEL p_selector, SEL p_fallback) { if ([NSCursor respondsToSelector:p_selector]) { id object = [NSCursor performSelector:p_selector]; @@ -498,7 +533,14 @@ void DisplayServerOSX::menu_callback(id p_sender) { GodotMenuItem *value = [p_sender representedObject]; if (value) { - if (value->checkable) { + if (value->max_states > 0) { + value->state++; + if (value->state >= value->max_states) { + value->state = 0; + } + } + + if (value->checkable_type == CHECKABLE_TYPE_CHECK_BOX) { if ([p_sender state] == NSControlStateValueOff) { [p_sender setState:NSControlStateValueOn]; } else { @@ -671,35 +713,195 @@ String DisplayServerOSX::get_name() const { return "OSX"; } -void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) { +void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_NONE; + obj->max_states = 0; + obj->state = 0; + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } +} + +void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; + obj->max_states = 0; + obj->state = 0; + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } +} + +void DisplayServerOSX::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_NONE; + obj->max_states = 0; + obj->state = 0; + if (p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); + [menu_item setImage:_convert_to_nsimg(obj->img)]; + } + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } +} + +void DisplayServerOSX::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; + obj->max_states = 0; + obj->state = 0; + if (p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); + [menu_item setImage:_convert_to_nsimg(obj->img)]; + } + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } +} + +void DisplayServerOSX::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; + obj->max_states = 0; + obj->state = 0; + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } +} + +void DisplayServerOSX::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""]; + String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; obj->meta = p_tag; - obj->checkable = false; + obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; + obj->max_states = 0; + obj->state = 0; + if (p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); + [menu_item setImage:_convert_to_nsimg(obj->img)]; + } + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; [menu_item setRepresentedObject:obj]; } } -void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) { +void DisplayServerOSX::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""]; + String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; obj->meta = p_tag; - obj->checkable = true; + obj->checkable_type = CHECKABLE_TYPE_NONE; + obj->max_states = p_max_states; + obj->state = p_default_state; + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; [menu_item setRepresentedObject:obj]; } } -void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) { +void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) { _THREAD_SAFE_METHOD_ NSMenu *menu = _get_menu_root(p_menu_root); @@ -713,18 +915,58 @@ void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, c ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!"); return; } - NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""]; + NSMenuItem *menu_item; + if (p_index != -1) { + menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index]; + } else { + menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""]; + } + [sub_menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]]; [menu setSubmenu:sub_menu forItem:menu_item]; } } -void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root) { +void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root, int p_index) { _THREAD_SAFE_METHOD_ NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - [menu addItem:[NSMenuItem separatorItem]]; + if (p_index != -1) { + [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index]; + } else { + [menu addItem:[NSMenuItem separatorItem]]; + } + } +} + +int DisplayServerOSX::global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; } + + return -1; +} + +int DisplayServerOSX::global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + for (NSInteger i = 0; i < [menu numberOfItems]; i++) { + const NSMenuItem *menu_item = [menu itemAtIndex:i]; + if (menu_item) { + const GodotMenuItem *obj = [menu_item representedObject]; + if (obj && obj->meta == p_tag) { + return i; + } + } + } + } + + return -1; } bool DisplayServerOSX::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const { @@ -749,14 +991,30 @@ bool DisplayServerOSX::global_menu_is_item_checkable(const String &p_menu_root, if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; if (obj) { - return obj->checkable; + return obj->checkable_type == CHECKABLE_TYPE_CHECK_BOX; + } + } + } + return false; +} + +bool DisplayServerOSX::global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->checkable_type == CHECKABLE_TYPE_RADIO_BUTTON; } } } return false; } -Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) { +Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ const NSMenu *menu = _get_menu_root(p_menu_root); @@ -772,7 +1030,7 @@ Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_ro return Callable(); } -Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) { +Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ const NSMenu *menu = _get_menu_root(p_menu_root); @@ -788,22 +1046,20 @@ Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, in return Variant(); } -String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) { +String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { - String ret; - ret.parse_utf8([[menu_item title] UTF8String]); - return ret; + return String::utf8([[menu_item title] UTF8String]); } } return String(); } -String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) { +String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ const NSMenu *menu = _get_menu_root(p_menu_root); @@ -823,6 +1079,116 @@ String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, return String(); } +Key DisplayServerOSX::global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + String ret = String::utf8([[menu_item keyEquivalent] UTF8String]); + Key keycode = find_keycode(ret); + NSUInteger mask = [menu_item keyEquivalentModifierMask]; + if (mask & NSEventModifierFlagControl) { + keycode |= KeyModifierMask::CTRL; + } + if (mask & NSEventModifierFlagOption) { + keycode |= KeyModifierMask::ALT; + } + if (mask & NSEventModifierFlagShift) { + keycode |= KeyModifierMask::SHIFT; + } + if (mask & NSEventModifierFlagCommand) { + keycode |= KeyModifierMask::META; + } + if (mask & NSEventModifierFlagNumericPad) { + keycode |= KeyModifierMask::KPAD; + } + return keycode; + } + } + return Key::NONE; +} + +bool DisplayServerOSX::global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + return ![menu_item isEnabled]; + } + } + return false; +} + +String DisplayServerOSX::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + return String::utf8([[menu_item toolTip] UTF8String]); + } + } + return String(); +} + +int DisplayServerOSX::global_menu_get_item_state(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->state; + } + } + } + return 0; +} + +int DisplayServerOSX::global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->max_states; + } + } + } + return 0; +} + +Ref<Texture2D> DisplayServerOSX::global_menu_get_item_icon(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + if (obj->img.is_valid()) { + Ref<ImageTexture> txt; + txt.instantiate(); + txt->create_from_image(obj->img); + return txt; + } + } + } + } + return Ref<Texture2D>(); +} + void DisplayServerOSX::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) { _THREAD_SAFE_METHOD_ @@ -853,7 +1219,23 @@ void DisplayServerOSX::global_menu_set_item_checkable(const String &p_menu_root, NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - obj->checkable = p_checkable; + obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE; + } + } +} + +void DisplayServerOSX::global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. + return; + } + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE; } } } @@ -929,6 +1311,116 @@ void DisplayServerOSX::global_menu_set_item_submenu(const String &p_menu_root, i } } +void DisplayServerOSX::global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. + return; + } + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_keycode)]; + String keycode = KeyMappingOSX::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); + [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } + } +} + +void DisplayServerOSX::global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. + return; + } + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setEnabled:(!p_disabled)]; + } + } +} + +void DisplayServerOSX::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. + return; + } + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]]; + } + } +} + +void DisplayServerOSX::global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. + return; + } + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + obj->state = p_state; + } + } + } +} + +void DisplayServerOSX::global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. + return; + } + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + obj->max_states = p_max_states; + } + } + } +} + +void DisplayServerOSX::global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. + return; + } + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); + [menu_item setImage:_convert_to_nsimg(obj->img)]; + } else { + obj->img = Ref<Image>(); + [menu_item setImage:nil]; + } + } + } +} + int DisplayServerOSX::global_menu_get_item_count(const String &p_menu_root) const { _THREAD_SAFE_METHOD_ @@ -1154,7 +1646,7 @@ bool DisplayServerOSX::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSP return false; } -void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) { +void DisplayServerOSX::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ if (mouse_mode != MOUSE_MODE_CAPTURED) { @@ -1164,7 +1656,7 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) { // Local point in window coords. const NSRect contentRect = [wd.window_view frame]; const float scale = screen_get_max_scale(); - NSRect pointInWindowRect = NSMakeRect(p_to.x / scale, contentRect.size.height - (p_to.y / scale - 1), 0, 0); + NSRect pointInWindowRect = NSMakeRect(p_position.x / scale, contentRect.size.height - (p_position.y / scale - 1), 0, 0); NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin; // Point in scren coords. @@ -1190,7 +1682,11 @@ Point2i DisplayServerOSX::mouse_get_position() const { for (NSScreen *screen in [NSScreen screens]) { NSRect frame = [screen frame]; if (NSMouseInRect(mouse_pos, frame, NO)) { - return Vector2i((int)mouse_pos.x, (int)-mouse_pos.y) * scale + _get_screens_origin(); + Vector2i pos = Vector2i((int)mouse_pos.x, (int)mouse_pos.y); + pos *= scale; + pos -= _get_screens_origin(); + pos.y *= -1; + return pos; } } return Vector2i(); @@ -2628,11 +3124,13 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode // Setup Dock menu. dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"]; + [dock_menu setAutoenablesItems:NO]; // Setup Apple menu. apple_menu = [[NSMenu alloc] initWithTitle:@""]; title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""]; + [apple_menu setAutoenablesItems:NO]; [apple_menu addItem:[NSMenuItem separatorItem]]; @@ -2660,6 +3158,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode NSMenu *main_menu = [NSApp mainMenu]; menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; [main_menu setSubmenu:apple_menu forItem:menu_item]; + [main_menu setAutoenablesItems:NO]; //!!!!!!!!!!!!!!!!!!!!!!!!!! //TODO - do Vulkan and OpenGL support checks, driver selection and fallback diff --git a/platform/osx/godot_menu_item.h b/platform/osx/godot_menu_item.h index 50c4709c18..2c12897f10 100644 --- a/platform/osx/godot_menu_item.h +++ b/platform/osx/godot_menu_item.h @@ -36,12 +36,21 @@ #import <AppKit/AppKit.h> #import <Foundation/Foundation.h> +enum GlobalMenuCheckType { + CHECKABLE_TYPE_NONE, + CHECKABLE_TYPE_CHECK_BOX, + CHECKABLE_TYPE_RADIO_BUTTON, +}; + @interface GodotMenuItem : NSObject { @public Callable callback; Variant meta; int id; - bool checkable; + GlobalMenuCheckType checkable_type; + int max_states; + int state; + Ref<Image> img; } @end diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index 230e5c749c..88343d6f85 100644 --- a/platform/uwp/export/export_plugin.cpp +++ b/platform/uwp/export/export_plugin.cpp @@ -131,6 +131,14 @@ void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options) } bool EditorExportPlatformUWP::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { +#ifndef DEV_ENABLED + // We don't provide export templates for the UWP platform currently as it + // has not been ported for Godot 4.0. This is skipped in DEV_ENABLED so that + // contributors can still test the pipeline if/when we can build it again. + r_error = "The UWP platform is currently not supported in Godot 4.0.\n"; + return false; +#endif + String err; bool valid = false; diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index d243d4c05d..16323dcc13 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -150,7 +150,7 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const { return mouse_mode; } -void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { +void DisplayServerWindows::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { @@ -158,12 +158,12 @@ void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { } if (mouse_mode == MOUSE_MODE_CAPTURED) { - old_x = p_to.x; - old_y = p_to.y; + old_x = p_position.x; + old_y = p_position.y; } else { POINT p; - p.x = p_to.x; - p.y = p_to.y; + p.x = p_position.x; + p.y = p_position.y; ClientToScreen(windows[last_focused_window].hWnd, &p); SetCursorPos(p.x, p.y); diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index a56a2b83ac..71fedf2bca 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -457,7 +457,7 @@ public: virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; - virtual void mouse_warp_to_position(const Point2i &p_to) override; + virtual void warp_mouse(const Point2i &p_position) override; virtual Point2i mouse_get_position() const override; virtual MouseButton mouse_get_button_state() const override; diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 17a24c08bf..0fa2913218 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -32,8 +32,6 @@ #include "export_plugin.h" -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); - void register_windows_exporter() { EDITOR_DEF("export/windows/rcedit", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); @@ -57,84 +55,7 @@ void register_windows_exporter() { logo->create_from_image(img); platform->set_logo(logo); platform->set_name("Windows Desktop"); - platform->set_extension("exe"); - platform->set_release_32("windows_32_release.exe"); - platform->set_debug_32("windows_32_debug.exe"); - platform->set_release_64("windows_64_release.exe"); - platform->set_debug_64("windows_64_debug.exe"); platform->set_os_name("Windows"); - platform->set_fixup_embedded_pck_func(&fixup_embedded_pck); EditorExport::get_singleton()->add_export_platform(platform); } - -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { - // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data - - FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE); - if (!f) { - return ERR_CANT_OPEN; - } - - // Jump to the PE header and check the magic number - { - f->seek(0x3c); - uint32_t pe_pos = f->get_32(); - - f->seek(pe_pos); - uint32_t magic = f->get_32(); - if (magic != 0x00004550) { - f->close(); - return ERR_FILE_CORRUPT; - } - } - - // Process header - - int num_sections; - { - int64_t header_pos = f->get_position(); - - f->seek(header_pos + 2); - num_sections = f->get_16(); - f->seek(header_pos + 16); - uint16_t opt_header_size = f->get_16(); - - // Skip rest of header + optional header to go to the section headers - f->seek(f->get_position() + 2 + opt_header_size); - } - - // Search for the "pck" section - - int64_t section_table_pos = f->get_position(); - - bool found = false; - for (int i = 0; i < num_sections; ++i) { - int64_t section_header_pos = section_table_pos + i * 40; - f->seek(section_header_pos); - - uint8_t section_name[9]; - f->get_buffer(section_name, 8); - section_name[8] = '\0'; - - if (strcmp((char *)section_name, "pck") == 0) { - // "pck" section found, let's patch! - - // Set virtual size to a little to avoid it taking memory (zero would give issues) - f->seek(section_header_pos + 8); - f->store_32(8); - - f->seek(section_header_pos + 16); - f->store_32(p_embedded_size); - f->seek(section_header_pos + 20); - f->store_32(p_embedded_start); - - found = true; - break; - } - } - - f->close(); - - return found ? OK : ERR_FILE_CORRUPT; -} diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 7b9cb59896..e627253739 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -86,6 +86,16 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> return err; } +String EditorExportPlatformWindows::get_template_file_name(const String &p_target, const String &p_arch) const { + return "windows_" + p_arch + "_" + p_target + ".exe"; +} + +List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("exe"); + return list; +} + bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { // This option is not supported by "osslsigncode", used on non-Windows host. if (!OS::get_singleton()->has_feature("windows") && p_option == "codesign/identity_type") { @@ -403,3 +413,74 @@ bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_pr return valid; } + +Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const { + // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE); + if (!f) { + return ERR_CANT_OPEN; + } + + // Jump to the PE header and check the magic number + { + f->seek(0x3c); + uint32_t pe_pos = f->get_32(); + + f->seek(pe_pos); + uint32_t magic = f->get_32(); + if (magic != 0x00004550) { + f->close(); + return ERR_FILE_CORRUPT; + } + } + + // Process header + + int num_sections; + { + int64_t header_pos = f->get_position(); + + f->seek(header_pos + 2); + num_sections = f->get_16(); + f->seek(header_pos + 16); + uint16_t opt_header_size = f->get_16(); + + // Skip rest of header + optional header to go to the section headers + f->seek(f->get_position() + 2 + opt_header_size); + } + + // Search for the "pck" section + + int64_t section_table_pos = f->get_position(); + + bool found = false; + for (int i = 0; i < num_sections; ++i) { + int64_t section_header_pos = section_table_pos + i * 40; + f->seek(section_header_pos); + + uint8_t section_name[9]; + f->get_buffer(section_name, 8); + section_name[8] = '\0'; + + if (strcmp((char *)section_name, "pck") == 0) { + // "pck" section found, let's patch! + + // Set virtual size to a little to avoid it taking memory (zero would give issues) + f->seek(section_header_pos + 8); + f->store_32(8); + + f->seek(section_header_pos + 16); + f->store_32(p_embedded_size); + f->seek(section_header_pos + 20); + f->store_32(p_embedded_start); + + found = true; + break; + } + } + + f->close(); + + return found ? OK : ERR_FILE_CORRUPT; +} diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index b40e872461..39d1cf4c77 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -45,9 +45,12 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC { public: virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; virtual void get_export_options(List<ExportOption> *r_options) override; virtual bool get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override; virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) 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) const override; }; #endif |