diff options
Diffstat (limited to 'platform')
| -rw-r--r-- | platform/android/dir_access_jandroid.cpp | 16 | ||||
| -rw-r--r-- | platform/android/dir_access_jandroid.h | 3 | ||||
| -rw-r--r-- | platform/linuxbsd/display_server_x11.cpp | 181 | ||||
| -rw-r--r-- | platform/linuxbsd/display_server_x11.h | 5 | ||||
| -rw-r--r-- | platform/macos/SCsub | 2 | ||||
| -rw-r--r-- | platform/macos/display_server_macos.h | 17 | ||||
| -rw-r--r-- | platform/macos/display_server_macos.mm | 85 | ||||
| -rw-r--r-- | platform/macos/godot_menu_delegate.h | 44 | ||||
| -rw-r--r-- | platform/macos/godot_menu_delegate.mm | 87 | ||||
| -rw-r--r-- | platform/macos/godot_menu_item.h | 4 | ||||
| -rw-r--r-- | platform/macos/godot_menu_item.mm | 34 | ||||
| -rw-r--r-- | platform/web/SCsub | 26 | ||||
| -rw-r--r-- | platform/web/detect.py | 70 | ||||
| -rw-r--r-- | platform/web/emscripten_helpers.py | 13 | ||||
| -rw-r--r-- | platform/web/export/export_plugin.cpp | 30 | ||||
| -rw-r--r-- | platform/web/export/export_plugin.h | 18 | ||||
| -rw-r--r-- | platform/web/os_web.cpp | 17 |
17 files changed, 405 insertions, 247 deletions
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 373c6e37f1..4f1ac16975 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -135,6 +135,13 @@ String DirAccessJAndroid::get_drive(int p_drive) { } } +String DirAccessJAndroid::_get_root_string() const { + if (get_access_type() == ACCESS_FILESYSTEM) { + return "/"; + } + return DirAccessUnix::_get_root_string(); +} + String DirAccessJAndroid::get_current_dir(bool p_include_drive) const { String base = _get_root_path(); String bd = current_dir; @@ -142,10 +149,13 @@ String DirAccessJAndroid::get_current_dir(bool p_include_drive) const { bd = current_dir.replace_first(base, ""); } - if (bd.begins_with("/")) { - return _get_root_string() + bd.substr(1, bd.length()); + String root_string = _get_root_string(); + if (bd.begins_with(root_string)) { + return bd; + } else if (bd.begins_with("/")) { + return root_string + bd.substr(1, bd.length()); } else { - return _get_root_string() + bd; + return root_string + bd; } } diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index 5b7b4a9c4d..5c4f1852a9 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -91,6 +91,9 @@ public: DirAccessJAndroid(); ~DirAccessJAndroid(); +protected: + String _get_root_string() const override; + private: int id = 0; diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 017fc3e3a2..0d619904bc 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -1823,6 +1823,47 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a return retval; } +bool DisplayServerX11::_window_minimize_check(WindowID p_window) const { + const WindowData &wd = windows[p_window]; + + // Using ICCCM -- Inter-Client Communication Conventions Manual + Atom property = XInternAtom(x11_display, "WM_STATE", True); + if (property == None) { + return false; + } + + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = nullptr; + + int result = XGetWindowProperty( + x11_display, + wd.x11_window, + property, + 0, + 32, + False, + AnyPropertyType, + &type, + &format, + &len, + &remaining, + &data); + + if (result == Success && data) { + long *state = (long *)data; + if (state[0] == WM_IconicState) { + XFree(data); + return true; + } + XFree(data); + } + + return false; +} + bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), false); const WindowData &wd = windows[p_window]; @@ -1869,11 +1910,15 @@ bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const { return retval; } -void DisplayServerX11::_validate_fullscreen_on_map(WindowID p_window) { +void DisplayServerX11::_validate_mode_on_map(WindowID p_window) { + // Check if we applied any window modes that didn't take effect while unmapped const WindowData &wd = windows[p_window]; if (wd.fullscreen && !_window_fullscreen_check(p_window)) { - // Unmapped fullscreen attempt didn't take effect, redo... _set_wm_fullscreen(p_window, true); + } else if (wd.maximized && !_window_maximize_check(p_window, "_NET_WM_STATE")) { + _set_wm_maximized(p_window, true); + } else if (wd.minimized && !_window_minimize_check(p_window)) { + _set_wm_minimized(p_window, true); } } @@ -1911,6 +1956,37 @@ void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) { usleep(10000); } } + wd.maximized = p_enabled; +} + +void DisplayServerX11::_set_wm_minimized(WindowID p_window, bool p_enabled) { + WindowData &wd = windows[p_window]; + // Using ICCCM -- Inter-Client Communication Conventions Manual + XEvent xev; + Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_change; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = wm_hidden; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + wd.minimized = p_enabled; } void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) { @@ -1992,32 +2068,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) { //do nothing } break; case WINDOW_MODE_MINIMIZED: { - //Un-Minimize - // Using ICCCM -- Inter-Client Communication Conventions Manual - XEvent xev; - Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_change; - xev.xclient.format = 32; - xev.xclient.data.l[0] = WM_NormalState; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = wm_hidden; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + _set_wm_minimized(p_window, false); } break; case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: case WINDOW_MODE_FULLSCREEN: { @@ -2046,31 +2097,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) { //do nothing } break; case WINDOW_MODE_MINIMIZED: { - // Using ICCCM -- Inter-Client Communication Conventions Manual - XEvent xev; - Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_change; - xev.xclient.format = 32; - xev.xclient.data.l[0] = WM_IconicState; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = wm_hidden; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + _set_wm_minimized(p_window, true); } break; case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: case WINDOW_MODE_FULLSCREEN: { @@ -2105,40 +2132,9 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c return WINDOW_MODE_MAXIMIZED; } - { // Test minimized. - // Using ICCCM -- Inter-Client Communication Conventions Manual - Atom property = XInternAtom(x11_display, "WM_STATE", True); - if (property == None) { - return WINDOW_MODE_WINDOWED; - } - - Atom type; - int format; - unsigned long len; - unsigned long remaining; - unsigned char *data = nullptr; - - int result = XGetWindowProperty( - x11_display, - wd.x11_window, - property, - 0, - 32, - False, - AnyPropertyType, - &type, - &format, - &len, - &remaining, - &data); - - if (result == Success && data) { - long *state = (long *)data; - if (state[0] == WM_IconicState) { - XFree(data); - return WINDOW_MODE_MINIMIZED; - } - XFree(data); + { + if (_window_minimize_check(p_window)) { + return WINDOW_MODE_MINIMIZED; } } @@ -3658,9 +3654,6 @@ void DisplayServerX11::process_events() { const WindowData &wd = windows[window_id]; - // Have we failed to set fullscreen while the window was unmapped? - _validate_fullscreen_on_map(window_id); - XWindowAttributes xwa; XSync(x11_display, False); XGetWindowAttributes(x11_display, wd.x11_window, &xwa); @@ -3671,6 +3664,9 @@ void DisplayServerX11::process_events() { if ((xwa.map_state == IsViewable) && !wd.no_focus && !wd.is_popup) { XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime); } + + // Have we failed to set fullscreen while the window was unmapped? + _validate_mode_on_map(window_id); } break; case Expose: { @@ -3690,8 +3686,7 @@ void DisplayServerX11::process_events() { case VisibilityNotify: { DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state); - XVisibilityEvent *visibility = (XVisibilityEvent *)&event; - windows[window_id].minimized = (visibility->state == VisibilityFullyObscured); + windows[window_id].minimized = _window_minimize_check(window_id); } break; case LeaveNotify: { @@ -5003,7 +4998,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode _window_changed(&xevent); } else if (xevent.type == MapNotify) { // Have we failed to set fullscreen while the window was unmapped? - _validate_fullscreen_on_map(main_window); + _validate_mode_on_map(main_window); } } diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 6b9f57d78f..650598d243 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -152,6 +152,7 @@ class DisplayServerX11 : public DisplayServer { Vector2i last_position_before_fs; bool focused = true; bool minimized = false; + bool maximized = false; bool is_popup = false; Rect2i parent_safe_rect; @@ -268,10 +269,12 @@ class DisplayServerX11 : public DisplayServer { void _update_real_mouse_position(const WindowData &wd); bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const; bool _window_fullscreen_check(WindowID p_window) const; - void _validate_fullscreen_on_map(WindowID p_window); + bool _window_minimize_check(WindowID p_window) const; + void _validate_mode_on_map(WindowID p_window); void _update_size_hints(WindowID p_window); void _set_wm_fullscreen(WindowID p_window, bool p_enabled); void _set_wm_maximized(WindowID p_window, bool p_enabled); + void _set_wm_minimized(WindowID p_window, bool p_enabled); void _update_context(WindowData &wd); diff --git a/platform/macos/SCsub b/platform/macos/SCsub index d0856c709a..bbd461fba9 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -17,6 +17,8 @@ files = [ "godot_window.mm", "key_mapping_macos.mm", "godot_main_macos.mm", + "godot_menu_delegate.mm", + "godot_menu_item.mm", "dir_access_macos.mm", "tts_macos.mm", "joypad_macos.cpp", diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index a08667a259..cd457836de 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -140,6 +140,7 @@ private: int key_event_pos = 0; id tts = nullptr; + id menu_delegate = nullptr; Point2i im_selection; String im_text; @@ -227,14 +228,14 @@ public: virtual bool has_feature(Feature p_feature) const override; virtual String get_name() const override; - virtual int 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 int 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 int 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 int 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 int 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 int 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 int 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 int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override; + virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int 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 Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int 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 Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int 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 Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int 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 Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; virtual int 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; @@ -244,6 +245,7 @@ public: virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const 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 Callable global_menu_get_item_key_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; @@ -259,6 +261,7 @@ public: 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_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_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; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index b06ae9f27c..c117713c2b 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -31,6 +31,7 @@ #include "display_server_macos.h" #include "godot_content_view.h" +#include "godot_menu_delegate.h" #include "godot_menu_item.h" #include "godot_window.h" #include "godot_window_delegate.h" @@ -95,6 +96,7 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { if (!submenu.has(p_menu_root)) { NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; [n_menu setAutoenablesItems:NO]; + [n_menu setDelegate:menu_delegate]; submenu[p_menu_root] = n_menu; } menu = submenu[p_menu_root]; @@ -754,7 +756,7 @@ NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const return nullptr; } -int DisplayServerMacOS::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) { +int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -762,6 +764,7 @@ int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const St if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_NONE; obj->max_states = 0; @@ -772,7 +775,7 @@ int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const St return out; } -int DisplayServerMacOS::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) { +int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -780,6 +783,7 @@ int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, co if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; obj->max_states = 0; @@ -790,7 +794,7 @@ int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, co return out; } -int DisplayServerMacOS::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) { +int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -798,6 +802,7 @@ int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, con if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_NONE; obj->max_states = 0; @@ -817,7 +822,7 @@ int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, con return out; } -int DisplayServerMacOS::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) { +int DisplayServerMacOS::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 Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -825,6 +830,7 @@ int DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_roo if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; obj->max_states = 0; @@ -844,7 +850,7 @@ int DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_roo return out; } -int DisplayServerMacOS::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) { +int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -852,6 +858,7 @@ int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_ro if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; obj->max_states = 0; @@ -862,7 +869,7 @@ int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_ro return out; } -int DisplayServerMacOS::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) { +int DisplayServerMacOS::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 Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -870,6 +877,7 @@ int DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_me if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; obj->max_states = 0; @@ -889,30 +897,15 @@ int DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_me return out; } -int DisplayServerMacOS::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) { +int DisplayServerMacOS::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 Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ - NSMenu *menu = _get_menu_root(p_menu_root); int out = -1; - if (menu) { - String keycode = KeyMappingMacOS::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - int item_count = ((menu == [NSApp mainMenu]) && _has_help_menu()) ? [menu numberOfItems] - 1 : [menu numberOfItems]; - if ((menu == [NSApp mainMenu]) && (p_label == "Help" || p_label == RTR("Help"))) { - p_index = [menu numberOfItems]; - } else if (p_index < 0) { - p_index = item_count; - } else { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_index++; - } - p_index = CLAMP(p_index, 0, item_count); - } - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index; - + NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); + if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_NONE; obj->max_states = p_max_states; @@ -1104,6 +1097,27 @@ Callable DisplayServerMacOS::global_menu_get_item_callback(const String &p_menu_ return Callable(); } +Callable DisplayServerMacOS::global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND_V(p_idx < 0, Callable()); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Callable()); + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->key_callback; + } + } + } + return Callable(); +} + Variant DisplayServerMacOS::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ @@ -1401,6 +1415,25 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root } } +void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_COND(!obj); + obj->key_callback = p_key_callback; + } + } +} + void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) { _THREAD_SAFE_METHOD_ @@ -3477,6 +3510,8 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [main_menu setSubmenu:apple_menu forItem:menu_item]; [main_menu setAutoenablesItems:NO]; + menu_delegate = [[GodotMenuDelegate alloc] init]; + //!!!!!!!!!!!!!!!!!!!!!!!!!! //TODO - do Vulkan and OpenGL support checks, driver selection and fallback rendering_driver = p_rendering_driver; diff --git a/platform/macos/godot_menu_delegate.h b/platform/macos/godot_menu_delegate.h new file mode 100644 index 0000000000..805ac0c4a3 --- /dev/null +++ b/platform/macos/godot_menu_delegate.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* godot_menu_delegate.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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. */ +/*************************************************************************/ + +#ifndef GODOT_MENU_DELEGATE_H +#define GODOT_MENU_DELEGATE_H + +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> + +@interface GodotMenuDelegate : NSObject <NSMenuDelegate> { +} + +- (void)doNothing:(id)sender; + +@end + +#endif // GODOT_MENU_DELEGATE_H diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm new file mode 100644 index 0000000000..bd394d8415 --- /dev/null +++ b/platform/macos/godot_menu_delegate.mm @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* godot_menu_delegate.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "godot_menu_delegate.h" + +#include "display_server_macos.h" +#include "godot_menu_item.h" +#include "key_mapping_macos.h" + +@implementation GodotMenuDelegate + +- (void)doNothing:(id)sender { +} + +- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action { + NSString *ev_key = [[event charactersIgnoringModifiers] lowercaseString]; + NSUInteger ev_modifiers = [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + for (int i = 0; i < [menu numberOfItems]; i++) { + const NSMenuItem *menu_item = [menu itemAtIndex:i]; + if ([menu_item isEnabled] && [[menu_item keyEquivalent] compare:ev_key] == NSOrderedSame) { + NSUInteger item_modifiers = [menu_item keyEquivalentModifierMask]; + + if (ev_modifiers == item_modifiers) { + GodotMenuItem *value = [menu_item representedObject]; + if (value->key_callback != Callable()) { + // If custom callback is set, use it. + Variant tag = value->meta; + Variant *tagp = &tag; + Variant ret; + Callable::CallError ce; + value->key_callback.callp((const Variant **)&tagp, 1, ret, ce); + } else { + // Otherwise redirect event to the engine. + if (DisplayServer::get_singleton()) { + DisplayServerMacOS::KeyEvent ke; + + ke.window_id = DisplayServer::MAIN_WINDOW_ID; + ke.macos_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = [event isARepeat]; + ke.keycode = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags]); + ke.physical_keycode = KeyMappingMacOS::translate_key([event keyCode]); + ke.raw = false; + ke.unicode = 0; + + reinterpret_cast<DisplayServerMacOS *>(DisplayServer::get_singleton())->push_to_key_event_buffer(ke); + } + } + + // Suppress default menu action. + *target = self; + *action = @selector(doNothing:); + return YES; + } + } + } + return NO; +} + +@end diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h index e0b9f41632..e96f5dc1cf 100644 --- a/platform/macos/godot_menu_item.h +++ b/platform/macos/godot_menu_item.h @@ -45,6 +45,7 @@ enum GlobalMenuCheckType { @interface GodotMenuItem : NSObject { @public Callable callback; + Callable key_callback; Variant meta; GlobalMenuCheckType checkable_type; int max_states; @@ -54,7 +55,4 @@ enum GlobalMenuCheckType { @end -@implementation GodotMenuItem -@end - #endif // GODOT_MENU_ITEM_H diff --git a/platform/macos/godot_menu_item.mm b/platform/macos/godot_menu_item.mm new file mode 100644 index 0000000000..ea35e35d19 --- /dev/null +++ b/platform/macos/godot_menu_item.mm @@ -0,0 +1,34 @@ +/*************************************************************************/ +/* godot_menu_item.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "godot_menu_item.h" + +@implementation GodotMenuItem +@end diff --git a/platform/web/SCsub b/platform/web/SCsub index 78d7ce4074..ae9d628857 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -35,18 +35,17 @@ for ext in env["JS_EXTERNS"]: sys_env["ENV"]["EMCC_CLOSURE_ARGS"] += " --externs " + ext.abspath build = [] -if env["gdnative_enabled"]: - build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] - if env["threads_enabled"]: - build_targets.append("#bin/godot${PROGSUFFIX}.worker.js") +build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm", "#bin/godot${PROGSUFFIX}.worker.js"] +if env["dlink_enabled"]: # Reset libraries. The main runtime will only link emscripten libraries, not godot ones. sys_env["LIBS"] = [] # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. sys_env.Append(LIBS=["idbfs.js"]) # Configure it as a main module (dynamic linking support). + sys_env["CCFLAGS"].remove("SIDE_MODULE=2") + sys_env["LINKFLAGS"].remove("SIDE_MODULE=2") sys_env.Append(CCFLAGS=["-s", "MAIN_MODULE=1"]) sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"]) - sys_env.Append(CCFLAGS=["-s", "EXPORT_ALL=1"]) sys_env.Append(LINKFLAGS=["-s", "EXPORT_ALL=1"]) sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"]) # Force exporting the standard library (printf, malloc, etc.) @@ -55,16 +54,9 @@ if env["gdnative_enabled"]: sys = sys_env.Program(build_targets, ["web_runtime.cpp"]) # The side library, containing all Godot code. - wasm_env = env.Clone() - wasm_env.Append(CPPDEFINES=["WASM_GDNATIVE"]) # So that OS knows it can run GDNative libraries. - wasm_env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) - wasm_env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"]) - wasm = wasm_env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", web_files) + wasm = env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", web_files) build = sys + [wasm[0]] else: - build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"] - if env["threads_enabled"]: - build_targets.append("#bin/godot${PROGSUFFIX}.worker.js") # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. sys_env.Append(LIBS=["idbfs.js"]) build = sys_env.Program(build_targets, web_files + ["web_runtime.cpp"]) @@ -88,6 +80,8 @@ wrap_list = [ ] js_wrapped = env.Textfile("#bin/godot", [env.File(f) for f in wrap_list], TEXTFILESUFFIX="${PROGSUFFIX}.wrapped.js") -# Extra will be the thread worker, or the GDNative side, or None -extra = build[2:] if len(build) > 2 else None -env.CreateTemplateZip(js_wrapped, build[1], extra) +# 0 - unwrapped js file (use wrapped one instead) +# 1 - wasm file +# 2 - worker file +# 3 - wasm side (when dlink is enabled). +env.CreateTemplateZip(js_wrapped, build[1], build[2], build[3] if len(build) > 3 else None) diff --git a/platform/web/detect.py b/platform/web/detect.py index ae0ff2c4ea..b1c1dd48a9 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -38,8 +38,9 @@ def get_opts(): BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False), # eval() can be a security concern, so it can be disabled. BoolVariable("javascript_eval", "Enable JavaScript eval interface", True), - BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", True), - BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False), + BoolVariable( + "dlink_enabled", "Enable WebAssembly dynamic linking (GDExtension support). Produces bigger binaries", False + ), BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False), ] @@ -50,6 +51,13 @@ def get_flags(): ("tools", False), ("builtin_pcre2_with_jit", False), ("vulkan", False), + # Use -Os to prioritize optimizing for reduced file size. This is + # particularly valuable for the web platform because it directly + # decreases download time. + # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about + # 100 KiB over -Os, which does not justify the negative impact on + # run-time performance. + ("optimize", "size"), ] @@ -71,15 +79,12 @@ def configure(env): ## Build type if env["target"].startswith("release"): - # Use -Os to prioritize optimizing for reduced file size. This is - # particularly valuable for the web platform because it directly - # decreases download time. - # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about - # 100 KiB over -Os, which does not justify the negative impact on - # run-time performance. - if env["optimize"] != "none": + if env["optimize"] == "size": env.Append(CCFLAGS=["-Os"]) env.Append(LINKFLAGS=["-Os"]) + elif env["optimize"] == "speed": + env.Append(CCFLAGS=["-O3"]) + env.Append(LINKFLAGS=["-O3"]) if env["target"] == "release_debug": # Retain function names for backtraces at the cost of file size. @@ -93,21 +98,11 @@ def configure(env): env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) if env["tools"]: - if not env["threads_enabled"]: - print('Note: Forcing "threads_enabled=yes" as it is required for the web editor.') - env["threads_enabled"] = "yes" if env["initial_memory"] < 64: print('Note: Forcing "initial_memory=64" as it is required for the web editor.') env["initial_memory"] = 64 - env.Append(CCFLAGS=["-frtti"]) - elif env["builtin_icu"]: - env.Append(CCFLAGS=["-fno-exceptions", "-frtti"]) else: - # Disable exceptions and rtti on non-tools (template) builds - # These flags help keep the file size down. - env.Append(CCFLAGS=["-fno-exceptions", "-fno-rtti"]) - # Don't use dynamic_cast, necessary with no-rtti. - env.Append(CPPDEFINES=["NO_SAFE_CAST"]) + env.Append(CPPFLAGS=["-fno-exceptions"]) env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]]) @@ -171,9 +166,9 @@ def configure(env): env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix") env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}" - # All intermediate files are just LLVM bitcode. + # All intermediate files are just object files. env["OBJPREFIX"] = "" - env["OBJSUFFIX"] = ".bc" + env["OBJSUFFIX"] = ".o" env["PROGPREFIX"] = "" # Program() output consists of multiple files, so specify suffixes manually at builder. env["PROGSUFFIX"] = "" @@ -196,31 +191,22 @@ def configure(env): env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"]) # Thread support (via SharedArrayBuffer). - if env["threads_enabled"]: - env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) - env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) - env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) - env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) - env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) - env.extra_suffix = ".threads" + env.extra_suffix - else: - env.Append(CPPDEFINES=["NO_THREADS"]) + env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) + env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) + env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) - if env["gdnative_enabled"]: + if env["dlink_enabled"]: cc_version = get_compiler_version(env) cc_semver = (int(cc_version["major"]), int(cc_version["minor"]), int(cc_version["patch"])) - if cc_semver < (2, 0, 10): - print("GDNative support requires emscripten >= 2.0.10, detected: %s.%s.%s" % cc_semver) + if cc_semver < (3, 1, 14): + print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) sys.exit(255) - if env["threads_enabled"] and cc_semver < (3, 1, 14): - print("Threads and GDNative requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) - sys.exit(255) - env.Append(CCFLAGS=["-s", "RELOCATABLE=1"]) - env.Append(LINKFLAGS=["-s", "RELOCATABLE=1"]) - # Weak symbols are broken upstream: https://github.com/emscripten-core/emscripten/issues/12819 - env.Append(CPPDEFINES=["ZSTD_HAVE_WEAK_SYMBOLS=0"]) - env.extra_suffix = ".gdnative" + env.extra_suffix + env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) + env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"]) + env.extra_suffix = ".dlink" + env.extra_suffix # Reduce code size by generating less support code (e.g. skip NodeJS support). env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"]) diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py index b7b1026ef7..6045bc6fbd 100644 --- a/platform/web/emscripten_helpers.py +++ b/platform/web/emscripten_helpers.py @@ -37,26 +37,25 @@ def create_engine_file(env, target, source, externs): return env.Textfile(target, [env.File(s) for s in source]) -def create_template_zip(env, js, wasm, extra): +def create_template_zip(env, js, wasm, worker, side): binary_name = "godot.tools" if env["tools"] else "godot" zip_dir = env.Dir("#bin/.web_zip") in_files = [ js, wasm, + worker, "#platform/web/js/libs/audio.worklet.js", ] out_files = [ zip_dir.File(binary_name + ".js"), zip_dir.File(binary_name + ".wasm"), + zip_dir.File(binary_name + ".worker.js"), zip_dir.File(binary_name + ".audio.worklet.js"), ] - # GDNative/Threads specific - if env["gdnative_enabled"]: - in_files.append(extra.pop()) # Runtime + # Dynamic linking (extensions) specific. + if env["dlink_enabled"]: + in_files.append(side) # Side wasm (contains the actual Godot code). out_files.append(zip_dir.File(binary_name + ".side.wasm")) - if env["threads_enabled"]: - in_files.append(extra.pop()) # Worker - out_files.append(zip_dir.File(binary_name + ".worker.js")) service_worker = "#misc/dist/html/service-worker.js" if env["tools"]: diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 2cd8ec88ef..9971481459 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -201,7 +201,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese // Service worker const String dir = p_path.get_base_dir(); const String name = p_path.get_file().get_basename(); - const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + bool extensions = (bool)p_preset->get("variant/extensions_support"); HashMap<String, String> replaces; replaces["@GODOT_VERSION@"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec()); replaces["@GODOT_NAME@"] = proj_name.substr(0, 16); @@ -216,17 +216,15 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese cache_files.push_back(name + ".icon.png"); cache_files.push_back(name + ".apple-touch-icon.png"); } - if (mode & EXPORT_MODE_THREADS) { - cache_files.push_back(name + ".worker.js"); - cache_files.push_back(name + ".audio.worklet.js"); - } + cache_files.push_back(name + ".worker.js"); + cache_files.push_back(name + ".audio.worklet.js"); replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string(); // Heavy files that are cached on demand. Array opt_cache_files; opt_cache_files.push_back(name + ".wasm"); opt_cache_files.push_back(name + ".pck"); - if (mode & EXPORT_MODE_GDNATIVE) { + if (extensions) { opt_cache_files.push_back(name + ".side.wasm"); for (int i = 0; i < p_shared_objects.size(); i++) { opt_cache_files.push_back(p_shared_objects[i].path.get_file()); @@ -317,20 +315,14 @@ void EditorExportPlatformWeb::get_preset_features(const Ref<EditorExportPreset> r_features->push_back("etc2"); } } - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - if (mode & EXPORT_MODE_THREADS) { - r_features->push_back("threads"); - } - if (mode & EXPORT_MODE_GDNATIVE) { - r_features->push_back("wasm32"); - } + r_features->push_back("wasm32"); } void EditorExportPlatformWeb::get_export_options(List<ExportOption> *r_options) { r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type. + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "variant/extensions_support"), false)); // Export type. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer @@ -374,11 +366,11 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp String err; bool valid = false; - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + bool extensions = (bool)p_preset->get("variant/extensions_support"); // Look for export templates (first official, and if defined custom templates). - bool dvalid = exists_export_template(_get_template_name(mode, true), &err); - bool rvalid = exists_export_template(_get_template_name(mode, false), &err); + bool dvalid = exists_export_template(_get_template_name(extensions, true), &err); + bool rvalid = exists_export_template(_get_template_name(extensions, false), &err); if (p_preset->get("custom_template/debug") != "") { dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); @@ -456,8 +448,8 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p String template_path = p_debug ? custom_debug : custom_release; template_path = template_path.strip_edges(); if (template_path.is_empty()) { - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - template_path = find_export_template(_get_template_name(mode, p_debug)); + bool extensions = (bool)p_preset->get("variant/extensions_support"); + template_path = find_export_template(_get_template_name(extensions, p_debug)); } if (!DirAccess::exists(base_dir)) { diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index 9c1bf2a5ef..5b7ce5f708 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -57,20 +57,10 @@ class EditorExportPlatformWeb : public EditorExportPlatform { Mutex server_lock; Thread server_thread; - enum ExportMode { - EXPORT_MODE_NORMAL = 0, - EXPORT_MODE_THREADS = 1, - EXPORT_MODE_GDNATIVE = 2, - EXPORT_MODE_THREADS_GDNATIVE = 3, - }; - - String _get_template_name(ExportMode p_mode, bool p_debug) const { - String name = "webassembly"; - if (p_mode & EXPORT_MODE_GDNATIVE) { - name += "_gdnative"; - } - if (p_mode & EXPORT_MODE_THREADS) { - name += "_threads"; + String _get_template_name(bool p_extension, bool p_debug) const { + String name = "web"; + if (p_extension) { + name += "_dlink"; } if (p_debug) { name += "_debug.zip"; diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index 461dc71119..f9714f25e7 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -140,26 +140,9 @@ int OS_Web::get_processor_count() const { } bool OS_Web::_check_internal_feature_support(const String &p_feature) { - if (p_feature == "html5" || p_feature == "web") { - return true; - } - -#ifdef JAVASCRIPT_EVAL_ENABLED if (p_feature == "web") { return true; } -#endif -#ifndef NO_THREADS - if (p_feature == "threads") { - return true; - } -#endif -#if WASM_GDNATIVE - if (p_feature == "wasm32") { - return true; - } -#endif - return false; } |