summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/android_input_handler.cpp53
-rw-r--r--platform/android/android_input_handler.h2
-rw-r--r--platform/android/android_keys_utils.cpp47
-rw-r--r--platform/android/android_keys_utils.h251
-rw-r--r--platform/android/export/export_plugin.cpp27
-rw-r--r--platform/android/export/export_plugin.h5
-rw-r--r--platform/android/file_access_filesystem_jandroid.cpp13
-rw-r--r--platform/android/file_access_filesystem_jandroid.h2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java19
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java20
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt5
-rw-r--r--platform/android/java_godot_lib_jni.cpp5
-rw-r--r--platform/android/java_godot_lib_jni.h2
-rw-r--r--platform/ios/display_server_ios.mm2
-rw-r--r--platform/ios/export/export_plugin.cpp57
-rw-r--r--platform/ios/export/export_plugin.h8
-rw-r--r--platform/ios/export/godot_plugin_config.cpp1
-rw-r--r--platform/ios/export/godot_plugin_config.h3
-rw-r--r--platform/ios/platform_config.h2
-rw-r--r--platform/javascript/export/export_plugin.cpp24
-rw-r--r--platform/javascript/export/export_plugin.h3
-rw-r--r--platform/linuxbsd/display_server_x11.cpp2
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp3
-rw-r--r--platform/macos/display_server_macos.h24
-rw-r--r--platform/macos/display_server_macos.mm416
-rw-r--r--platform/macos/export/export_plugin.cpp13
-rw-r--r--platform/macos/export/export_plugin.h3
-rw-r--r--platform/macos/godot_menu_item.h1
-rw-r--r--platform/uwp/export/export_plugin.cpp23
-rw-r--r--platform/uwp/export/export_plugin.h3
-rw-r--r--platform/uwp/os_uwp.cpp2
-rw-r--r--platform/windows/display_server_windows.cpp41
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--platform/windows/export/export_plugin.cpp15
-rw-r--r--platform/windows/export/export_plugin.h3
37 files changed, 778 insertions, 334 deletions
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 81802298d9..6427346365 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -56,10 +56,10 @@ void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> e
ev->set_ctrl_pressed(control_mem);
}
-void AndroidInputHandler::process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed) {
+void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed) {
static char32_t prev_wc = 0;
- char32_t unicode = p_unicode_char;
- if ((p_unicode_char & 0xfffffc00) == 0xd800) {
+ char32_t unicode = p_unicode;
+ if ((p_unicode & 0xfffffc00) == 0xd800) {
if (prev_wc != 0) {
ERR_PRINT("invalid utf16 surrogate input");
}
@@ -78,39 +78,38 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_scancode, int p
Ref<InputEventKey> ev;
ev.instantiate();
- int val = unicode;
- Key keycode = android_get_keysym(p_keycode);
- Key phy_keycode = android_get_keysym(p_scancode);
- if (keycode == Key::SHIFT) {
- shift_mem = p_pressed;
+ Key physical_keycode = godot_code_from_android_code(p_physical_keycode);
+ Key keycode = physical_keycode;
+ if (p_keycode != 0) {
+ keycode = godot_code_from_unicode(p_keycode);
}
- if (keycode == Key::ALT) {
- alt_mem = p_pressed;
- }
- if (keycode == Key::CTRL) {
- control_mem = p_pressed;
- }
- if (keycode == Key::META) {
- meta_mem = p_pressed;
+
+ switch (physical_keycode) {
+ case Key::SHIFT: {
+ shift_mem = p_pressed;
+ } break;
+ case Key::ALT: {
+ alt_mem = p_pressed;
+ } break;
+ case Key::CTRL: {
+ control_mem = p_pressed;
+ } break;
+ case Key::META: {
+ meta_mem = p_pressed;
+ } break;
+ default:
+ break;
}
ev->set_keycode(keycode);
- ev->set_physical_keycode(phy_keycode);
- ev->set_unicode(val);
+ ev->set_physical_keycode(physical_keycode);
+ ev->set_unicode(unicode);
ev->set_pressed(p_pressed);
_set_key_modifier_state(ev);
- if (val == '\n') {
- ev->set_keycode(Key::ENTER);
- } else if (val == 61448) {
- ev->set_keycode(Key::BACKSPACE);
- ev->set_unicode((char32_t)Key::BACKSPACE);
- } else if (val == 61453) {
- ev->set_keycode(Key::ENTER);
- ev->set_unicode((char32_t)Key::ENTER);
- } else if (p_keycode == 4) {
+ if (p_physical_keycode == AKEYCODE_BACK) {
if (DisplayServerAndroid *dsa = Object::cast_to<DisplayServerAndroid>(DisplayServer::get_singleton())) {
dsa->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST, true);
}
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 1397ca59e4..6dfab7def8 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -83,7 +83,7 @@ public:
void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
void process_double_tap(int event_android_button_mask, Point2 p_pos);
void process_joy_event(JoypadEvent p_event);
- void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed);
+ void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed);
};
#endif // ANDROID_INPUT_HANDLER_H
diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp
index 885e4ff145..d2c5fdfd6c 100644
--- a/platform/android/android_keys_utils.cpp
+++ b/platform/android/android_keys_utils.cpp
@@ -30,12 +30,49 @@
#include "android_keys_utils.h"
-Key android_get_keysym(unsigned int p_code) {
- for (int i = 0; _ak_to_keycode[i].keysym != Key::UNKNOWN; i++) {
- if (_ak_to_keycode[i].keycode == p_code) {
- return _ak_to_keycode[i].keysym;
+Key godot_code_from_android_code(unsigned int p_code) {
+ for (int i = 0; android_godot_code_pairs[i].android_code != AKEYCODE_MAX; i++) {
+ if (android_godot_code_pairs[i].android_code == p_code) {
+ return android_godot_code_pairs[i].godot_code;
}
}
-
return Key::UNKNOWN;
}
+
+Key godot_code_from_unicode(unsigned int p_code) {
+ unsigned int code = p_code;
+ if (code > 0xFF) {
+ return Key::UNKNOWN;
+ }
+ // Known control codes.
+ if (code == '\b') { // 0x08
+ return Key::BACKSPACE;
+ }
+ if (code == '\t') { // 0x09
+ return Key::TAB;
+ }
+ if (code == '\n') { // 0x0A
+ return Key::ENTER;
+ }
+ if (code == 0x1B) {
+ return Key::ESCAPE;
+ }
+ if (code == 0x1F) {
+ return Key::KEY_DELETE;
+ }
+ // Unknown control codes.
+ if (code <= 0x1F || (code >= 0x80 && code <= 0x9F)) {
+ return Key::UNKNOWN;
+ }
+ // Convert to uppercase.
+ if (code >= 'a' && code <= 'z') { // 0x61 - 0x7A
+ code -= ('a' - 'A');
+ }
+ if (code >= u'à' && code <= u'ö') { // 0xE0 - 0xF6
+ code -= (u'à' - u'À'); // 0xE0 - 0xC0
+ }
+ if (code >= u'ø' && code <= u'þ') { // 0xF8 - 0xFF
+ code -= (u'ø' - u'Ø'); // 0xF8 - 0xD8
+ }
+ return Key(code);
+}
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index 24a6589fba..5ec3ee17aa 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -34,129 +34,140 @@
#include <android/input.h>
#include <core/os/keyboard.h>
-struct _WinTranslatePair {
- Key keysym = Key::NONE;
- unsigned int keycode = 0;
+#define AKEYCODE_MAX 0xFFFF
+
+struct AndroidGodotCodePair {
+ unsigned int android_code = 0;
+ Key godot_code = Key::NONE;
};
-static _WinTranslatePair _ak_to_keycode[] = {
- { Key::TAB, AKEYCODE_TAB },
- { Key::ENTER, AKEYCODE_ENTER },
- { Key::SHIFT, AKEYCODE_SHIFT_LEFT },
- { Key::SHIFT, AKEYCODE_SHIFT_RIGHT },
- { Key::ALT, AKEYCODE_ALT_LEFT },
- { Key::ALT, AKEYCODE_ALT_RIGHT },
- { Key::MENU, AKEYCODE_MENU },
- { Key::PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE },
- { Key::ESCAPE, AKEYCODE_BACK },
- { Key::SPACE, AKEYCODE_SPACE },
- { Key::PAGEUP, AKEYCODE_PAGE_UP },
- { Key::PAGEDOWN, AKEYCODE_PAGE_DOWN },
- { Key::HOME, AKEYCODE_HOME }, //(0x24)
- { Key::LEFT, AKEYCODE_DPAD_LEFT },
- { Key::UP, AKEYCODE_DPAD_UP },
- { Key::RIGHT, AKEYCODE_DPAD_RIGHT },
- { Key::DOWN, AKEYCODE_DPAD_DOWN },
- { Key::PERIODCENTERED, AKEYCODE_DPAD_CENTER },
- { Key::BACKSPACE, AKEYCODE_DEL },
- { Key::KEY_0, AKEYCODE_0 },
- { Key::KEY_1, AKEYCODE_1 },
- { Key::KEY_2, AKEYCODE_2 },
- { Key::KEY_3, AKEYCODE_3 },
- { Key::KEY_4, AKEYCODE_4 },
- { Key::KEY_5, AKEYCODE_5 },
- { Key::KEY_6, AKEYCODE_6 },
- { Key::KEY_7, AKEYCODE_7 },
- { Key::KEY_8, AKEYCODE_8 },
- { Key::KEY_9, AKEYCODE_9 },
- { Key::A, AKEYCODE_A },
- { Key::B, AKEYCODE_B },
- { Key::C, AKEYCODE_C },
- { Key::D, AKEYCODE_D },
- { Key::E, AKEYCODE_E },
- { Key::F, AKEYCODE_F },
- { Key::G, AKEYCODE_G },
- { Key::H, AKEYCODE_H },
- { Key::I, AKEYCODE_I },
- { Key::J, AKEYCODE_J },
- { Key::K, AKEYCODE_K },
- { Key::L, AKEYCODE_L },
- { Key::M, AKEYCODE_M },
- { Key::N, AKEYCODE_N },
- { Key::O, AKEYCODE_O },
- { Key::P, AKEYCODE_P },
- { Key::Q, AKEYCODE_Q },
- { Key::R, AKEYCODE_R },
- { Key::S, AKEYCODE_S },
- { Key::T, AKEYCODE_T },
- { Key::U, AKEYCODE_U },
- { Key::V, AKEYCODE_V },
- { Key::W, AKEYCODE_W },
- { Key::X, AKEYCODE_X },
- { Key::Y, AKEYCODE_Y },
- { Key::Z, AKEYCODE_Z },
- { Key::HOMEPAGE, AKEYCODE_EXPLORER },
- { Key::LAUNCH0, AKEYCODE_BUTTON_A },
- { Key::LAUNCH1, AKEYCODE_BUTTON_B },
- { Key::LAUNCH2, AKEYCODE_BUTTON_C },
- { Key::LAUNCH3, AKEYCODE_BUTTON_X },
- { Key::LAUNCH4, AKEYCODE_BUTTON_Y },
- { Key::LAUNCH5, AKEYCODE_BUTTON_Z },
- { Key::LAUNCH6, AKEYCODE_BUTTON_L1 },
- { Key::LAUNCH7, AKEYCODE_BUTTON_R1 },
- { Key::LAUNCH8, AKEYCODE_BUTTON_L2 },
- { Key::LAUNCH9, AKEYCODE_BUTTON_R2 },
- { Key::LAUNCHA, AKEYCODE_BUTTON_THUMBL },
- { Key::LAUNCHB, AKEYCODE_BUTTON_THUMBR },
- { Key::LAUNCHC, AKEYCODE_BUTTON_START },
- { Key::LAUNCHD, AKEYCODE_BUTTON_SELECT },
- { Key::LAUNCHE, AKEYCODE_BUTTON_MODE },
- { Key::VOLUMEMUTE, AKEYCODE_MUTE },
- { Key::VOLUMEDOWN, AKEYCODE_VOLUME_DOWN },
- { Key::VOLUMEUP, AKEYCODE_VOLUME_UP },
- { Key::BACK, AKEYCODE_MEDIA_REWIND },
- { Key::FORWARD, AKEYCODE_MEDIA_FAST_FORWARD },
- { Key::MEDIANEXT, AKEYCODE_MEDIA_NEXT },
- { Key::MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS },
- { Key::MEDIASTOP, AKEYCODE_MEDIA_STOP },
- { Key::PLUS, AKEYCODE_PLUS },
- { Key::EQUAL, AKEYCODE_EQUALS }, // the '+' key
- { Key::COMMA, AKEYCODE_COMMA }, // the ',' key
- { Key::MINUS, AKEYCODE_MINUS }, // the '-' key
- { Key::SLASH, AKEYCODE_SLASH }, // the '/?' key
- { Key::BACKSLASH, AKEYCODE_BACKSLASH },
- { Key::BRACKETLEFT, AKEYCODE_LEFT_BRACKET },
- { Key::BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET },
- { Key::CTRL, AKEYCODE_CTRL_LEFT },
- { Key::CTRL, AKEYCODE_CTRL_RIGHT },
- { Key::UNKNOWN, 0 }
+static AndroidGodotCodePair android_godot_code_pairs[] = {
+ { AKEYCODE_UNKNOWN, Key::UNKNOWN }, // (0) Unknown key code.
+ { AKEYCODE_HOME, Key::HOME }, // (3) Home key.
+ { AKEYCODE_BACK, Key::BACK }, // (4) Back key.
+ { AKEYCODE_0, Key::KEY_0 }, // (7) '0' key.
+ { AKEYCODE_1, Key::KEY_1 }, // (8) '1' key.
+ { AKEYCODE_2, Key::KEY_2 }, // (9) '2' key.
+ { AKEYCODE_3, Key::KEY_3 }, // (10) '3' key.
+ { AKEYCODE_4, Key::KEY_4 }, // (11) '4' key.
+ { AKEYCODE_5, Key::KEY_5 }, // (12) '5' key.
+ { AKEYCODE_6, Key::KEY_6 }, // (13) '6' key.
+ { AKEYCODE_7, Key::KEY_7 }, // (14) '7' key.
+ { AKEYCODE_8, Key::KEY_8 }, // (15) '8' key.
+ { AKEYCODE_9, Key::KEY_9 }, // (16) '9' key.
+ { AKEYCODE_STAR, Key::ASTERISK }, // (17) '*' key.
+ { AKEYCODE_POUND, Key::NUMBERSIGN }, // (18) '#' key.
+ { AKEYCODE_DPAD_UP, Key::UP }, // (19) Directional Pad Up key.
+ { AKEYCODE_DPAD_DOWN, Key::DOWN }, // (20) Directional Pad Down key.
+ { AKEYCODE_DPAD_LEFT, Key::LEFT }, // (21) Directional Pad Left key.
+ { AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key.
+ { AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key.
+ { AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key.
+ { AKEYCODE_CLEAR, Key::CLEAR }, // (28) Clear key.
+ { AKEYCODE_A, Key::A }, // (29) 'A' key.
+ { AKEYCODE_B, Key::B }, // (30) 'B' key.
+ { AKEYCODE_C, Key::C }, // (31) 'C' key.
+ { AKEYCODE_D, Key::D }, // (32) 'D' key.
+ { AKEYCODE_E, Key::E }, // (33) 'E' key.
+ { AKEYCODE_F, Key::F }, // (34) 'F' key.
+ { AKEYCODE_G, Key::G }, // (35) 'G' key.
+ { AKEYCODE_H, Key::H }, // (36) 'H' key.
+ { AKEYCODE_I, Key::I }, // (37) 'I' key.
+ { AKEYCODE_J, Key::J }, // (38) 'J' key.
+ { AKEYCODE_K, Key::K }, // (39) 'K' key.
+ { AKEYCODE_L, Key::L }, // (40) 'L' key.
+ { AKEYCODE_M, Key::M }, // (41) 'M' key.
+ { AKEYCODE_N, Key::N }, // (42) 'N' key.
+ { AKEYCODE_O, Key::O }, // (43) 'O' key.
+ { AKEYCODE_P, Key::P }, // (44) 'P' key.
+ { AKEYCODE_Q, Key::Q }, // (45) 'Q' key.
+ { AKEYCODE_R, Key::R }, // (46) 'R' key.
+ { AKEYCODE_S, Key::S }, // (47) 'S' key.
+ { AKEYCODE_T, Key::T }, // (48) 'T' key.
+ { AKEYCODE_U, Key::U }, // (49) 'U' key.
+ { AKEYCODE_V, Key::V }, // (50) 'V' key.
+ { AKEYCODE_W, Key::W }, // (51) 'W' key.
+ { AKEYCODE_X, Key::X }, // (52) 'X' key.
+ { AKEYCODE_Y, Key::Y }, // (53) 'Y' key.
+ { AKEYCODE_Z, Key::Z }, // (54) 'Z' key.
+ { AKEYCODE_COMMA, Key::COMMA }, // (55) ',’ key.
+ { AKEYCODE_PERIOD, Key::PERIOD }, // (56) '.' key.
+ { AKEYCODE_ALT_LEFT, Key::ALT }, // (57) Left Alt modifier key.
+ { AKEYCODE_ALT_RIGHT, Key::ALT }, // (58) Right Alt modifier key.
+ { AKEYCODE_SHIFT_LEFT, Key::SHIFT }, // (59) Left Shift modifier key.
+ { AKEYCODE_SHIFT_RIGHT, Key::SHIFT }, // (60) Right Shift modifier key.
+ { AKEYCODE_TAB, Key::TAB }, // (61) Tab key.
+ { AKEYCODE_SPACE, Key::SPACE }, // (62) Space key.
+ { AKEYCODE_ENTER, Key::ENTER }, // (66) Enter key.
+ { AKEYCODE_DEL, Key::BACKSPACE }, // (67) Backspace key.
+ { AKEYCODE_GRAVE, Key::QUOTELEFT }, // (68) '`' (backtick) key.
+ { AKEYCODE_MINUS, Key::MINUS }, // (69) '-'.
+ { AKEYCODE_EQUALS, Key::EQUAL }, // (70) '=' key.
+ { AKEYCODE_LEFT_BRACKET, Key::BRACKETLEFT }, // (71) '[' key.
+ { AKEYCODE_RIGHT_BRACKET, Key::BRACKETRIGHT }, // (72) ']' key.
+ { AKEYCODE_BACKSLASH, Key::BACKSLASH }, // (73) '\' key.
+ { AKEYCODE_SEMICOLON, Key::SEMICOLON }, // (74) ';' key.
+ { AKEYCODE_APOSTROPHE, Key::APOSTROPHE }, // (75) ''' (apostrophe) key.
+ { AKEYCODE_SLASH, Key::SLASH }, // (76) '/' key.
+ { AKEYCODE_AT, Key::AT }, // (77) '@' key.
+ { AKEYCODE_PLUS, Key::PLUS }, // (81) '+' key.
+ { AKEYCODE_MENU, Key::MENU }, // (82) Menu key.
+ { AKEYCODE_SEARCH, Key::SEARCH }, // (84) Search key.
+ { AKEYCODE_MEDIA_STOP, Key::MEDIASTOP }, // (86) Stop media key.
+ { AKEYCODE_MEDIA_PREVIOUS, Key::MEDIAPREVIOUS }, // (88) Play Previous media key.
+ { AKEYCODE_PAGE_UP, Key::PAGEUP }, // (92) Page Up key.
+ { AKEYCODE_PAGE_DOWN, Key::PAGEDOWN }, // (93) Page Down key.
+ { AKEYCODE_ESCAPE, Key::ESCAPE }, // (111) Escape key.
+ { AKEYCODE_FORWARD_DEL, Key::KEY_DELETE }, // (112) Forward Delete key.
+ { AKEYCODE_CTRL_LEFT, Key::CTRL }, // (113) Left Control modifier key.
+ { AKEYCODE_CTRL_RIGHT, Key::CTRL }, // (114) Right Control modifier key.
+ { AKEYCODE_CAPS_LOCK, Key::CAPSLOCK }, // (115) Caps Lock key.
+ { AKEYCODE_SCROLL_LOCK, Key::SCROLLLOCK }, // (116) Scroll Lock key.
+ { AKEYCODE_META_LEFT, Key::META }, // (117) Left Meta modifier key.
+ { AKEYCODE_META_RIGHT, Key::META }, // (118) Right Meta modifier key.
+ { AKEYCODE_SYSRQ, Key::PRINT }, // (120) System Request / Print Screen key.
+ { AKEYCODE_BREAK, Key::PAUSE }, // (121) Break / Pause key.
+ { AKEYCODE_INSERT, Key::INSERT }, // (124) Insert key.
+ { AKEYCODE_FORWARD, Key::FORWARD }, // (125) Forward key.
+ { AKEYCODE_MEDIA_PLAY, Key::MEDIAPLAY }, // (126) Play media key.
+ { AKEYCODE_MEDIA_RECORD, Key::MEDIARECORD }, // (130) Record media key.
+ { AKEYCODE_F1, Key::F1 }, // (131) F1 key.
+ { AKEYCODE_F2, Key::F2 }, // (132) F2 key.
+ { AKEYCODE_F3, Key::F3 }, // (133) F3 key.
+ { AKEYCODE_F4, Key::F4 }, // (134) F4 key.
+ { AKEYCODE_F5, Key::F5 }, // (135) F5 key.
+ { AKEYCODE_F6, Key::F6 }, // (136) F6 key.
+ { AKEYCODE_F7, Key::F7 }, // (137) F7 key.
+ { AKEYCODE_F8, Key::F8 }, // (138) F8 key.
+ { AKEYCODE_F9, Key::F9 }, // (139) F9 key.
+ { AKEYCODE_F10, Key::F10 }, // (140) F10 key.
+ { AKEYCODE_F11, Key::F11 }, // (141) F11 key.
+ { AKEYCODE_F12, Key::F12 }, // (142) F12 key.
+ { AKEYCODE_NUM_LOCK, Key::NUMLOCK }, // (143) Num Lock key.
+ { AKEYCODE_NUMPAD_0, Key::KP_0 }, // (144) Numeric keypad '0' key.
+ { AKEYCODE_NUMPAD_1, Key::KP_1 }, // (145) Numeric keypad '1' key.
+ { AKEYCODE_NUMPAD_2, Key::KP_2 }, // (146) Numeric keypad '2' key.
+ { AKEYCODE_NUMPAD_3, Key::KP_3 }, // (147) Numeric keypad '3' key.
+ { AKEYCODE_NUMPAD_4, Key::KP_4 }, // (148) Numeric keypad '4' key.
+ { AKEYCODE_NUMPAD_5, Key::KP_5 }, // (149) Numeric keypad '5' key.
+ { AKEYCODE_NUMPAD_6, Key::KP_6 }, // (150) Numeric keypad '6' key.
+ { AKEYCODE_NUMPAD_7, Key::KP_7 }, // (151) Numeric keypad '7' key.
+ { AKEYCODE_NUMPAD_8, Key::KP_8 }, // (152) Numeric keypad '8' key.
+ { AKEYCODE_NUMPAD_9, Key::KP_9 }, // (153) Numeric keypad '9' key.
+ { AKEYCODE_NUMPAD_DIVIDE, Key::KP_DIVIDE }, // (154) Numeric keypad '/' key (for division).
+ { AKEYCODE_NUMPAD_MULTIPLY, Key::KP_MULTIPLY }, // (155) Numeric keypad '*' key (for multiplication).
+ { AKEYCODE_NUMPAD_SUBTRACT, Key::KP_SUBTRACT }, // (156) Numeric keypad '-' key (for subtraction).
+ { AKEYCODE_NUMPAD_ADD, Key::KP_ADD }, // (157) Numeric keypad '+' key (for addition).
+ { AKEYCODE_NUMPAD_DOT, Key::KP_PERIOD }, // (158) Numeric keypad '.' key (for decimals or digit grouping).
+ { AKEYCODE_NUMPAD_ENTER, Key::KP_ENTER }, // (160) Numeric keypad Enter key.
+ { AKEYCODE_VOLUME_MUTE, Key::VOLUMEMUTE }, // (164) Volume Mute key.
+ { AKEYCODE_YEN, Key::YEN }, // (216) Japanese Yen key.
+ { AKEYCODE_HELP, Key::HELP }, // (259) Help key.
+ { AKEYCODE_REFRESH, Key::REFRESH }, // (285) Refresh key.
+ { AKEYCODE_MAX, Key::UNKNOWN }
};
-/*
-TODO: map these android key:
- AKEYCODE_SOFT_LEFT = 1,
- AKEYCODE_SOFT_RIGHT = 2,
- AKEYCODE_CALL = 5,
- AKEYCODE_ENDCALL = 6,
- AKEYCODE_STAR = 17,
- AKEYCODE_POUND = 18,
- AKEYCODE_POWER = 26,
- AKEYCODE_CAMERA = 27,
- AKEYCODE_CLEAR = 28,
- AKEYCODE_SYM = 63,
- AKEYCODE_ENVELOPE = 65,
- AKEYCODE_GRAVE = 68,
- AKEYCODE_SEMICOLON = 74,
- AKEYCODE_APOSTROPHE = 75,
- AKEYCODE_AT = 77,
- AKEYCODE_NUM = 78,
- AKEYCODE_HEADSETHOOK = 79,
- AKEYCODE_FOCUS = 80, // *Camera* focus
- AKEYCODE_NOTIFICATION = 83,
- AKEYCODE_SEARCH = 84,
- AKEYCODE_PICTSYMBOLS = 94,
- AKEYCODE_SWITCH_CHARSET = 95,
-*/
-Key android_get_keysym(unsigned int p_code);
+Key godot_code_from_android_code(unsigned int p_code);
+Key godot_code_from_unicode(unsigned int p_code);
#endif // ANDROID_KEYS_UTILS_H
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 6f1b4bde40..685b1f01af 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -248,6 +248,7 @@ static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/instal
static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
+#ifndef ANDROID_ENABLED
void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
EditorExportPlatformAndroid *ea = static_cast<EditorExportPlatformAndroid *>(ud);
@@ -277,7 +278,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
}
}
-#ifndef ANDROID_ENABLED
// Check for devices updates
String adb = get_adb_path();
if (FileAccess::exists(adb)) {
@@ -389,7 +389,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
ea->devices_changed.set();
}
}
-#endif
uint64_t sleep = 200;
uint64_t wait = 3000000;
@@ -402,7 +401,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
}
}
-#ifndef ANDROID_ENABLED
if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) {
String adb = get_adb_path();
if (!FileAccess::exists(adb)) {
@@ -413,8 +411,8 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
args.push_back("kill-server");
OS::get_singleton()->execute(adb, args);
}
-#endif
}
+#endif
String EditorExportPlatformAndroid::get_project_name(const String &p_name) const {
String aname;
@@ -2049,7 +2047,7 @@ String EditorExportPlatformAndroid::get_apksigner_path() {
return apksigner_path;
}
-bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
const bool custom_build_enabled = p_preset->get("custom_build/use_custom_build");
@@ -2097,7 +2095,7 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
valid = installed_android_build_template && !r_missing_templates;
}
- // Validate the rest of the configuration.
+ // Validate the rest of the export configuration.
String dk = p_preset->get("keystore/debug");
String dk_user = p_preset->get("keystore/debug_user");
@@ -2173,6 +2171,19 @@ bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_pr
}
}
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
+ String err;
+ bool valid = true;
+ const bool custom_build_enabled = p_preset->get("custom_build/use_custom_build");
+
+ // Validate the project configuration.
bool apk_expansion = p_preset->get("apk_expansion/enable");
if (apk_expansion) {
@@ -3125,10 +3136,14 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
devices_changed.set();
plugins_changed.set();
+#ifndef ANDROID_ENABLED
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
+#endif
}
EditorExportPlatformAndroid::~EditorExportPlatformAndroid() {
+#ifndef ANDROID_ENABLED
quit_request.set();
check_for_changes_thread.wait_to_finish();
+#endif
}
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index 1da3f68f9a..46012bd46c 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -80,10 +80,12 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
Vector<Device> devices;
SafeFlag devices_changed;
Mutex device_lock;
+#ifndef ANDROID_ENABLED
Thread check_for_changes_thread;
SafeFlag quit_request;
static void _check_for_changes_poll_thread(void *ud);
+#endif
String get_project_name(const String &p_name) const;
@@ -186,7 +188,8 @@ public:
static String get_apksigner_path();
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp
index 6b21c18d59..56561cb616 100644
--- a/platform/android/file_access_filesystem_jandroid.cpp
+++ b/platform/android/file_access_filesystem_jandroid.cpp
@@ -46,6 +46,7 @@ jmethodID FileAccessFilesystemJAndroid::_file_seek_end = nullptr;
jmethodID FileAccessFilesystemJAndroid::_file_read = nullptr;
jmethodID FileAccessFilesystemJAndroid::_file_tell = nullptr;
jmethodID FileAccessFilesystemJAndroid::_file_eof = nullptr;
+jmethodID FileAccessFilesystemJAndroid::_file_set_eof = nullptr;
jmethodID FileAccessFilesystemJAndroid::_file_close = nullptr;
jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;
jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;
@@ -162,6 +163,16 @@ bool FileAccessFilesystemJAndroid::eof_reached() const {
}
}
+void FileAccessFilesystemJAndroid::_set_eof(bool eof) {
+ if (_file_set_eof) {
+ ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
+
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+ env->CallVoidMethod(file_access_handler, _file_set_eof, id, eof);
+ }
+}
+
uint8_t FileAccessFilesystemJAndroid::get_8() const {
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
uint8_t byte;
@@ -184,6 +195,7 @@ String FileAccessFilesystemJAndroid::get_line() const {
while (true) {
size_t line_buffer_size = MIN(buffer_size_limit, file_size - get_position());
if (line_buffer_size <= 0) {
+ const_cast<FileAccessFilesystemJAndroid *>(this)->_set_eof(true);
break;
}
@@ -310,6 +322,7 @@ void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
_file_get_size = env->GetMethodID(cls, "fileGetSize", "(I)J");
_file_tell = env->GetMethodID(cls, "fileGetPosition", "(I)J");
_file_eof = env->GetMethodID(cls, "isFileEof", "(I)Z");
+ _file_set_eof = env->GetMethodID(cls, "setFileEof", "(IZ)V");
_file_seek = env->GetMethodID(cls, "fileSeek", "(IJ)V");
_file_seek_end = env->GetMethodID(cls, "fileSeekFromEnd", "(IJ)V");
_file_read = env->GetMethodID(cls, "fileRead", "(ILjava/nio/ByteBuffer;)I");
diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h
index 7deb8de37b..76d7db6e3a 100644
--- a/platform/android/file_access_filesystem_jandroid.h
+++ b/platform/android/file_access_filesystem_jandroid.h
@@ -44,6 +44,7 @@ class FileAccessFilesystemJAndroid : public FileAccess {
static jmethodID _file_seek_end;
static jmethodID _file_tell;
static jmethodID _file_eof;
+ static jmethodID _file_set_eof;
static jmethodID _file_read;
static jmethodID _file_write;
static jmethodID _file_flush;
@@ -56,6 +57,7 @@ class FileAccessFilesystemJAndroid : public FileAccess {
String path_src;
void _close(); ///< close a file
+ void _set_eof(bool eof);
public:
virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index e2ae62d9cf..f855fc6cf6 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -151,7 +151,7 @@ public class GodotLib {
/**
* Forward regular key events from the main thread to the GL thread.
*/
- public static native void key(int p_keycode, int p_scancode, int p_unicode_char, boolean p_pressed);
+ public static native void key(int p_keycode, int p_physical_keycode, int p_unicode, boolean p_pressed);
/**
* Forward game device's key events from the main thread to the GL thread.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index ccfb865b1a..da15b2490c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -96,10 +96,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
GodotLib.joybutton(godotJoyId, button, false);
}
} else {
- final int scanCode = event.getScanCode();
- final int chr = event.getUnicodeChar(0);
- GodotLib.key(keyCode, scanCode, chr, false);
- }
+ // getKeyCode(): The physical key that was pressed.
+ // Godot's keycodes match the ASCII codes, so for single byte unicode characters,
+ // we can use the unmodified unicode character to determine Godot's keycode.
+ final int keycode = event.getUnicodeChar(0);
+ final int physical_keycode = event.getKeyCode();
+ final int unicode = event.getUnicodeChar();
+ GodotLib.key(keycode, physical_keycode, unicode, false);
+ };
return true;
}
@@ -131,9 +135,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
GodotLib.joybutton(godotJoyId, button, true);
}
} else {
- final int scanCode = event.getScanCode();
- final int chr = event.getUnicodeChar(0);
- GodotLib.key(keyCode, scanCode, chr, true);
+ final int keycode = event.getUnicodeChar(0);
+ final int physical_keycode = event.getKeyCode();
+ final int unicode = event.getUnicodeChar();
+ GodotLib.key(keycode, physical_keycode, unicode, true);
}
return true;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 7714703a21..c959b5f28c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -92,11 +92,9 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
- //Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after);
-
for (int i = 0; i < count; ++i) {
- GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false);
+ GodotLib.key(0, KeyEvent.KEYCODE_DEL, 0, true);
+ GodotLib.key(0, KeyEvent.KEYCODE_DEL, 0, false);
if (mHasSelection) {
mHasSelection = false;
@@ -107,8 +105,6 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public void onTextChanged(final CharSequence pCharSequence, final int start, final int before, final int count) {
- //Log.d(TAG, "onTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",before: " + before);
-
final int[] newChars = new int[count];
for (int i = start; i < start + count; ++i) {
newChars[i - start] = pCharSequence.charAt(i);
@@ -119,8 +115,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
// Return keys are handled through action events
continue;
}
- GodotLib.key(0, 0, key, true);
- GodotLib.key(0, 0, key, false);
+ GodotLib.key(key, 0, key, true);
+ GodotLib.key(key, 0, key, false);
}
}
@@ -131,16 +127,16 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
for (int i = 0; i < characters.length(); i++) {
final int ch = characters.codePointAt(i);
- GodotLib.key(0, 0, ch, true);
- GodotLib.key(0, 0, ch, false);
+ GodotLib.key(ch, 0, ch, true);
+ GodotLib.key(ch, 0, ch, false);
}
}
if (pActionID == EditorInfo.IME_ACTION_DONE) {
// Enter key has been pressed
mRenderView.queueOnRenderThread(() -> {
- GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);
+ GodotLib.key(0, KeyEvent.KEYCODE_ENTER, 0, true);
+ GodotLib.key(0, KeyEvent.KEYCODE_ENTER, 0, false);
});
mRenderView.getView().requestFocus();
return true;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt
index 463dabfb23..f23537a29e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/DataAccess.kt
@@ -104,7 +104,6 @@ internal abstract class DataAccess(private val filePath: String) {
protected abstract val fileChannel: FileChannel
internal var endOfFile = false
- private set
fun close() {
try {
@@ -125,9 +124,7 @@ internal abstract class DataAccess(private val filePath: String) {
fun seek(position: Long) {
try {
fileChannel.position(position)
- if (position <= size()) {
- endOfFile = false
- }
+ endOfFile = position >= fileChannel.size()
} catch (e: Exception) {
Log.w(TAG, "Exception when seeking file $filePath.", e)
}
@@ -161,8 +158,7 @@ internal abstract class DataAccess(private val filePath: String) {
fun read(buffer: ByteBuffer): Int {
return try {
val readBytes = fileChannel.read(buffer)
- endOfFile = readBytes == -1
- || (fileChannel.position() >= fileChannel.size() && fileChannel.size() > 0)
+ endOfFile = readBytes == -1 || (fileChannel.position() >= fileChannel.size())
if (readBytes == -1) {
0
} else {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
index 04b6772c45..83da3a24b3 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
@@ -194,6 +194,11 @@ class FileAccessHandler(val context: Context) {
return files[fileId].endOfFile
}
+ fun setFileEof(fileId: Int, eof: Boolean) {
+ val file = files[fileId] ?: return
+ file.endOfFile = eof
+ }
+
fun fileClose(fileId: Int) {
if (hasFileId(fileId)) {
files[fileId].close()
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index f4de4acfad..422c05e5ce 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -376,12 +376,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
}
// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed) {
if (step.get() <= 0) {
return;
}
-
- input_handler->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed);
+ input_handler->process_key_event(p_keycode, p_physical_keycode, p_unicode, p_pressed);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index de16f197b8..3c48ca0459 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -51,7 +51,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEn
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y);
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 080ee5f3ab..6ce7e676a2 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -128,7 +128,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
}
#endif
- bool keep_screen_on = bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+ bool keep_screen_on = bool(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
screen_set_keep_on(keep_screen_on);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 9ca2948542..425a977569 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -413,6 +413,35 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
}
strnew += lines[i].replace("$pbx_locale_build_reference", locale_files);
+ } else if (lines[i].find("$swift_runtime_migration") != -1) {
+ String value = !p_config.use_swift_runtime ? "" : "LastSwiftMigration = 1250;";
+ strnew += lines[i].replace("$swift_runtime_migration", value) + "\n";
+ } else if (lines[i].find("$swift_runtime_build_settings") != -1) {
+ String value = !p_config.use_swift_runtime ? "" : R"(
+ CLANG_ENABLE_MODULES = YES;
+ SWIFT_OBJC_BRIDGING_HEADER = "$binary/dummy.h";
+ SWIFT_VERSION = 5.0;
+ )";
+ value = value.replace("$binary", p_config.binary_name);
+ strnew += lines[i].replace("$swift_runtime_build_settings", value) + "\n";
+ } else if (lines[i].find("$swift_runtime_fileref") != -1) {
+ String value = !p_config.use_swift_runtime ? "" : R"(
+ 90B4C2AA2680BC560039117A /* dummy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dummy.h"; sourceTree = "<group>"; };
+ 90B4C2B52680C7E90039117A /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "dummy.swift"; sourceTree = "<group>"; };
+ )";
+ strnew += lines[i].replace("$swift_runtime_fileref", value) + "\n";
+ } else if (lines[i].find("$swift_runtime_binary_files") != -1) {
+ String value = !p_config.use_swift_runtime ? "" : R"(
+ 90B4C2AA2680BC560039117A /* dummy.h */,
+ 90B4C2B52680C7E90039117A /* dummy.swift */,
+ )";
+ strnew += lines[i].replace("$swift_runtime_binary_files", value) + "\n";
+ } else if (lines[i].find("$swift_runtime_buildfile") != -1) {
+ String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B4C2B52680C7E90039117A /* dummy.swift */; };";
+ strnew += lines[i].replace("$swift_runtime_buildfile", value) + "\n";
+ } else if (lines[i].find("$swift_runtime_build_phase") != -1) {
+ String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,";
+ strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n";
} else {
strnew += lines[i] + "\n";
}
@@ -1298,6 +1327,10 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
plugin_initialization_cpp_code += "\t" + initialization_method;
plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
+
+ if (plugin.use_swift_runtime) {
+ p_config_data.use_swift_runtime = true;
+ }
}
// Updating `Info.plist`
@@ -1479,7 +1512,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
"",
"",
"",
- Vector<String>()
+ Vector<String>(),
+ false
};
Vector<IOSExportAsset> assets;
@@ -1528,8 +1562,6 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
//write
- file = file.replace_first("ios/", "");
-
if (files_to_parse.has(file)) {
_fix_config_file(p_preset, data, config_data, p_debug);
} else if (file.begins_with("libgodot.ios")) {
@@ -1785,7 +1817,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
return OK;
}
-bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
@@ -1810,7 +1842,18 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
valid = dvalid || rvalid;
r_missing_templates = !valid;
- // Validate the rest of the configuration.
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
+ String err;
+ bool valid = true;
+
+ // Validate the project configuration.
String team_id = p_preset->get("application/app_store_team_id");
if (team_id.length() == 0) {
@@ -1841,10 +1884,14 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
EditorExportPlatformIOS::EditorExportPlatformIOS() {
logo = ImageTexture::create_from_image(memnew(Image(_ios_logo)));
plugins_changed.set();
+#ifndef ANDROID_ENABLED
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
+#endif
}
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
+#ifndef ANDROID_ENABLED
quit_request.set();
check_for_changes_thread.wait_to_finish();
+#endif
}
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index 07e30c1d00..abda8e218a 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -57,8 +57,10 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
// Plugins
SafeFlag plugins_changed;
+#ifndef ANDROID_ENABLED
Thread check_for_changes_thread;
SafeFlag quit_request;
+#endif
Mutex plugins_lock;
Vector<PluginConfigIOS> plugins;
@@ -79,6 +81,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
String modules_buildphase;
String modules_buildgrp;
Vector<String> capabilities;
+ bool use_swift_runtime;
};
struct ExportArchitecture {
String name;
@@ -138,6 +141,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
return true;
}
+#ifndef ANDROID_ENABLED
static void _check_for_changes_poll_thread(void *ud) {
EditorExportPlatformIOS *ea = static_cast<EditorExportPlatformIOS *>(ud);
@@ -171,6 +175,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
}
}
}
+#endif
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
@@ -197,7 +202,8 @@ public:
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual void get_platform_features(List<String> *r_features) const override {
r_features->push_back("mobile");
diff --git a/platform/ios/export/godot_plugin_config.cpp b/platform/ios/export/godot_plugin_config.cpp
index 9118b95337..24a95a11a4 100644
--- a/platform/ios/export/godot_plugin_config.cpp
+++ b/platform/ios/export/godot_plugin_config.cpp
@@ -184,6 +184,7 @@ PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file,
String config_base_dir = path.get_base_dir();
plugin_config.name = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_NAME_KEY, String());
+ plugin_config.use_swift_runtime = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_USE_SWIFT_KEY, false);
plugin_config.initialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_INITIALIZE_KEY, String());
plugin_config.deinitialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_DEINITIALIZE_KEY, String());
diff --git a/platform/ios/export/godot_plugin_config.h b/platform/ios/export/godot_plugin_config.h
index 5ca8b05b42..98e8456ed5 100644
--- a/platform/ios/export/godot_plugin_config.h
+++ b/platform/ios/export/godot_plugin_config.h
@@ -39,6 +39,7 @@
The `config` section and fields are required and defined as follow:
- **name**: name of the plugin
- **binary**: path to static `.a` library
+- **use_swift_runtime**: optional boolean field used to determine if Swift runtime is used
The `dependencies` and fields are optional.
- **linked**: dependencies that should only be linked.
@@ -57,6 +58,7 @@ struct PluginConfigIOS {
inline static const char *CONFIG_SECTION = "config";
inline static const char *CONFIG_NAME_KEY = "name";
inline static const char *CONFIG_BINARY_KEY = "binary";
+ inline static const char *CONFIG_USE_SWIFT_KEY = "use_swift_runtime";
inline static const char *CONFIG_INITIALIZE_KEY = "initialization";
inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization";
@@ -93,6 +95,7 @@ struct PluginConfigIOS {
// Required config section
String name;
String binary;
+ bool use_swift_runtime;
String initialization_method;
String deinitialization_method;
diff --git a/platform/ios/platform_config.h b/platform/ios/platform_config.h
index fed77d8932..3af08b0d65 100644
--- a/platform/ios/platform_config.h
+++ b/platform/ios/platform_config.h
@@ -32,8 +32,6 @@
#define OPENGL_INCLUDE_H <ES3/gl.h>
-#define PLATFORM_REFCOUNT
-
#define PTHREAD_RENAME_SELF
#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp
index b99f88d067..0bdee11018 100644
--- a/platform/javascript/export/export_plugin.cpp
+++ b/platform/javascript/export/export_plugin.cpp
@@ -362,7 +362,7 @@ Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const {
return logo;
}
-bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+bool EditorExportPlatformJavaScript::has_valid_export_configuration(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
@@ -396,7 +396,27 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p
valid = dvalid || rvalid;
r_missing_templates = !valid;
- // Validate the rest of the configuration.
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+bool EditorExportPlatformJavaScript::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) 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 = true;
+
+ // Validate the project configuration.
if (p_preset->get("vram_texture_compression/for_mobile")) {
String etc_error = test_etc2();
diff --git a/platform/javascript/export/export_plugin.h b/platform/javascript/export/export_plugin.h
index fbaa3615cb..16bab02d54 100644
--- a/platform/javascript/export/export_plugin.h
+++ b/platform/javascript/export/export_plugin.h
@@ -118,7 +118,8 @@ public:
virtual String get_os_name() const override;
virtual Ref<Texture2D> get_logo() const override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 3409e43ba5..8efbd2e3c5 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -4991,7 +4991,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
#ifdef DBUS_ENABLED
screensaver = memnew(FreeDesktopScreenSaver);
- screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+ screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
#endif
r_error = OK;
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index e306c1054b..197d31dc81 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -368,6 +368,7 @@ Vector<String> OS_LinuxBSD::get_system_fonts() const {
FcPatternDestroy(pattern);
}
FcObjectSetDestroy(object_set);
+ FcConfigDestroy(config);
for (const String &E : font_names) {
ret.push_back(E);
@@ -417,6 +418,8 @@ String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold,
FcPatternDestroy(pattern);
}
FcObjectSetDestroy(object_set);
+ FcConfigDestroy(config);
+
return ret;
#else
ERR_FAIL_V_MSG(String(), "Godot was compiled without fontconfig, system font support is disabled.");
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 65f9a3d4b8..5fda5dd974 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -196,6 +196,9 @@ private:
static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil);
+ bool _has_help_menu() const;
+ NSMenuItem *_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out);
+
public:
NSMenu *get_dock_menu() const;
void menu_callback(id p_sender);
@@ -224,15 +227,15 @@ 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 = 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_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_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;
@@ -250,6 +253,7 @@ public:
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 int global_menu_get_item_indentation_level(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;
@@ -264,6 +268,7 @@ public:
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 void global_menu_set_item_indentation_level(const String &p_menu_root, int p_idx, int p_level) override;
virtual int global_menu_get_item_count(const String &p_menu_root) const override;
@@ -303,6 +308,7 @@ public:
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void screen_set_keep_on(bool p_enable) override;
+ virtual bool screen_is_kept_on() const override;
virtual Vector<int> get_window_list() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 2f5efae69e..8142f44bb0 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -63,7 +63,7 @@
const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) const {
const NSMenu *menu = nullptr;
- if (p_menu_root == "") {
+ if (p_menu_root == "" || p_menu_root.to_lower() == "_main") {
// Main menu.
menu = [NSApp mainMenu];
} else if (p_menu_root.to_lower() == "_dock") {
@@ -84,7 +84,7 @@ const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) cons
NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) {
NSMenu *menu = nullptr;
- if (p_menu_root == "") {
+ if (p_menu_root == "" || p_menu_root.to_lower() == "_main") {
// Main menu.
menu = [NSApp mainMenu];
} else if (p_menu_root.to_lower() == "_dock") {
@@ -714,18 +714,51 @@ String DisplayServerMacOS::get_name() const {
return "macOS";
}
-void 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) {
- _THREAD_SAFE_METHOD_
+bool DisplayServerMacOS::_has_help_menu() const {
+ if ([NSApp helpMenu]) {
+ return true;
+ } else {
+ NSMenu *menu = [NSApp mainMenu];
+ const NSMenuItem *menu_item = [menu itemAtIndex:[menu numberOfItems] - 1];
+ if (menu_item) {
+ String menu_name = String::utf8([[menu_item title] UTF8String]);
+ if (menu_name == "Help" || menu_name == RTR("Help")) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out) {
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
String keycode = KeyMappingMacOS::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];
+ 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 {
- menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+ 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];
+ *r_out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index;
+ return menu_item;
+ }
+ 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) {
+ _THREAD_SAFE_METHOD_
+
+ int out = -1;
+ 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->meta = p_tag;
@@ -735,20 +768,15 @@ void DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const S
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
+ return out;
}
-void 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 Variant &p_tag, Key p_accel, int p_index) {
_THREAD_SAFE_METHOD_
- NSMenu *menu = _get_menu_root(p_menu_root);
- if (menu) {
- String keycode = KeyMappingMacOS::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()]];
- }
+ int out = -1;
+ 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->meta = p_tag;
@@ -758,20 +786,15 @@ void DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, c
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
+ return out;
}
-void 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 Variant &p_tag, Key p_accel, int p_index) {
_THREAD_SAFE_METHOD_
- NSMenu *menu = _get_menu_root(p_menu_root);
- if (menu) {
- String keycode = KeyMappingMacOS::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()]];
- }
+ int out = -1;
+ 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->meta = p_tag;
@@ -790,20 +813,15 @@ void DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, co
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
+ return out;
}
-void 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 Variant &p_tag, Key p_accel, int p_index) {
_THREAD_SAFE_METHOD_
- NSMenu *menu = _get_menu_root(p_menu_root);
- if (menu) {
- String keycode = KeyMappingMacOS::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()]];
- }
+ int out = -1;
+ 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->meta = p_tag;
@@ -822,20 +840,15 @@ void DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_ro
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
+ return out;
}
-void 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 Variant &p_tag, Key p_accel, int p_index) {
_THREAD_SAFE_METHOD_
- NSMenu *menu = _get_menu_root(p_menu_root);
- if (menu) {
- String keycode = KeyMappingMacOS::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()]];
- }
+ int out = -1;
+ 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->meta = p_tag;
@@ -845,20 +858,15 @@ void DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_r
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
+ return out;
}
-void 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 Variant &p_tag, Key p_accel, int p_index) {
_THREAD_SAFE_METHOD_
- NSMenu *menu = _get_menu_root(p_menu_root);
- if (menu) {
- String keycode = KeyMappingMacOS::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()]];
- }
+ int out = -1;
+ 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->meta = p_tag;
@@ -877,20 +885,31 @@ void DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_m
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
+ return out;
}
-void 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 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;
- 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];
+ 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 {
- menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+ 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;
+
GodotMenuItem *obj = [[GodotMenuItem alloc] init];
obj->callback = p_callback;
obj->meta = p_tag;
@@ -900,44 +919,69 @@ void DisplayServerMacOS::global_menu_add_multistate_item(const String &p_menu_ro
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
+ return out;
}
-void DisplayServerMacOS::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) {
+int DisplayServerMacOS::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);
NSMenu *sub_menu = _get_menu_root(p_submenu);
+ int out = -1;
if (menu && sub_menu) {
if (sub_menu == menu) {
ERR_PRINT("Can't set submenu to self!");
- return;
+ return -1;
}
if ([sub_menu supermenu]) {
ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
- return;
+ return -1;
}
NSMenuItem *menu_item;
- if (p_index != -1) {
- menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index];
+ 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 {
- menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""];
+ 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:nil keyEquivalent:@"" atIndex:p_index];
+ out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index;
+
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
+ obj->callback = Callable();
+ obj->checkable_type = CHECKABLE_TYPE_NONE;
+ obj->max_states = 0;
+ obj->state = 0;
+ [menu_item setRepresentedObject:obj];
+
[sub_menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]];
[menu setSubmenu:sub_menu forItem:menu_item];
}
+ return out;
}
-void DisplayServerMacOS::global_menu_add_separator(const String &p_menu_root, int p_index) {
+int DisplayServerMacOS::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) {
- if (p_index != -1) {
- [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index];
+ if (menu == [NSApp mainMenu]) { // Do not add separators into main menu.
+ return -1;
+ }
+ if (p_index < 0) {
+ p_index = [menu numberOfItems];
} else {
- [menu addItem:[NSMenuItem separatorItem]];
+ p_index = CLAMP(p_index, 0, [menu numberOfItems]);
}
+ [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index];
+ return p_index;
}
+ return -1;
}
int DisplayServerMacOS::global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const {
@@ -945,7 +989,11 @@ int DisplayServerMacOS::global_menu_get_item_index_from_text(const String &p_men
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]];
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]] - 1;
+ } else {
+ return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]];
+ }
}
return -1;
@@ -956,12 +1004,16 @@ int DisplayServerMacOS::global_menu_get_item_index_from_tag(const String &p_menu
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- for (NSInteger i = 0; i < [menu numberOfItems]; i++) {
+ for (NSInteger i = (menu == [NSApp mainMenu]) ? 1 : 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;
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ return i - 1;
+ } else {
+ return i;
+ }
}
}
}
@@ -975,6 +1027,11 @@ bool DisplayServerMacOS::global_menu_is_item_checked(const String &p_menu_root,
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, false);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false);
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
return ([menu_item state] == NSControlStateValueOn);
@@ -988,6 +1045,11 @@ bool DisplayServerMacOS::global_menu_is_item_checkable(const String &p_menu_root
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, false);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false);
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
GodotMenuItem *obj = [menu_item representedObject];
@@ -1004,6 +1066,11 @@ bool DisplayServerMacOS::global_menu_is_item_radio_checkable(const String &p_men
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, false);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false);
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
GodotMenuItem *obj = [menu_item representedObject];
@@ -1020,6 +1087,11 @@ Callable DisplayServerMacOS::global_menu_get_item_callback(const String &p_menu_
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];
@@ -1036,6 +1108,11 @@ Variant DisplayServerMacOS::global_menu_get_item_tag(const String &p_menu_root,
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, Variant());
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Variant());
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
GodotMenuItem *obj = [menu_item representedObject];
@@ -1052,6 +1129,11 @@ String DisplayServerMacOS::global_menu_get_item_text(const String &p_menu_root,
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, String());
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String());
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
return String::utf8([[menu_item title] UTF8String]);
@@ -1065,6 +1147,11 @@ String DisplayServerMacOS::global_menu_get_item_submenu(const String &p_menu_roo
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, String());
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String());
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
const NSMenu *sub_menu = [menu_item submenu];
@@ -1085,6 +1172,11 @@ Key DisplayServerMacOS::global_menu_get_item_accelerator(const String &p_menu_ro
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, Key::NONE);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Key::NONE);
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
String ret = String::utf8([[menu_item keyEquivalent] UTF8String]);
@@ -1116,6 +1208,11 @@ bool DisplayServerMacOS::global_menu_is_item_disabled(const String &p_menu_root,
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, false);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false);
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
return ![menu_item isEnabled];
@@ -1129,6 +1226,11 @@ String DisplayServerMacOS::global_menu_get_item_tooltip(const String &p_menu_roo
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, String());
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String());
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
return String::utf8([[menu_item toolTip] UTF8String]);
@@ -1142,6 +1244,11 @@ int DisplayServerMacOS::global_menu_get_item_state(const String &p_menu_root, in
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, 0);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0);
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
GodotMenuItem *obj = [menu_item representedObject];
@@ -1158,6 +1265,11 @@ int DisplayServerMacOS::global_menu_get_item_max_states(const String &p_menu_roo
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, 0);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0);
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
GodotMenuItem *obj = [menu_item representedObject];
@@ -1174,6 +1286,11 @@ Ref<Texture2D> DisplayServerMacOS::global_menu_get_item_icon(const String &p_men
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>());
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Ref<Texture2D>());
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
GodotMenuItem *obj = [menu_item representedObject];
@@ -1187,14 +1304,34 @@ Ref<Texture2D> DisplayServerMacOS::global_menu_get_item_icon(const String &p_men
return Ref<Texture2D>();
}
+int DisplayServerMacOS::global_menu_get_item_indentation_level(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, 0);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0);
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ return [menu_item indentationLevel];
+ }
+ }
+ return 0;
+}
+
void DisplayServerMacOS::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) {
_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;
+ 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) {
if (p_checked) {
@@ -1211,12 +1348,15 @@ void DisplayServerMacOS::global_menu_set_item_checkable(const String &p_menu_roo
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE;
}
}
@@ -1227,12 +1367,15 @@ void DisplayServerMacOS::global_menu_set_item_radio_checkable(const String &p_me
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE;
}
}
@@ -1243,12 +1386,15 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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->callback = p_callback;
}
}
@@ -1259,12 +1405,15 @@ void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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->meta = p_tag;
}
}
@@ -1275,9 +1424,11 @@ void DisplayServerMacOS::global_menu_set_item_text(const String &p_menu_root, in
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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) {
[menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]];
@@ -1299,9 +1450,11 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root,
ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
return;
}
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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) {
[menu setSubmenu:sub_menu forItem:menu_item];
@@ -1314,9 +1467,11 @@ void DisplayServerMacOS::global_menu_set_item_accelerator(const String &p_menu_r
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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) {
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)];
@@ -1331,9 +1486,11 @@ void DisplayServerMacOS::global_menu_set_item_disabled(const String &p_menu_root
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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) {
[menu_item setEnabled:(!p_disabled)];
@@ -1346,9 +1503,11 @@ void DisplayServerMacOS::global_menu_set_item_tooltip(const String &p_menu_root,
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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) {
[menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]];
@@ -1361,15 +1520,16 @@ void DisplayServerMacOS::global_menu_set_item_state(const String &p_menu_root, i
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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];
- if (obj) {
- obj->state = p_state;
- }
+ ERR_FAIL_COND(!obj);
+ obj->state = p_state;
}
}
}
@@ -1379,15 +1539,16 @@ void DisplayServerMacOS::global_menu_set_item_max_states(const String &p_menu_ro
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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];
- if (obj) {
- obj->max_states = p_max_states;
- }
+ ERR_FAIL_COND(!obj);
+ obj->max_states = p_max_states;
}
}
}
@@ -1397,12 +1558,15 @@ void DisplayServerMacOS::global_menu_set_item_icon(const String &p_menu_root, in
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
- return;
+ 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);
if (p_icon.is_valid()) {
obj->img = p_icon->get_image();
obj->img = obj->img->duplicate();
@@ -1419,12 +1583,33 @@ void DisplayServerMacOS::global_menu_set_item_icon(const String &p_menu_root, in
}
}
+void DisplayServerMacOS::global_menu_set_item_indentation_level(const String &p_menu_root, int p_idx, int p_level) {
+ _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) {
+ [menu_item setIndentationLevel:p_level];
+ }
+ }
+}
+
int DisplayServerMacOS::global_menu_get_item_count(const String &p_menu_root) const {
_THREAD_SAFE_METHOD_
const NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- return [menu numberOfItems];
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ return [menu numberOfItems] - 1;
+ } else {
+ return [menu numberOfItems];
+ }
} else {
return 0;
}
@@ -1435,9 +1620,11 @@ void DisplayServerMacOS::global_menu_remove_item(const String &p_menu_root, int
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
- if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not delete Apple menu.
- return;
+ ERR_FAIL_COND(p_idx < 0);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
}
+ ERR_FAIL_COND(p_idx >= [menu numberOfItems]);
[menu removeItemAtIndex:p_idx];
}
}
@@ -1453,6 +1640,9 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) {
NSMenuItem *menu_item = [menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
[menu setSubmenu:apple_menu forItem:menu_item];
}
+ if (submenu.has(p_menu_root)) {
+ submenu.erase(p_menu_root);
+ }
}
}
@@ -1892,6 +2082,10 @@ float DisplayServerMacOS::screen_get_refresh_rate(int p_screen) const {
return SCREEN_REFRESH_RATE_FALLBACK;
}
+bool DisplayServerMacOS::screen_is_kept_on() const {
+ return (screen_keep_on_assertion);
+}
+
void DisplayServerMacOS::screen_set_keep_on(bool p_enable) {
if (screen_keep_on_assertion) {
IOPMAssertionRelease(screen_keep_on_assertion);
@@ -3218,7 +3412,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
[apple_menu addItem:[NSMenuItem separatorItem]];
- title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
+ title = [NSString stringWithFormat:NSLocalizedString(@"\t\tQuit %@", nil), nsappname];
[apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
// Add items to the menu bar.
@@ -3282,7 +3476,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
}
#endif
- screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+ screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
}
DisplayServerMacOS::~DisplayServerMacOS() {
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index bcc2636c07..edce9c0380 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1550,7 +1550,7 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri
da->list_dir_end();
}
-bool EditorExportPlatformMacOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
@@ -1580,6 +1580,17 @@ bool EditorExportPlatformMacOS::can_export(const Ref<EditorExportPreset> &p_pres
valid = dvalid || rvalid;
r_missing_templates = !valid;
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
+ String err;
+ bool valid = true;
+
String identifier = p_preset->get("application/bundle_identifier");
String pn_err;
if (!is_package_name_valid(identifier, &pn_err)) {
diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h
index 21bc380d55..4603c61a28 100644
--- a/platform/macos/export/export_plugin.h
+++ b/platform/macos/export/export_plugin.h
@@ -119,7 +119,8 @@ public:
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual void get_platform_features(List<String> *r_features) const override {
r_features->push_back("pc");
diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h
index 2c12897f10..e0b9f41632 100644
--- a/platform/macos/godot_menu_item.h
+++ b/platform/macos/godot_menu_item.h
@@ -46,7 +46,6 @@ enum GlobalMenuCheckType {
@public
Callable callback;
Variant meta;
- int id;
GlobalMenuCheckType checkable_type;
int max_states;
int state;
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 070c46242f..4e4afb9704 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -121,7 +121,7 @@ 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 {
+bool EditorExportPlatformUWP::has_valid_export_configuration(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
@@ -163,7 +163,26 @@ bool EditorExportPlatformUWP::can_export(const Ref<EditorExportPreset> &p_preset
valid = dvalid || rvalid;
r_missing_templates = !valid;
- // Validate the rest of the configuration.
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+bool EditorExportPlatformUWP::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) 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 = true;
+
+ // Validate the project configuration.
if (!_valid_resource_name(p_preset->get("package/short_name"))) {
valid = false;
diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h
index 4a3c5db377..71d0479543 100644
--- a/platform/uwp/export/export_plugin.h
+++ b/platform/uwp/export/export_plugin.h
@@ -429,7 +429,8 @@ public:
virtual void get_export_options(List<ExportOption> *r_options) override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 7bc4c6047c..40f93bb18b 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -275,7 +275,7 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
display_request->RequestActive();
}
- set_keep_screen_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+ set_keep_screen_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
return OK;
}
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index f6baab1644..8c8dbef8a4 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -531,10 +531,43 @@ DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(in
}
void DisplayServerWindows::screen_set_keep_on(bool p_enable) {
+ if (keep_screen_on == p_enable) {
+ return;
+ }
+
+ if (p_enable) {
+ const String reason = "Godot Engine running with display/window/energy_saving/keep_screen_on = true";
+ Char16String reason_utf16 = reason.utf16();
+
+ REASON_CONTEXT context;
+ context.Version = POWER_REQUEST_CONTEXT_VERSION;
+ context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
+ context.Reason.SimpleReasonString = (LPWSTR)(reason_utf16.ptrw());
+ power_request = PowerCreateRequest(&context);
+ if (power_request == INVALID_HANDLE_VALUE) {
+ print_error("Failed to enable screen_keep_on.");
+ return;
+ }
+ if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired) == 0) {
+ print_error("Failed to request system sleep override.");
+ return;
+ }
+ if (PowerSetRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired) == 0) {
+ print_error("Failed to request display timeout override.");
+ return;
+ }
+ } else {
+ PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestSystemRequired);
+ PowerClearRequest(power_request, POWER_REQUEST_TYPE::PowerRequestDisplayRequired);
+ CloseHandle(power_request);
+ power_request = nullptr;
+ }
+
+ keep_screen_on = p_enable;
}
bool DisplayServerWindows::screen_is_kept_on() const {
- return false;
+ return keep_screen_on;
}
Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {
@@ -3619,6 +3652,9 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
// Init TTS
tts = memnew(TTS_Windows);
+ // Enforce default keep screen on value.
+ screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
+
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
if (wintab_lib) {
@@ -3822,6 +3858,9 @@ DisplayServerWindows::~DisplayServerWindows() {
SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
}
+ // Close power request handle.
+ screen_set_keep_on(false);
+
#ifdef GLES3_ENABLED
// destroy windows .. NYI?
// FIXME wglDeleteContext is never called
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index ddbf674c64..db9b589304 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -331,6 +331,8 @@ class DisplayServerWindows : public DisplayServer {
HINSTANCE hInstance; // Holds The Instance Of The Application
String rendering_driver;
bool app_focused = false;
+ bool keep_screen_on = false;
+ HANDLE power_request;
TTS_Windows *tts = nullptr;
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index febef5ad12..9d3ec31f73 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -412,15 +412,26 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
return OK;
}
-bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err = "";
- bool valid = EditorExportPlatformPC::can_export(p_preset, err, r_missing_templates);
+ bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates);
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
if (p_preset->get("application/modify_resources") && rcedit_path.is_empty()) {
err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > Rcedit) to change the icon or app information data.") + "\n";
}
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
+ String err = "";
+ bool valid = true;
+
String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon"));
if (!icon_path.is_empty() && !FileAccess::exists(icon_path)) {
err += TTR("Invalid icon path:") + " " + icon_path + "\n";
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index b9e59829a0..3ea8ff3dc9 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -49,7 +49,8 @@ public:
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 HashMap<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 bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
};