summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/export/export.cpp65
-rw-r--r--platform/android/java/app/AndroidManifest.xml3
-rw-r--r--platform/android/java/app/config.gradle7
-rw-r--r--platform/android/java/build.gradle2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java2
-rw-r--r--platform/javascript/display_server_javascript.cpp4
-rw-r--r--platform/javascript/dom_keys.inc587
-rw-r--r--platform/javascript/javascript_main.cpp12
-rw-r--r--platform/linuxbsd/display_server_x11.cpp119
-rw-r--r--platform/linuxbsd/display_server_x11.h6
-rw-r--r--platform/osx/display_server_osx.h6
-rw-r--r--platform/osx/display_server_osx.mm152
-rw-r--r--platform/osx/export/export.cpp119
-rw-r--r--platform/osx/joypad_osx.cpp9
-rw-r--r--platform/uwp/export/export.cpp18
-rw-r--r--platform/uwp/os_uwp.cpp21
-rw-r--r--platform/windows/display_server_windows.cpp137
-rw-r--r--platform/windows/display_server_windows.h6
-rw-r--r--platform/windows/joypad_windows.cpp37
-rw-r--r--platform/windows/os_windows.cpp73
-rw-r--r--platform/windows/os_windows.h4
21 files changed, 761 insertions, 628 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 1bd198ccc0..a663a847c2 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -772,6 +772,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
bool screen_support_xlarge = p_preset->get("screen/support_xlarge");
int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ bool focus_awareness = p_preset->get("xr_features/focus_awareness");
String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
@@ -928,6 +929,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
+ if (tname == "meta-data" && attrname == "value" && value == "oculus_focus_aware_value") {
+ // Update the focus awareness meta-data value
+ string_table.write[attr_value] = xr_mode_index == /* XRMode.OVR */ 1 && focus_awareness ? "true" : "false";
+ }
+
if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.empty()) {
// Update the meta-data 'android:value' attribute with the list of enabled plugins.
string_table.write[attr_value] = plugins_names;
@@ -1449,6 +1455,7 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "xr_features/focus_awareness"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
@@ -1729,23 +1736,32 @@ public:
// Look for export templates (first official, and if defined custom templates).
if (!bool(p_preset->get("custom_template/use_custom_build"))) {
- bool dvalid = exists_export_template("android_debug.apk", &err);
- bool rvalid = exists_export_template("android_release.apk", &err);
+ String template_err;
+ bool dvalid = false;
+ bool rvalid = false;
if (p_preset->get("custom_template/debug") != "") {
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
if (!dvalid) {
- err += TTR("Custom debug template not found.") + "\n";
+ template_err += TTR("Custom debug template not found.") + "\n";
}
+ } else {
+ dvalid = exists_export_template("android_debug.apk", &template_err);
}
+
if (p_preset->get("custom_template/release") != "") {
rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
if (!rvalid) {
- err += TTR("Custom release template not found.") + "\n";
+ template_err += TTR("Custom release template not found.") + "\n";
}
+ } else {
+ rvalid = exists_export_template("android_release.apk", &template_err);
}
valid = dvalid || rvalid;
+ if (!valid) {
+ err += template_err;
+ }
} else {
valid = exists_export_template("android_source.zip", &err);
}
@@ -1777,6 +1793,13 @@ public:
}
}
+ String rk = p_preset->get("keystore/release");
+
+ if (!rk.empty() && !FileAccess::exists(rk)) {
+ valid = false;
+ err += TTR("Release keystore incorrectly configured in the export preset.") + "\n";
+ }
+
if (bool(p_preset->get("custom_template/use_custom_build"))) {
String sdk_path = EditorSettings::get_singleton()->get("export/android/custom_build_sdk_path");
if (sdk_path == "") {
@@ -1823,6 +1846,40 @@ public:
err += etc_error;
}
+ // Ensure that `Use Custom Build` is enabled if a plugin is selected.
+ String enabled_plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
+ bool custom_build_enabled = p_preset->get("custom_template/use_custom_build");
+ if (!enabled_plugins_names.empty() && !custom_build_enabled) {
+ valid = false;
+ err += TTR("\"Use Custom Build\" must be enabled to use the plugins.");
+ err += "\n";
+ }
+
+ // Validate the Xr features are properly populated
+ int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ int degrees_of_freedom = p_preset->get("xr_features/degrees_of_freedom");
+ int hand_tracking = p_preset->get("xr_features/hand_tracking");
+ bool focus_awareness = p_preset->get("xr_features/focus_awareness");
+ if (xr_mode_index != /* XRMode.OVR*/ 1) {
+ if (degrees_of_freedom > 0) {
+ valid = false;
+ err += TTR("\"Degrees Of Freedom\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+ err += "\n";
+ }
+
+ if (hand_tracking > 0) {
+ valid = false;
+ err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+ err += "\n";
+ }
+
+ if (focus_awareness) {
+ valid = false;
+ err += TTR("\"Focus Awareness\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+ err += "\n";
+ }
+ }
+
r_error = err;
return valid;
}
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index f5b1d29f22..dbf1dc0f3c 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -45,6 +45,9 @@
android:resizeableActivity="false"
tools:ignore="UnusedAttribute" >
+ <!-- Focus awareness metadata populated at export time if the user enables it in the 'Xr Features' section. -->
+ <meta-data android:name="com.oculus.vr.focusaware" android:value="oculus_focus_aware_value" />
+
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 5251bc3066..acfdef531e 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -72,17 +72,12 @@ ext.getGodotPluginsRemoteBinaries = { ->
/**
* Parse the project properties for the 'plugins_local_binaries' property and return
* their binaries for inclusion in the build dependencies.
- *
- * Returns the prebuilt plugins if the 'plugins_local_binaries' property is unavailable.
*/
ext.getGodotPluginsLocalBinaries = { ->
- // Set the prebuilt plugins as default. If custom build is enabled,
- // the 'plugins_local_binaries' will be defined so we can use it instead.
- Set<String> binDeps = ["libs/plugins/GodotPayment.release.aar"]
+ Set<String> binDeps = []
// Retrieve the list of local plugins binaries.
if (project.hasProperty("plugins_local_binaries")) {
- binDeps.clear()
String pluginsList = project.property("plugins_local_binaries")
if (pluginsList != null && !pluginsList.trim().isEmpty()) {
for (String plugin : pluginsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 01a3607b20..80c6be0fae 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -193,4 +193,6 @@ task cleanGodotTemplates(type: Delete) {
delete("$binDir/android_source.zip")
delete("$binDir/godot-lib.debug.aar")
delete("$binDir/godot-lib.release.aar")
+
+ finalizedBy getTasksByName("clean", true)
}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java
index 9e55ab5027..6447a9ec3f 100644
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java
@@ -214,7 +214,7 @@ public class GodotPayment extends GodotPlugin implements PurchasesUpdatedListene
@NonNull
@Override
public List<String> getPluginMethods() {
- return Arrays.asList("startConnection", "endConnection", "purchase", "querySkuDetails", "isReady", "queryPurchases", "acknowledgePurchase");
+ return Arrays.asList("startConnection", "endConnection", "purchase", "querySkuDetails", "isReady", "queryPurchases", "acknowledgePurchase", "consumePurchase");
}
@NonNull
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index b95674efc3..0312efb377 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -143,8 +143,8 @@ static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscrip
ev.instance();
ev->set_echo(emscripten_event->repeat);
dom2godot_mod(emscripten_event, ev);
- ev->set_keycode(dom2godot_keycode(emscripten_event->keyCode));
- ev->set_physical_keycode(dom2godot_keycode(emscripten_event->keyCode));
+ ev->set_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false));
+ ev->set_physical_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, true));
String unicode = String::utf8(emscripten_event->key);
// Check if empty or multi-character (e.g. `CapsLock`).
diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc
index 882e943471..e3f2ce42b4 100644
--- a/platform/javascript/dom_keys.inc
+++ b/platform/javascript/dom_keys.inc
@@ -30,400 +30,203 @@
#include "core/os/keyboard.h"
-// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value
-#define DOM_VK_CANCEL 0x03
-#define DOM_VK_HELP 0x06
-#define DOM_VK_BACK_SPACE 0x08
-#define DOM_VK_TAB 0x09
-#define DOM_VK_CLEAR 0x0C
-#define DOM_VK_RETURN 0x0D
-#define DOM_VK_ENTER 0x0E // "Reserved, but not used."
-#define DOM_VK_SHIFT 0x10
-#define DOM_VK_CONTROL 0x11
-#define DOM_VK_ALT 0x12
-#define DOM_VK_PAUSE 0x13
-#define DOM_VK_CAPS_LOCK 0x14
-#define DOM_VK_KANA 0x15
-#define DOM_VK_HANGUL 0x15
-#define DOM_VK_EISU 0x16
-#define DOM_VK_JUNJA 0x17
-#define DOM_VK_FINAL 0x18
-#define DOM_VK_HANJA 0x19
-#define DOM_VK_KANJI 0x19
-#define DOM_VK_ESCAPE 0x1B
-#define DOM_VK_CONVERT 0x1C
-#define DOM_VK_NONCONVERT 0x1D
-#define DOM_VK_ACCEPT 0x1E
-#define DOM_VK_MODECHANGE 0x1F
-#define DOM_VK_SPACE 0x20
-#define DOM_VK_PAGE_UP 0x21
-#define DOM_VK_PAGE_DOWN 0x22
-#define DOM_VK_END 0x23
-#define DOM_VK_HOME 0x24
-#define DOM_VK_LEFT 0x25
-#define DOM_VK_UP 0x26
-#define DOM_VK_RIGHT 0x27
-#define DOM_VK_DOWN 0x28
-#define DOM_VK_SELECT 0x29
-#define DOM_VK_PRINT 0x2A
-#define DOM_VK_EXECUTE 0x2B
-#define DOM_VK_PRINTSCREEN 0x2C
-#define DOM_VK_INSERT 0x2D
-#define DOM_VK_DELETE 0x2E
-#define DOM_VK_0 0x30
-#define DOM_VK_1 0x31
-#define DOM_VK_2 0x32
-#define DOM_VK_3 0x33
-#define DOM_VK_4 0x34
-#define DOM_VK_5 0x35
-#define DOM_VK_6 0x36
-#define DOM_VK_7 0x37
-#define DOM_VK_8 0x38
-#define DOM_VK_9 0x39
-#define DOM_VK_COLON 0x3A
-#define DOM_VK_SEMICOLON 0x3B
-#define DOM_VK_LESS_THAN 0x3C
-#define DOM_VK_EQUALS 0x3D
-#define DOM_VK_GREATER_THAN 0x3E
-#define DOM_VK_QUESTION_MARK 0x3F
-#define DOM_VK_AT 0x40
-#define DOM_VK_A 0x41
-#define DOM_VK_B 0x42
-#define DOM_VK_C 0x43
-#define DOM_VK_D 0x44
-#define DOM_VK_E 0x45
-#define DOM_VK_F 0x46
-#define DOM_VK_G 0x47
-#define DOM_VK_H 0x48
-#define DOM_VK_I 0x49
-#define DOM_VK_J 0x4A
-#define DOM_VK_K 0x4B
-#define DOM_VK_L 0x4C
-#define DOM_VK_M 0x4D
-#define DOM_VK_N 0x4E
-#define DOM_VK_O 0x4F
-#define DOM_VK_P 0x50
-#define DOM_VK_Q 0x51
-#define DOM_VK_R 0x52
-#define DOM_VK_S 0x53
-#define DOM_VK_T 0x54
-#define DOM_VK_U 0x55
-#define DOM_VK_V 0x56
-#define DOM_VK_W 0x57
-#define DOM_VK_X 0x58
-#define DOM_VK_Y 0x59
-#define DOM_VK_Z 0x5A
-#define DOM_VK_WIN 0x5B
-#define DOM_VK_CONTEXT_MENU 0x5D
-#define DOM_VK_SLEEP 0x5F
-#define DOM_VK_NUMPAD0 0x60
-#define DOM_VK_NUMPAD1 0x61
-#define DOM_VK_NUMPAD2 0x62
-#define DOM_VK_NUMPAD3 0x63
-#define DOM_VK_NUMPAD4 0x64
-#define DOM_VK_NUMPAD5 0x65
-#define DOM_VK_NUMPAD6 0x66
-#define DOM_VK_NUMPAD7 0x67
-#define DOM_VK_NUMPAD8 0x68
-#define DOM_VK_NUMPAD9 0x69
-#define DOM_VK_MULTIPLY 0x6A
-#define DOM_VK_ADD 0x6B
-#define DOM_VK_SEPARATOR 0x6C
-#define DOM_VK_SUBTRACT 0x6D
-#define DOM_VK_DECIMAL 0x6E
-#define DOM_VK_DIVIDE 0x6F
-#define DOM_VK_F1 0x70
-#define DOM_VK_F2 0x71
-#define DOM_VK_F3 0x72
-#define DOM_VK_F4 0x73
-#define DOM_VK_F5 0x74
-#define DOM_VK_F6 0x75
-#define DOM_VK_F7 0x76
-#define DOM_VK_F8 0x77
-#define DOM_VK_F9 0x78
-#define DOM_VK_F10 0x79
-#define DOM_VK_F11 0x7A
-#define DOM_VK_F12 0x7B
-#define DOM_VK_F13 0x7C
-#define DOM_VK_F14 0x7D
-#define DOM_VK_F15 0x7E
-#define DOM_VK_F16 0x7F
-#define DOM_VK_F17 0x80
-#define DOM_VK_F18 0x81
-#define DOM_VK_F19 0x82
-#define DOM_VK_F20 0x83
-#define DOM_VK_F21 0x84
-#define DOM_VK_F22 0x85
-#define DOM_VK_F23 0x86
-#define DOM_VK_F24 0x87
-#define DOM_VK_NUM_LOCK 0x90
-#define DOM_VK_SCROLL_LOCK 0x91
-#define DOM_VK_WIN_OEM_FJ_JISHO 0x92
-#define DOM_VK_WIN_OEM_FJ_MASSHOU 0x93
-#define DOM_VK_WIN_OEM_FJ_TOUROKU 0x94
-#define DOM_VK_WIN_OEM_FJ_LOYA 0x95
-#define DOM_VK_WIN_OEM_FJ_ROYA 0x96
-#define DOM_VK_CIRCUMFLEX 0xA0
-#define DOM_VK_EXCLAMATION 0xA1
-#define DOM_VK_DOUBLE_QUOTE 0xA2
-#define DOM_VK_HASH 0xA3
-#define DOM_VK_DOLLAR 0xA4
-#define DOM_VK_PERCENT 0xA5
-#define DOM_VK_AMPERSAND 0xA6
-#define DOM_VK_UNDERSCORE 0xA7
-#define DOM_VK_OPEN_PAREN 0xA8
-#define DOM_VK_CLOSE_PAREN 0xA9
-#define DOM_VK_ASTERISK 0xAA
-#define DOM_VK_PLUS 0xAB
-#define DOM_VK_PIPE 0xAC
-#define DOM_VK_HYPHEN_MINUS 0xAD
-#define DOM_VK_OPEN_CURLY_BRACKET 0xAE
-#define DOM_VK_CLOSE_CURLY_BRACKET 0xAF
-#define DOM_VK_TILDE 0xB0
-#define DOM_VK_VOLUME_MUTE 0xB5
-#define DOM_VK_VOLUME_DOWN 0xB6
-#define DOM_VK_VOLUME_UP 0xB7
-#define DOM_VK_COMMA 0xBC
-#define DOM_VK_PERIOD 0xBE
-#define DOM_VK_SLASH 0xBF
-#define DOM_VK_BACK_QUOTE 0xC0
-#define DOM_VK_OPEN_BRACKET 0xDB
-#define DOM_VK_BACK_SLASH 0xDC
-#define DOM_VK_CLOSE_BRACKET 0xDD
-#define DOM_VK_QUOTE 0xDE
-#define DOM_VK_META 0xE0
-#define DOM_VK_ALTGR 0xE1
-#define DOM_VK_WIN_ICO_HELP 0xE3
-#define DOM_VK_WIN_ICO_00 0xE4
-#define DOM_VK_WIN_ICO_CLEAR 0xE6
-#define DOM_VK_WIN_OEM_RESET 0xE9
-#define DOM_VK_WIN_OEM_JUMP 0xEA
-#define DOM_VK_WIN_OEM_PA1 0xEB
-#define DOM_VK_WIN_OEM_PA2 0xEC
-#define DOM_VK_WIN_OEM_PA3 0xED
-#define DOM_VK_WIN_OEM_WSCTRL 0xEE
-#define DOM_VK_WIN_OEM_CUSEL 0xEF
-#define DOM_VK_WIN_OEM_ATTN 0xF0
-#define DOM_VK_WIN_OEM_FINISH 0xF1
-#define DOM_VK_WIN_OEM_COPY 0xF2
-#define DOM_VK_WIN_OEM_AUTO 0xF3
-#define DOM_VK_WIN_OEM_ENLW 0xF4
-#define DOM_VK_WIN_OEM_BACKTAB 0xF5
-#define DOM_VK_ATTN 0xF6
-#define DOM_VK_CRSEL 0xF7
-#define DOM_VK_EXSEL 0xF8
-#define DOM_VK_EREOF 0xF9
-#define DOM_VK_PLAY 0xFA
-#define DOM_VK_ZOOM 0xFB
-#define DOM_VK_PA1 0xFD
-#define DOM_VK_WIN_OEM_CLEAR 0xFE
-
-int dom2godot_keycode(int dom_keycode) {
- if (DOM_VK_0 <= dom_keycode && dom_keycode <= DOM_VK_Z) {
- // ASCII intersection
- return dom_keycode;
- }
-
- if (DOM_VK_NUMPAD0 <= dom_keycode && dom_keycode <= DOM_VK_NUMPAD9) {
- // Numpad numbers
- return KEY_KP_0 + (dom_keycode - DOM_VK_NUMPAD0);
+// See https://w3c.github.io/uievents-code/#code-value-tables
+int dom_code2godot_scancode(EM_UTF8 const p_code[32], EM_UTF8 const p_key[32], bool p_physical) {
+#define DOM2GODOT(p_str, p_godot_code) \
+ if (memcmp((const void *)p_str, (void *)p_code, strlen(p_str) + 1) == 0) { \
+ return KEY_##p_godot_code; \
}
- if (DOM_VK_F1 <= dom_keycode && dom_keycode <= DOM_VK_F16) {
- // F1-F16
- return KEY_F1 + (dom_keycode - DOM_VK_F1);
+ // Numpad section.
+ DOM2GODOT("NumLock", NUMLOCK);
+ DOM2GODOT("Numpad0", KP_0);
+ DOM2GODOT("Numpad1", KP_1);
+ DOM2GODOT("Numpad2", KP_2);
+ DOM2GODOT("Numpad3", KP_3);
+ DOM2GODOT("Numpad4", KP_4);
+ DOM2GODOT("Numpad5", KP_5);
+ DOM2GODOT("Numpad6", KP_6);
+ DOM2GODOT("Numpad7", KP_7);
+ DOM2GODOT("Numpad8", KP_8);
+ DOM2GODOT("Numpad9", KP_9);
+ DOM2GODOT("NumpadAdd", KP_ADD);
+ DOM2GODOT("NumpadBackspace", BACKSPACE);
+ DOM2GODOT("NumpadClear", CLEAR);
+ DOM2GODOT("NumpadClearEntry", CLEAR);
+ //DOM2GODOT("NumpadComma", UNKNOWN);
+ DOM2GODOT("NumpadDecimal", KP_PERIOD);
+ DOM2GODOT("NumpadDivide", KP_DIVIDE);
+ DOM2GODOT("NumpadEnter", KP_ENTER);
+ DOM2GODOT("NumpadEqual", EQUAL);
+ //DOM2GODOT("NumpadHash", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryAdd", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryClear", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryRecall", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryStore", UNKNOWN);
+ //DOM2GODOT("NumpadMemorySubtract", UNKNOWN);
+ DOM2GODOT("NumpadMultiply", KP_MULTIPLY);
+ DOM2GODOT("NumpadParenLeft", PARENLEFT);
+ DOM2GODOT("NumpadParenRight", PARENRIGHT);
+ DOM2GODOT("NumpadStar", KP_MULTIPLY); // or ASTERISK ?
+ DOM2GODOT("NumpadSubtract", KP_SUBTRACT);
+
+ // Printable ASCII.
+ if (!p_physical) {
+ uint8_t b0 = (uint8_t)p_key[0];
+ uint8_t b1 = (uint8_t)p_key[1];
+ uint8_t b2 = (uint8_t)p_key[2];
+ if (b1 == 0 && b0 > 0x1F && b0 < 0x7F) { // ASCII.
+ if (b0 > 0x60 && b0 < 0x7B) { // Lowercase ASCII.
+ b0 -= 32;
+ }
+ return b0;
+ }
+
+#define _U_2BYTES_MASK 0xE0
+#define _U_2BYTES 0xC0
+ // Latin-1 codes.
+ if (b2 == 0 && (b0 & _U_2BYTES_MASK) == _U_2BYTES) { // 2-bytes utf8, only known latin.
+ uint32_t key = ((b0 & ~_U_2BYTES_MASK) << 6) | (b1 & 0x3F);
+ if (key >= 0xA0 && key <= 0xDF) {
+ return key;
+ }
+ if (key >= 0xE0 && key <= 0xFF) { // Lowercase known latin.
+ key -= 0x20;
+ return key;
+ }
+ }
+#undef _U_2BYTES_MASK
+#undef _U_2BYTES
}
- switch (dom_keycode) {
- //case DOM_VK_CANCEL: return KEY_UNKNOWN;
- case DOM_VK_HELP:
- return KEY_HELP;
- case DOM_VK_BACK_SPACE:
- return KEY_BACKSPACE;
- case DOM_VK_TAB:
- return KEY_TAB;
-
- case DOM_VK_CLEAR:
- case DOM_VK_WIN_OEM_CLEAR: // OEM duplicate
- return KEY_CLEAR;
-
- case DOM_VK_RETURN:
- case DOM_VK_ENTER: // unused according to MDN
- return KEY_ENTER;
-
- case DOM_VK_SHIFT:
- return KEY_SHIFT;
- case DOM_VK_CONTROL:
- return KEY_CONTROL;
-
- case DOM_VK_ALT:
- case DOM_VK_ALTGR:
- return KEY_ALT;
-
- case DOM_VK_PAUSE:
- return KEY_PAUSE;
- case DOM_VK_CAPS_LOCK:
- return KEY_CAPSLOCK;
-
- /*
- case DOM_VK_KANA: return KEY_UNKNOWN;
- case DOM_VK_HANGUL: return KEY_UNKNOWN;
- case DOM_VK_EISU: return KEY_UNKNOWN;
- case DOM_VK_JUNJA: return KEY_UNKNOWN;
- case DOM_VK_FINAL: return KEY_UNKNOWN;
- case DOM_VK_HANJA: return KEY_UNKNOWN;
- case DOM_VK_KANJI: return KEY_UNKNOWN;
- */
-
- case DOM_VK_ESCAPE:
- return KEY_ESCAPE;
- /*
- case DOM_VK_CONVERT: return KEY_UNKNOWN;
- case DOM_VK_NONCONVERT: return KEY_UNKNOWN;
- case DOM_VK_ACCEPT: return KEY_UNKNOWN;
- case DOM_VK_MODECHANGE: return KEY_UNKNOWN;
- */
-
- case DOM_VK_SPACE:
- return KEY_SPACE;
- case DOM_VK_PAGE_UP:
- return KEY_PAGEUP;
- case DOM_VK_PAGE_DOWN:
- return KEY_PAGEDOWN;
- case DOM_VK_END:
- return KEY_END;
- case DOM_VK_HOME:
- return KEY_HOME;
- case DOM_VK_LEFT:
- return KEY_LEFT;
- case DOM_VK_UP:
- return KEY_UP;
- case DOM_VK_RIGHT:
- return KEY_RIGHT;
- case DOM_VK_DOWN:
- return KEY_DOWN;
-
- //case DOM_VK_SELECT: return KEY_UNKNOWN;
-
- case DOM_VK_PRINTSCREEN:
- case DOM_VK_PRINT:
- return KEY_PRINT;
-
- //case DOM_VK_EXECUTE: return KEY_UNKNOWN;
- case DOM_VK_INSERT:
- return KEY_INSERT;
- case DOM_VK_DELETE:
- return KEY_DELETE;
-
- case DOM_VK_META:
- case DOM_VK_WIN:
- return KEY_META;
-
- case DOM_VK_CONTEXT_MENU:
- return KEY_MENU;
- case DOM_VK_SLEEP:
- return KEY_STANDBY;
-
- // Numpad keys
- case DOM_VK_MULTIPLY:
- return KEY_KP_MULTIPLY;
- case DOM_VK_ADD:
- return KEY_KP_ADD;
- case DOM_VK_SEPARATOR:
- return KEY_KP_PERIOD; // Good enough?
- case DOM_VK_SUBTRACT:
- return KEY_KP_SUBTRACT;
- case DOM_VK_DECIMAL:
- return KEY_KP_PERIOD;
- case DOM_VK_DIVIDE:
- return KEY_KP_DIVIDE;
-
- /*
- case DOM_VK_F17: return KEY_UNKNOWN;
- case DOM_VK_F18: return KEY_UNKNOWN;
- case DOM_VK_F19: return KEY_UNKNOWN;
- case DOM_VK_F20: return KEY_UNKNOWN;
- case DOM_VK_F21: return KEY_UNKNOWN;
- case DOM_VK_F22: return KEY_UNKNOWN;
- case DOM_VK_F23: return KEY_UNKNOWN;
- case DOM_VK_F24: return KEY_UNKNOWN;
- */
-
- case DOM_VK_NUM_LOCK:
- return KEY_NUMLOCK;
- case DOM_VK_SCROLL_LOCK:
- return KEY_SCROLLLOCK;
-
- /*
- case DOM_VK_WIN_OEM_FJ_JISHO: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_MASSHOU: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_TOUROKU: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_LOYA: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_ROYA: return KEY_UNKNOWN;
- */
-
- case DOM_VK_CIRCUMFLEX:
- return KEY_ASCIICIRCUM;
- case DOM_VK_EXCLAMATION:
- return KEY_EXCLAM;
- case DOM_VK_DOUBLE_QUOTE:
- return KEY_QUOTEDBL;
- case DOM_VK_HASH:
- return KEY_NUMBERSIGN;
- case DOM_VK_DOLLAR:
- return KEY_DOLLAR;
- case DOM_VK_PERCENT:
- return KEY_PERCENT;
- case DOM_VK_AMPERSAND:
- return KEY_AMPERSAND;
- case DOM_VK_UNDERSCORE:
- return KEY_UNDERSCORE;
- case DOM_VK_OPEN_PAREN:
- return KEY_PARENLEFT;
- case DOM_VK_CLOSE_PAREN:
- return KEY_PARENRIGHT;
- case DOM_VK_ASTERISK:
- return KEY_ASTERISK;
- case DOM_VK_PLUS:
- return KEY_PLUS;
- case DOM_VK_PIPE:
- return KEY_BAR;
- case DOM_VK_HYPHEN_MINUS:
- return KEY_MINUS;
- case DOM_VK_OPEN_CURLY_BRACKET:
- return KEY_BRACELEFT;
- case DOM_VK_CLOSE_CURLY_BRACKET:
- return KEY_BRACERIGHT;
- case DOM_VK_TILDE:
- return KEY_ASCIITILDE;
-
- case DOM_VK_VOLUME_MUTE:
- return KEY_VOLUMEMUTE;
- case DOM_VK_VOLUME_DOWN:
- return KEY_VOLUMEDOWN;
- case DOM_VK_VOLUME_UP:
- return KEY_VOLUMEUP;
-
- case DOM_VK_COMMA:
- return KEY_COMMA;
- case DOM_VK_PERIOD:
- return KEY_PERIOD;
- case DOM_VK_SLASH:
- return KEY_SLASH;
- case DOM_VK_BACK_QUOTE:
- return KEY_QUOTELEFT;
- case DOM_VK_OPEN_BRACKET:
- return KEY_BRACKETLEFT;
- case DOM_VK_BACK_SLASH:
- return KEY_BACKSLASH;
- case DOM_VK_CLOSE_BRACKET:
- return KEY_BRACKETRIGHT;
- case DOM_VK_QUOTE:
- return KEY_APOSTROPHE;
-
- // The rest is OEM/unusual.
-
- default:
- return KEY_UNKNOWN;
- };
+ // Alphanumeric section.
+ DOM2GODOT("Backquote", QUOTELEFT);
+ DOM2GODOT("Backslash", BACKSLASH);
+ DOM2GODOT("BracketLeft", BRACKETLEFT);
+ DOM2GODOT("BracketRight", BRACKETRIGHT);
+ DOM2GODOT("Comma", COMMA);
+ DOM2GODOT("Digit0", 0);
+ DOM2GODOT("Digit1", 1);
+ DOM2GODOT("Digit2", 2);
+ DOM2GODOT("Digit3", 3);
+ DOM2GODOT("Digit4", 4);
+ DOM2GODOT("Digit5", 5);
+ DOM2GODOT("Digit6", 6);
+ DOM2GODOT("Digit7", 7);
+ DOM2GODOT("Digit8", 8);
+ DOM2GODOT("Digit9", 9);
+ DOM2GODOT("Equal", EQUAL);
+ DOM2GODOT("IntlBackslash", BACKSLASH);
+ //DOM2GODOT("IntlRo", UNKNOWN);
+ DOM2GODOT("IntlYen", YEN);
+
+ DOM2GODOT("KeyA", A);
+ DOM2GODOT("KeyB", B);
+ DOM2GODOT("KeyC", C);
+ DOM2GODOT("KeyD", D);
+ DOM2GODOT("KeyE", E);
+ DOM2GODOT("KeyF", F);
+ DOM2GODOT("KeyG", G);
+ DOM2GODOT("KeyH", H);
+ DOM2GODOT("KeyI", I);
+ DOM2GODOT("KeyJ", J);
+ DOM2GODOT("KeyK", K);
+ DOM2GODOT("KeyL", L);
+ DOM2GODOT("KeyM", M);
+ DOM2GODOT("KeyN", N);
+ DOM2GODOT("KeyO", O);
+ DOM2GODOT("KeyP", P);
+ DOM2GODOT("KeyQ", Q);
+ DOM2GODOT("KeyR", R);
+ DOM2GODOT("KeyS", S);
+ DOM2GODOT("KeyT", T);
+ DOM2GODOT("KeyU", U);
+ DOM2GODOT("KeyV", V);
+ DOM2GODOT("KeyW", W);
+ DOM2GODOT("KeyX", X);
+ DOM2GODOT("KeyY", Y);
+ DOM2GODOT("KeyZ", Z);
+
+ DOM2GODOT("Minus", MINUS);
+ DOM2GODOT("Period", PERIOD);
+ DOM2GODOT("Quote", APOSTROPHE);
+ DOM2GODOT("Semicolon", SEMICOLON);
+ DOM2GODOT("Slash", SLASH);
+
+ // Functional keys in the Alphanumeric section.
+ DOM2GODOT("AltLeft", ALT);
+ DOM2GODOT("AltRight", ALT);
+ DOM2GODOT("Backspace", BACKSPACE);
+ DOM2GODOT("CapsLock", CAPSLOCK);
+ DOM2GODOT("ContextMenu", MENU);
+ DOM2GODOT("ControlLeft", CONTROL);
+ DOM2GODOT("ControlRight", CONTROL);
+ DOM2GODOT("Enter", ENTER);
+ DOM2GODOT("MetaLeft", SUPER_L);
+ DOM2GODOT("MetaRight", SUPER_R);
+ DOM2GODOT("ShiftLeft", SHIFT);
+ DOM2GODOT("ShiftRight", SHIFT);
+ DOM2GODOT("Space", SPACE);
+ DOM2GODOT("Tab", TAB);
+
+ // ControlPad section.
+ DOM2GODOT("Delete", DELETE);
+ DOM2GODOT("End", END);
+ DOM2GODOT("Help", HELP);
+ DOM2GODOT("Home", HOME);
+ DOM2GODOT("Insert", INSERT);
+ DOM2GODOT("PageDown", PAGEDOWN);
+ DOM2GODOT("PageUp", PAGEUP);
+
+ // ArrowPad section.
+ DOM2GODOT("ArrowDown", DOWN);
+ DOM2GODOT("ArrowLeft", LEFT);
+ DOM2GODOT("ArrowRight", RIGHT);
+ DOM2GODOT("ArrowUp", UP);
+
+ // Function section.
+ DOM2GODOT("Escape", ESCAPE);
+ DOM2GODOT("F1", F1);
+ DOM2GODOT("F2", F2);
+ DOM2GODOT("F3", F3);
+ DOM2GODOT("F4", F4);
+ DOM2GODOT("F5", F5);
+ DOM2GODOT("F6", F6);
+ DOM2GODOT("F7", F7);
+ DOM2GODOT("F8", F8);
+ DOM2GODOT("F9", F9);
+ DOM2GODOT("F10", F10);
+ DOM2GODOT("F11", F11);
+ DOM2GODOT("F12", F12);
+ //DOM2GODOT("Fn", UNKNOWN); // never actually fired, but included in the standard draft.
+ //DOM2GODOT("FnLock", UNKNOWN);
+ DOM2GODOT("PrintScreen", PRINT);
+ DOM2GODOT("ScrollLock", SCROLLLOCK);
+ DOM2GODOT("Pause", PAUSE);
+
+ // Media keys section.
+ DOM2GODOT("BrowserBack", BACK);
+ DOM2GODOT("BrowserFavorites", FAVORITES);
+ DOM2GODOT("BrowserForward", FORWARD);
+ DOM2GODOT("BrowserHome", OPENURL);
+ DOM2GODOT("BrowserRefresh", REFRESH);
+ DOM2GODOT("BrowserSearch", SEARCH);
+ DOM2GODOT("BrowserStop", STOP);
+ //DOM2GODOT("Eject", UNKNOWN);
+ DOM2GODOT("LaunchApp1", LAUNCH0);
+ DOM2GODOT("LaunchApp2", LAUNCH1);
+ DOM2GODOT("LaunchMail", LAUNCHMAIL);
+ DOM2GODOT("MediaPlayPause", MEDIAPLAY);
+ DOM2GODOT("MediaSelect", LAUNCHMEDIA);
+ DOM2GODOT("MediaStop", MEDIASTOP);
+ DOM2GODOT("MediaTrackNext", MEDIANEXT);
+ DOM2GODOT("MediaTrackPrevious", MEDIAPREVIOUS);
+ //DOM2GODOT("Power", UNKNOWN);
+ //DOM2GODOT("Sleep", UNKNOWN);
+ DOM2GODOT("AudioVolumeDown", VOLUMEDOWN);
+ DOM2GODOT("AudioVolumeMute", VOLUMEMUTE);
+ DOM2GODOT("AudioVolumeUp", VOLUMEUP);
+ //DOM2GODOT("WakeUp", UNKNOWN);
+ return KEY_UNKNOWN;
+#undef DOM2GODOT
}
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 740a72fafa..fd61c46e63 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -80,6 +80,9 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
Main::start();
os->get_main_loop()->init();
emscripten_resume_main_loop();
+ // Immediately run the first iteration.
+ // We are inside an animation frame, we want to immediately draw on the newly setup canvas.
+ main_loop_callback();
}
int main(int argc, char *argv[]) {
@@ -91,14 +94,15 @@ int main(int argc, char *argv[]) {
// Sync from persistent state into memory and then
// run the 'main_after_fs_sync' function.
/* clang-format off */
- EM_ASM(
+ EM_ASM({
FS.mkdir('/userfs');
FS.mount(IDBFS, {}, '/userfs');
FS.syncfs(true, function(err) {
- ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""])
+ requestAnimationFrame(function() {
+ ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);
+ });
});
-
- );
+ });
/* clang-format on */
return 0;
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index ef5ac66b34..a0954600a2 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -1846,37 +1846,106 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
}
}
-DisplayServerX11::LatinKeyboardVariant DisplayServerX11::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
-
- XkbDescRec *xkbdesc = XkbAllocKeyboard();
- ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+int DisplayServerX11::keyboard_get_layout_count() const {
+ int _group_count = 0;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
+ XkbFreeKeyboard(kbd, 0, true);
+ }
+ return _group_count;
+}
- XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
- ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
- ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+int DisplayServerX11::keyboard_get_current_layout() const {
+ XkbStateRec state;
+ XkbGetState(x11_display, XkbUseCoreKbd, &state);
+ return state.group;
+}
- char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
- ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+void DisplayServerX11::keyboard_set_current_layout(int p_index) {
+ ERR_FAIL_INDEX(p_index, keyboard_get_layout_count());
+ XkbLockGroup(x11_display, XkbUseCoreKbd, p_index);
+}
- Vector<String> info = String(layout).split("+");
- ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+String DisplayServerX11::keyboard_get_layout_language(int p_index) const {
+ String ret;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+ XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
+
+ int _group_count = 0;
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
- if (info[1].find("colemak") != -1) {
- return LATIN_KEYBOARD_COLEMAK;
- } else if (info[1].find("qwertz") != -1) {
- return LATIN_KEYBOARD_QWERTZ;
- } else if (info[1].find("azerty") != -1) {
- return LATIN_KEYBOARD_AZERTY;
- } else if (info[1].find("qzerty") != -1) {
- return LATIN_KEYBOARD_QZERTY;
- } else if (info[1].find("dvorak") != -1) {
- return LATIN_KEYBOARD_DVORAK;
- } else if (info[1].find("neo") != -1) {
- return LATIN_KEYBOARD_NEO;
+ Atom names = kbd->names->symbols;
+ if (names != None) {
+ char *name = XGetAtomName(x11_display, names);
+ Vector<String> info = String(name).split("+");
+ if (p_index >= 0 && p_index < _group_count) {
+ if (p_index + 1 < info.size()) {
+ ret = info[p_index + 1]; // Skip "pc" at the start and "inet"/"group" at the end of symbols.
+ } else {
+ ret = "en"; // No symbol for layout fallback to "en".
+ }
+ } else {
+ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
+ }
+ XFree(name);
+ }
+ XkbFreeKeyboard(kbd, 0, true);
}
+ return ret.substr(0, 2);
+}
+
+String DisplayServerX11::keyboard_get_layout_name(int p_index) const {
+ String ret;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+ XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
+
+ int _group_count = 0;
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
- return LATIN_KEYBOARD_QWERTY;
+ if (p_index >= 0 && p_index < _group_count) {
+ char *full_name = XGetAtomName(x11_display, groups[p_index]);
+ ret.parse_utf8(full_name);
+ XFree(full_name);
+ } else {
+ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
+ }
+ XkbFreeKeyboard(kbd, 0, true);
+ }
+ return ret;
}
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index b5ea71f72a..f01b9a2323 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -327,7 +327,11 @@ public:
virtual CursorShape cursor_get_shape() const;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+ virtual int keyboard_get_layout_count() const;
+ virtual int keyboard_get_current_layout() const;
+ virtual void keyboard_set_current_layout(int p_index);
+ virtual String keyboard_get_layout_language(int p_index) const;
+ virtual String keyboard_get_layout_name(int p_index) const;
virtual void process_events();
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 8133dfe2c4..fddb1d0ca6 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -281,7 +281,11 @@ public:
virtual bool get_swap_ok_cancel();
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+ virtual int keyboard_get_layout_count() const;
+ virtual int keyboard_get_current_layout() const;
+ virtual void keyboard_set_current_layout(int p_index);
+ virtual String keyboard_get_layout_language(int p_index) const;
+ virtual String keyboard_get_layout_name(int p_index) const;
virtual void process_events();
virtual void force_process_and_drop_events();
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 9a1191490c..4a94e09c1c 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -2987,85 +2987,129 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
}
}
+struct LayoutInfo {
+ String name;
+ String code;
+};
+
+static Vector<LayoutInfo> kbd_layouts;
+static int current_layout = 0;
static bool keyboard_layout_dirty = true;
static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
+ kbd_layouts.clear();
+ current_layout = 0;
keyboard_layout_dirty = true;
}
-// Returns string representation of keys, if they are printable.
-static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
- return nil;
+void _update_keyboard_layouts() {
+ @autoreleasepool {
+ TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource();
+ NSString *cur_name = (NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName);
+ CFRelease(cur_source);
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
- return nil;
+ // Enum IME layouts
+ NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
+ NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
+ for (NSUInteger i = 0; i < [list_ime count]; i++) {
+ LayoutInfo ly;
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
+ ly.name.parse_utf8([name UTF8String]);
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+ NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages);
+ ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
+ kbd_layouts.push_back(ly);
- OSStatus err;
- CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
+ }
+ }
+ [list_ime release];
- for (int i = 0; i < length; ++i) {
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
+ // Enum plain keyboard layouts
+ NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
+ NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
+ for (NSUInteger i = 0; i < [list_kbd count]; i++) {
+ LayoutInfo ly;
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
+ ly.name.parse_utf8([name UTF8String]);
- err = UCKeyTranslate(keyboardLayout,
- keyCode[i],
- kUCKeyActionDisplay,
- 0,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
+ NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages);
+ ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
+ kbd_layouts.push_back(ly);
- if (err != noErr) {
- CFRelease(output);
- return nil;
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
+ }
}
-
- CFStringAppendCharacters(output, chars, 1);
+ [list_kbd release];
}
- return (NSString *)output;
+ keyboard_layout_dirty = false;
}
-DisplayServerOSX::LatinKeyboardVariant DisplayServerOSX::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
-
- static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
+int DisplayServerOSX::keyboard_get_layout_count() const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
+ return kbd_layouts.size();
+}
+void DisplayServerOSX::keyboard_set_current_layout(int p_index) {
if (keyboard_layout_dirty) {
- layout = LATIN_KEYBOARD_QWERTY;
+ _update_keyboard_layouts();
+ }
+
+ ERR_FAIL_INDEX(p_index, kbd_layouts.size());
- CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y };
- NSString *test = createStringForKeys(keys, 6);
+ NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()];
- if ([test isEqualToString:@"qwertz"]) {
- layout = LATIN_KEYBOARD_QWERTZ;
- } else if ([test isEqualToString:@"azerty"]) {
- layout = LATIN_KEYBOARD_AZERTY;
- } else if ([test isEqualToString:@"qzerty"]) {
- layout = LATIN_KEYBOARD_QZERTY;
- } else if ([test isEqualToString:@"',.pyf"]) {
- layout = LATIN_KEYBOARD_DVORAK;
- } else if ([test isEqualToString:@"xvlcwk"]) {
- layout = LATIN_KEYBOARD_NEO;
- } else if ([test isEqualToString:@"qwfpgj"]) {
- layout = LATIN_KEYBOARD_COLEMAK;
+ NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
+ NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
+ for (NSUInteger i = 0; i < [list_kbd count]; i++) {
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
+ if ([name isEqualToString:cur_name]) {
+ TISSelectInputSource((TISInputSourceRef)[list_kbd objectAtIndex:i]);
+ break;
}
+ }
+ [list_kbd release];
- [test release];
+ NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
+ NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
+ for (NSUInteger i = 0; i < [list_ime count]; i++) {
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
+ if ([name isEqualToString:cur_name]) {
+ TISSelectInputSource((TISInputSourceRef)[list_ime objectAtIndex:i]);
+ break;
+ }
+ }
+ [list_ime release];
+}
+
+int DisplayServerOSX::keyboard_get_current_layout() const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
- keyboard_layout_dirty = false;
- return layout;
+ return current_layout;
+}
+
+String DisplayServerOSX::keyboard_get_layout_language(int p_index) const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
+
+ ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
+ return kbd_layouts[p_index].code;
+}
+
+String DisplayServerOSX::keyboard_get_layout_name(int p_index) const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
}
- return layout;
+ ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
+ return kbd_layouts[p_index].name;
}
void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 784fba75ec..c9b01ebbb4 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -55,6 +55,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
+ Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
@@ -66,6 +67,28 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
bool use_codesign() const { return false; }
bool use_dmg() const { return false; }
#endif
+ bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
+ String pname = p_package;
+
+ if (pname.length() == 0) {
+ if (r_error) {
+ *r_error = TTR("Identifier is missing.");
+ }
+ return false;
+ }
+
+ for (int i = 0; i < pname.length(); i++) {
+ CharType c = pname[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
+ if (r_error) {
+ *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
@@ -138,6 +161,11 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
#endif
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
@@ -363,6 +391,52 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
- and then wrap it up in a DMG
**/
+Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+#ifdef OSX_ENABLED
+ List<String> args;
+
+ args.push_back("altool");
+ args.push_back("--notarize-app");
+
+ args.push_back("--primary-bundle-id");
+ args.push_back(p_preset->get("application/identifier"));
+
+ args.push_back("--username");
+ args.push_back(p_preset->get("notarization/apple_id_name"));
+
+ args.push_back("--password");
+ args.push_back(p_preset->get("notarization/apple_id_password"));
+
+ args.push_back("--type");
+ args.push_back("osx");
+
+ if (p_preset->get("notarization/apple_team_id")) {
+ args.push_back("--asc-provider");
+ args.push_back(p_preset->get("notarization/apple_team_id"));
+ }
+
+ args.push_back("--file");
+ args.push_back(p_path);
+
+ String str;
+ Error err = OS::get_singleton()->execute("xcrun", args, true, nullptr, &str, nullptr, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("altool (" + p_path + "):\n" + str);
+ if (str.find("RequestUUID") == -1) {
+ EditorNode::add_io_error("altool: " + str);
+ return FAILED;
+ } else {
+ print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email.");
+ print_line(" You can check progress manually by opening a Terminal and running the following command:");
+ print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
+ }
+
+#endif
+
+ return OK;
+}
+
Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
#ifdef OSX_ENABLED
List<String> args;
@@ -399,7 +473,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
Error err = OS::get_singleton()->execute("codesign", args, true, nullptr, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
- print_line("codesign (" + p_path + "): " + str);
+ print_line("codesign (" + p_path + "):\n" + str);
if (str.find("no identity found") != -1) {
EditorNode::add_io_error("codesign: no identity found");
return FAILED;
@@ -714,6 +788,14 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
}
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (err == OK && noto_enabled) {
+ if (ep.step("Sending archive for notarization", 4)) {
+ return ERR_SKIP;
+ }
+ err = _notarize(p_preset, p_path);
+ }
+
// Clean up temporary .app dir.
OS::get_singleton()->move_to_trash(tmp_app_path_name);
}
@@ -803,6 +885,41 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
valid = dvalid || rvalid;
r_missing_templates = !valid;
+ String identifier = p_preset->get("application/identifier");
+ String pn_err;
+ if (!is_package_name_valid(identifier, &pn_err)) {
+ err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n";
+ valid = false;
+ }
+
+ bool sign_enabled = p_preset->get("codesign/enable");
+ if (sign_enabled) {
+ if (p_preset->get("codesign/identity") == "") {
+ err += TTR("Codesign: identity not specified.") + "\n";
+ valid = false;
+ }
+ }
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (noto_enabled) {
+ if (!sign_enabled) {
+ err += TTR("Notarization: code signing required.") + "\n";
+ valid = false;
+ }
+ bool hr_enabled = p_preset->get("codesign/hardened_runtime");
+ if (!hr_enabled) {
+ err += TTR("Notarization: hardened runtime required.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_name") == "") {
+ err += TTR("Notarization: Apple ID name not specified.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_password") == "") {
+ err += TTR("Notarization: Apple ID password not specified.") + "\n";
+ valid = false;
+ }
+ }
+
if (!err.empty()) {
r_error = err;
}
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index d342d30097..cfc371710b 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -311,9 +311,16 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
if (refCF) {
CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id);
}
+
+ int version = 0;
+ refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVersionNumberKey));
+ if (refCF) {
+ CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &version);
+ }
+
if (vendor && product_id) {
char uid[128];
- sprintf(uid, "%04x%08x%04x%08x", OSSwapHostToBigInt32(vendor), 0, OSSwapHostToBigInt32(product_id), 0);
+ sprintf(uid, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version));
input->joy_connection_changed(id, true, name, uid);
} else {
//bluetooth device
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index db42908f89..cb4716bd65 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -743,23 +743,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
// TODO: Add resource creation or image rescaling to enable other scales:
// 1.25, 1.5, 2.0
- real_t scales[] = { 1.0 };
- bool valid_w = false;
- bool valid_h = false;
-
- for (int i = 0; i < 1; i++) {
- int w = ceil(p_width * scales[i]);
- int h = ceil(p_height * scales[i]);
-
- if (w == p_image->get_width()) {
- valid_w = true;
- }
- if (h == p_image->get_height()) {
- valid_h = true;
- }
- }
-
- return valid_w && valid_h;
+ return p_width == p_image->get_width() && p_height == p_image->get_height();
}
Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const {
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 7bd67d3726..ee25754704 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -535,11 +535,28 @@ void OS_UWP::delay_usec(uint32_t p_usec) const {
uint64_t OS_UWP::get_ticks_usec() const {
uint64_t ticks;
- uint64_t time;
+
// This is the number of clock ticks since start
QueryPerformanceCounter((LARGE_INTEGER *)&ticks);
+
// Divide by frequency to get the time in seconds
- time = ticks * 1000000L / ticks_per_second;
+ // original calculation shown below is subject to overflow
+ // with high ticks_per_second and a number of days since the last reboot.
+ // time = ticks * 1000000L / ticks_per_second;
+
+ // we can prevent this by either using 128 bit math
+ // or separating into a calculation for seconds, and the fraction
+ uint64_t seconds = ticks / ticks_per_second;
+
+ // compiler will optimize these two into one divide
+ uint64_t leftover = ticks % ticks_per_second;
+
+ // remainder
+ uint64_t time = (leftover * 1000000L) / ticks_per_second;
+
+ // seconds
+ time += seconds * 1000000L;
+
// Subtract the time at game start to get
// the time since the game started
time -= ticks_start;
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 9d8344fa7e..0b7130db74 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -1372,70 +1372,99 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
AllowSetForegroundWindow(pid);
}
-DisplayServer::LatinKeyboardVariant DisplayServerWindows::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
+int DisplayServerWindows::keyboard_get_layout_count() const {
+ return GetKeyboardLayoutList(0, NULL);
+}
- unsigned long azerty[] = {
- 0x00020401, // Arabic (102) AZERTY
- 0x0001080c, // Belgian (Comma)
- 0x0000080c, // Belgian French
- 0x0000040c, // French
- 0 // <--- STOP MARK
- };
- unsigned long qwertz[] = {
- 0x0000041a, // Croation
- 0x00000405, // Czech
- 0x00000407, // German
- 0x00010407, // German (IBM)
- 0x0000040e, // Hungarian
- 0x0000046e, // Luxembourgish
- 0x00010415, // Polish (214)
- 0x00000418, // Romanian (Legacy)
- 0x0000081a, // Serbian (Latin)
- 0x0000041b, // Slovak
- 0x00000424, // Slovenian
- 0x0001042e, // Sorbian Extended
- 0x0002042e, // Sorbian Standard
- 0x0000042e, // Sorbian Standard (Legacy)
- 0x0000100c, // Swiss French
- 0x00000807, // Swiss German
- 0 // <--- STOP MARK
- };
- unsigned long dvorak[] = {
- 0x00010409, // US-Dvorak
- 0x00030409, // US-Dvorak for left hand
- 0x00040409, // US-Dvorak for right hand
- 0 // <--- STOP MARK
- };
+int DisplayServerWindows::keyboard_get_current_layout() const {
+ HKL cur_layout = GetKeyboardLayout(0);
+
+ int layout_count = GetKeyboardLayoutList(0, NULL);
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
+
+ for (int i = 0; i < layout_count; i++) {
+ if (cur_layout == layouts[i]) {
+ memfree(layouts);
+ return i;
+ }
+ }
+ memfree(layouts);
+ return -1;
+}
+
+void DisplayServerWindows::keyboard_set_current_layout(int p_index) {
+ int layout_count = GetKeyboardLayoutList(0, NULL);
+
+ ERR_FAIL_INDEX(p_index, layout_count);
+
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
+ ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS);
+ memfree(layouts);
+}
+
+String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {
+ int layout_count = GetKeyboardLayoutList(0, NULL);
- char name[KL_NAMELENGTH + 1];
- name[0] = 0;
- GetKeyboardLayoutNameA(name);
+ ERR_FAIL_INDEX_V(p_index, layout_count, "");
- unsigned long hex = strtoul(name, nullptr, 16);
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
- int i = 0;
- while (azerty[i] != 0) {
- if (azerty[i] == hex)
- return LATIN_KEYBOARD_AZERTY;
- i++;
+ wchar_t buf[LOCALE_NAME_MAX_LENGTH];
+ memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t));
+ LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);
+
+ memfree(layouts);
+
+ return String(buf).substr(0, 2);
+}
+
+String _get_full_layout_name_from_registry(HKL p_layout) {
+ String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0");
+ String ret;
+
+ HKEY hkey;
+ wchar_t layout_text[1024];
+ memset(layout_text, 0, 1024 * sizeof(wchar_t));
+
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)id.c_str(), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) {
+ return ret;
}
- i = 0;
- while (qwertz[i] != 0) {
- if (qwertz[i] == hex)
- return LATIN_KEYBOARD_QWERTZ;
- i++;
+ DWORD buffer = 1024;
+ DWORD vtype = REG_SZ;
+ if (RegQueryValueExW(hkey, L"Layout Text", NULL, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) {
+ ret = String(layout_text);
}
+ RegCloseKey(hkey);
+ return ret;
+}
+
+String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
+ int layout_count = GetKeyboardLayoutList(0, NULL);
+
+ ERR_FAIL_INDEX_V(p_index, layout_count, "");
+
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
- i = 0;
- while (dvorak[i] != 0) {
- if (dvorak[i] == hex)
- return LATIN_KEYBOARD_DVORAK;
- i++;
+ String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine).
+ if (ret == String()) {
+ wchar_t buf[LOCALE_NAME_MAX_LENGTH];
+ memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t));
+ LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);
+
+ wchar_t name[1024];
+ memset(name, 0, 1024 * sizeof(wchar_t));
+ GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024);
+
+ ret = String(name);
}
+ memfree(layouts);
- return LATIN_KEYBOARD_QWERTY;
+ return ret;
}
void DisplayServerWindows::process_events() {
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index caf8598dc2..995ced0809 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -523,7 +523,11 @@ public:
virtual void enable_for_stealing_focus(OS::ProcessID pid);
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+ virtual int keyboard_get_layout_count() const;
+ virtual int keyboard_get_current_layout() const;
+ virtual void keyboard_set_current_layout(int p_index);
+ virtual String keyboard_get_layout_language(int p_index) const;
+ virtual String keyboard_get_layout_name(int p_index) const;
virtual void process_events();
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 0cff12ca8c..271a4e41bc 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -33,10 +33,6 @@
#include <oleauto.h>
#include <wbemidl.h>
-#ifndef __GNUC__
-#define __builtin_bswap32 _byteswap_ulong
-#endif
-
#if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
#define GetProcAddress (void *)GetProcAddress
@@ -67,17 +63,25 @@ JoypadWindows::JoypadWindows(HWND *hwnd) {
for (int i = 0; i < JOYPADS_MAX; i++)
attached_joypads[i] = false;
- HRESULT result;
- result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
- if (FAILED(result)) {
- printf("failed init DINPUT: %ld\n", result);
+ HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
+ if (result == DI_OK) {
+ probe_joypads();
+ } else {
+ ERR_PRINT("Couldn't initialize DirectInput. Error: " + itos(result));
+ if (result == DIERR_OUTOFMEMORY) {
+ ERR_PRINT("The Windows DirectInput subsystem could not allocate sufficient memory.");
+ ERR_PRINT("Rebooting your PC may solve this issue.");
+ }
+ // Ensure dinput is still a nullptr.
+ dinput = nullptr;
}
- probe_joypads();
}
JoypadWindows::~JoypadWindows() {
close_joypad();
- dinput->Release();
+ if (dinput) {
+ dinput->Release();
+ }
unload_xinput();
}
@@ -135,6 +139,7 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
}
bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
+ ERR_FAIL_NULL_V_MSG(dinput, false, "DirectInput not initialized. Rebooting your PC may solve this issue.");
HRESULT hr;
int num = input->get_unused_joy_id();
@@ -158,10 +163,13 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
const GUID &guid = instance->guidProduct;
char uid[128];
- sprintf_s(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
- __builtin_bswap32(guid.Data1), guid.Data2, guid.Data3,
- guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
- guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
+
+ ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised.");
+ WORD type = BSWAP16(0x03);
+ WORD vendor = BSWAP16(LOWORD(guid.Data1));
+ WORD product = BSWAP16(HIWORD(guid.Data1));
+ WORD version = 0;
+ sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0);
id_to_change = joypad_count;
@@ -266,6 +274,7 @@ void JoypadWindows::close_joypad(int id) {
}
void JoypadWindows::probe_joypads() {
+ ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue.");
DWORD dwResult;
for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) {
ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE));
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 0dab0c601a..9c1b8f2949 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -343,55 +343,21 @@ OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
return ret;
}
-uint64_t OS_Windows::get_unix_time() const {
- FILETIME ft;
- SYSTEMTIME st;
- GetSystemTime(&st);
- SystemTimeToFileTime(&st, &ft);
-
- SYSTEMTIME ep;
- ep.wYear = 1970;
- ep.wMonth = 1;
- ep.wDayOfWeek = 4;
- ep.wDay = 1;
- ep.wHour = 0;
- ep.wMinute = 0;
- ep.wSecond = 0;
- ep.wMilliseconds = 0;
- FILETIME fep;
- SystemTimeToFileTime(&ep, &fep);
-
- // Type punning through unions (rather than pointer cast) as per:
- // https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks
- ULARGE_INTEGER ft_punning;
- ft_punning.LowPart = ft.dwLowDateTime;
- ft_punning.HighPart = ft.dwHighDateTime;
-
- ULARGE_INTEGER fep_punning;
- fep_punning.LowPart = fep.dwLowDateTime;
- fep_punning.HighPart = fep.dwHighDateTime;
-
- return (ft_punning.QuadPart - fep_punning.QuadPart) / 10000000;
-};
-
-uint64_t OS_Windows::get_system_time_secs() const {
- return get_system_time_msecs() / 1000;
-}
-
-uint64_t OS_Windows::get_system_time_msecs() const {
- const uint64_t WINDOWS_TICK = 10000;
- const uint64_t MSEC_TO_UNIX_EPOCH = 11644473600000LL;
+double OS_Windows::get_unix_time() const {
+ // 1 Windows tick is 100ns
+ const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000;
+ const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL;
SYSTEMTIME st;
GetSystemTime(&st);
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
- uint64_t ret;
- ret = ft.dwHighDateTime;
- ret <<= 32;
- ret |= ft.dwLowDateTime;
+ uint64_t ticks_time;
+ ticks_time = ft.dwHighDateTime;
+ ticks_time <<= 32;
+ ticks_time |= ft.dwLowDateTime;
- return (uint64_t)(ret / WINDOWS_TICK - MSEC_TO_UNIX_EPOCH);
+ return (double)(ticks_time - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND;
}
void OS_Windows::delay_usec(uint32_t p_usec) const {
@@ -403,12 +369,29 @@ void OS_Windows::delay_usec(uint32_t p_usec) const {
uint64_t OS_Windows::get_ticks_usec() const {
uint64_t ticks;
- uint64_t time;
+
// This is the number of clock ticks since start
if (!QueryPerformanceCounter((LARGE_INTEGER *)&ticks))
ticks = (UINT64)timeGetTime();
+
// Divide by frequency to get the time in seconds
- time = ticks * 1000000L / ticks_per_second;
+ // original calculation shown below is subject to overflow
+ // with high ticks_per_second and a number of days since the last reboot.
+ // time = ticks * 1000000L / ticks_per_second;
+
+ // we can prevent this by either using 128 bit math
+ // or separating into a calculation for seconds, and the fraction
+ uint64_t seconds = ticks / ticks_per_second;
+
+ // compiler will optimize these two into one divide
+ uint64_t leftover = ticks % ticks_per_second;
+
+ // remainder
+ uint64_t time = (leftover * 1000000L) / ticks_per_second;
+
+ // seconds
+ time += seconds * 1000000L;
+
// Subtract the time at game start to get
// the time since the game started
time -= ticks_start;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 11e3533bfd..910a83539a 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -129,9 +129,7 @@ public:
virtual Date get_date(bool utc) const;
virtual Time get_time(bool utc) const;
virtual TimeZoneInfo get_time_zone_info() const;
- virtual uint64_t get_unix_time() const;
- virtual uint64_t get_system_time_secs() const;
- virtual uint64_t get_system_time_msecs() const;
+ virtual double get_unix_time() const;
virtual Error set_cwd(const String &p_cwd);