summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMel Collins <sigh@ambimist.com>2023-08-03 15:18:26 +0200
committerMel Collins <sigh@ambimist.com>2024-01-26 14:42:28 +0100
commit8406e60522bb8d09649193be43c1c819edc1d059 (patch)
tree3c9ad2268b5c0c4ab50387e8d745528b2856daed
parent4b6ad349886288405890b07d4a8da425eb3c97ec (diff)
downloadredot-engine-8406e60522bb8d09649193be43c1c819edc1d059.tar.gz
Add InputEventKey.location to tell left from right
This adds a new enum `KeyLocation` and associated property `InputEventKey.location`, which indicates the left/right location of key events which may come from one of two physical keys, eg. Shift, Ctrl. It also adds simulation of missing Shift KEYUP events for Windows. When multiple Shifts are held down at the same time, Windows natively only sends a KEYUP for the last one to be released.
-rw-r--r--core/core_constants.cpp4
-rw-r--r--core/input/input_event.cpp44
-rw-r--r--core/input/input_event.h5
-rw-r--r--core/os/keyboard.h6
-rw-r--r--core/variant/binder_common.h1
-rw-r--r--core/variant/variant.h1
-rw-r--r--doc/classes/@GlobalScope.xml10
-rw-r--r--doc/classes/InputEventKey.xml9
-rw-r--r--editor/event_listener_line_edit.cpp6
-rw-r--r--editor/input_event_configuration_dialog.cpp46
-rw-r--r--editor/input_event_configuration_dialog.h4
-rw-r--r--platform/android/android_input_handler.cpp1
-rw-r--r--platform/android/android_keys_utils.cpp9
-rw-r--r--platform/android/android_keys_utils.h20
-rw-r--r--platform/ios/display_server_ios.h2
-rw-r--r--platform/ios/display_server_ios.mm3
-rw-r--r--platform/ios/key_mapping_ios.h1
-rw-r--r--platform/ios/key_mapping_ios.mm20
-rw-r--r--platform/ios/keyboard_input_view.mm8
-rw-r--r--platform/ios/view_controller.mm10
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp8
-rw-r--r--platform/linuxbsd/x11/key_mapping_x11.cpp22
-rw-r--r--platform/linuxbsd/x11/key_mapping_x11.h2
-rw-r--r--platform/macos/display_server_macos.h1
-rw-r--r--platform/macos/display_server_macos.mm2
-rw-r--r--platform/macos/godot_content_view.mm3
-rw-r--r--platform/macos/key_mapping_macos.h1
-rw-r--r--platform/macos/key_mapping_macos.mm24
-rw-r--r--platform/web/display_server_web.cpp3
-rw-r--r--platform/web/display_server_web.h1
-rw-r--r--platform/web/dom_keys.inc21
-rw-r--r--platform/windows/display_server_windows.cpp45
-rw-r--r--platform/windows/key_mapping_windows.cpp23
-rw-r--r--platform/windows/key_mapping_windows.h1
-rw-r--r--tests/core/input/test_input_event_key.h56
35 files changed, 397 insertions, 26 deletions
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 3b96fc20c6..aaabbabfd9 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -507,6 +507,10 @@ void register_global_constants() {
BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, KPAD);
BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, GROUP_SWITCH);
+ BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, UNSPECIFIED);
+ BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, LEFT);
+ BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, RIGHT);
+
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, NONE);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, LEFT);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, RIGHT);
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index e99dd04599..89ffcecf50 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -364,6 +364,15 @@ char32_t InputEventKey::get_unicode() const {
return unicode;
}
+void InputEventKey::set_location(KeyLocation p_key_location) {
+ location = p_key_location;
+ emit_changed();
+}
+
+KeyLocation InputEventKey::get_location() const {
+ return location;
+}
+
void InputEventKey::set_echo(bool p_enable) {
echo = p_enable;
emit_changed();
@@ -436,6 +445,23 @@ String InputEventKey::as_text_key_label() const {
return mods_text.is_empty() ? kc : mods_text + "+" + kc;
}
+String InputEventKey::as_text_location() const {
+ String loc;
+
+ switch (location) {
+ case KeyLocation::LEFT:
+ loc = "left";
+ break;
+ case KeyLocation::RIGHT:
+ loc = "right";
+ break;
+ default:
+ break;
+ }
+
+ return loc;
+}
+
String InputEventKey::as_text() const {
String kc;
@@ -464,6 +490,11 @@ String InputEventKey::to_string() {
String kc = "";
String physical = "false";
+ String loc = as_text_location();
+ if (loc.is_empty()) {
+ loc = "unspecified";
+ }
+
if (keycode == Key::NONE && physical_keycode == Key::NONE && unicode != 0) {
kc = "U+" + String::num_uint64(unicode, 16) + " (" + String::chr(unicode) + ")";
} else if (keycode != Key::NONE) {
@@ -478,7 +509,7 @@ String InputEventKey::to_string() {
String mods = InputEventWithModifiers::as_text();
mods = mods.is_empty() ? "none" : mods;
- return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, pressed=%s, echo=%s", kc, mods, physical, p, e);
+ return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, location=%s, pressed=%s, echo=%s", kc, mods, physical, loc, p, e);
}
Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode, bool p_physical) {
@@ -531,6 +562,9 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool p_exact_ma
match = keycode == key->keycode;
} else if (physical_keycode != Key::NONE) {
match = physical_keycode == key->physical_keycode;
+ if (location != KeyLocation::UNSPECIFIED) {
+ match &= location == key->location;
+ }
} else {
match = false;
}
@@ -572,6 +606,9 @@ bool InputEventKey::is_match(const Ref<InputEvent> &p_event, bool p_exact_match)
return (keycode == key->keycode) &&
(!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask());
} else if (physical_keycode != Key::NONE) {
+ if (location != KeyLocation::UNSPECIFIED && location != key->location) {
+ return false;
+ }
return (physical_keycode == key->physical_keycode) &&
(!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask());
} else {
@@ -594,6 +631,9 @@ void InputEventKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_unicode", "unicode"), &InputEventKey::set_unicode);
ClassDB::bind_method(D_METHOD("get_unicode"), &InputEventKey::get_unicode);
+ ClassDB::bind_method(D_METHOD("set_location", "location"), &InputEventKey::set_location);
+ ClassDB::bind_method(D_METHOD("get_location"), &InputEventKey::get_location);
+
ClassDB::bind_method(D_METHOD("set_echo", "echo"), &InputEventKey::set_echo);
ClassDB::bind_method(D_METHOD("get_keycode_with_modifiers"), &InputEventKey::get_keycode_with_modifiers);
@@ -603,12 +643,14 @@ void InputEventKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("as_text_keycode"), &InputEventKey::as_text_keycode);
ClassDB::bind_method(D_METHOD("as_text_physical_keycode"), &InputEventKey::as_text_physical_keycode);
ClassDB::bind_method(D_METHOD("as_text_key_label"), &InputEventKey::as_text_key_label);
+ ClassDB::bind_method(D_METHOD("as_text_location"), &InputEventKey::as_text_location);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "keycode"), "set_keycode", "get_keycode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "physical_keycode"), "set_physical_keycode", "get_physical_keycode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "key_label"), "set_key_label", "get_key_label");
ADD_PROPERTY(PropertyInfo(Variant::INT, "unicode"), "set_unicode", "get_unicode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "location", PROPERTY_HINT_ENUM, "Unspecified,Left,Right"), "set_location", "get_location");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo");
}
diff --git a/core/input/input_event.h b/core/input/input_event.h
index ed7ccf0a9f..61a53116e9 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -157,6 +157,7 @@ class InputEventKey : public InputEventWithModifiers {
Key physical_keycode = Key::NONE;
Key key_label = Key::NONE;
uint32_t unicode = 0; ///unicode
+ KeyLocation location = KeyLocation::UNSPECIFIED;
bool echo = false; /// true if this is an echo key
@@ -178,6 +179,9 @@ public:
void set_unicode(char32_t p_unicode);
char32_t get_unicode() const;
+ void set_location(KeyLocation p_key_location);
+ KeyLocation get_location() const;
+
void set_echo(bool p_enable);
virtual bool is_echo() const override;
@@ -193,6 +197,7 @@ public:
virtual String as_text_physical_keycode() const;
virtual String as_text_keycode() const;
virtual String as_text_key_label() const;
+ virtual String as_text_location() const;
virtual String as_text() const override;
virtual String to_string() override;
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 785972d31d..2051973336 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -260,6 +260,12 @@ enum class KeyModifierMask {
GROUP_SWITCH = (1 << 30)
};
+enum class KeyLocation {
+ UNSPECIFIED,
+ LEFT,
+ RIGHT
+};
+
// To avoid having unnecessary operators, only define the ones that are needed.
constexpr Key operator-(uint32_t a, Key b) {
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 34b54f1d00..c9f5ae7fc6 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -176,6 +176,7 @@ VARIANT_ENUM_CAST(Variant::Operator);
VARIANT_ENUM_CAST(Key);
VARIANT_BITFIELD_CAST(KeyModifierMask);
+VARIANT_ENUM_CAST(KeyLocation);
static inline Key &operator|=(Key &a, BitField<KeyModifierMask> b) {
a = static_cast<Key>(static_cast<int>(a) | static_cast<int>(b.operator int64_t()));
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 602d287f22..429b346896 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -502,6 +502,7 @@ public:
VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyAxis)
VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyButton)
VARIANT_ENUM_CLASS_CONSTRUCTOR(Key)
+ VARIANT_ENUM_CLASS_CONSTRUCTOR(KeyLocation)
VARIANT_ENUM_CLASS_CONSTRUCTOR(MIDIMessage)
VARIANT_ENUM_CLASS_CONSTRUCTOR(MouseButton)
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index b74ca4b035..a2de6d2ac5 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2374,6 +2374,16 @@
<constant name="KEY_MASK_GROUP_SWITCH" value="1073741824" enum="KeyModifierMask" is_bitfield="true">
Group Switch key mask.
</constant>
+ <constant name="KEY_LOCATION_UNSPECIFIED" value="0" enum="KeyLocation">
+ Used for keys which only appear once, or when a comparison doesn't need to differentiate the [code]LEFT[/code] and [code]RIGHT[/code] versions.
+ For example, when using [method InputEvent.is_match], an event which has [constant KEY_LOCATION_UNSPECIFIED] will match any [enum KeyLocation] on the passed event.
+ </constant>
+ <constant name="KEY_LOCATION_LEFT" value="1" enum="KeyLocation">
+ A key which is to the left of its twin.
+ </constant>
+ <constant name="KEY_LOCATION_RIGHT" value="2" enum="KeyLocation">
+ A key which is to the right of its twin.
+ </constant>
<constant name="MOUSE_BUTTON_NONE" value="0" enum="MouseButton">
Enum value which doesn't correspond to any mouse button. This is used to initialize [enum MouseButton] properties with a generic state.
</constant>
diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml
index 48a6804290..7b6fc3f216 100644
--- a/doc/classes/InputEventKey.xml
+++ b/doc/classes/InputEventKey.xml
@@ -24,6 +24,12 @@
Returns a [String] representation of the event's [member keycode] and modifiers.
</description>
</method>
+ <method name="as_text_location" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns a [String] representation of the event's [member location]. This will be a blank string if the event is not specific to a location.
+ </description>
+ </method>
<method name="as_text_physical_keycode" qualifiers="const">
<return type="String" />
<description>
@@ -77,6 +83,9 @@
+-----+ +-----+
[/codeblock]
</member>
+ <member name="location" type="int" setter="set_location" getter="get_location" enum="KeyLocation" default="0">
+ Represents the location of a key which has both left and right versions, such as [kbd]Shift[/kbd] or [kbd]Alt[/kbd].
+ </member>
<member name="physical_keycode" type="int" setter="set_physical_keycode" getter="get_physical_keycode" enum="Key" default="0">
Represents the physical location of a key on the 101/102-key US QWERTY keyboard, which corresponds to one of the [enum Key] constants.
To get a human-readable representation of the [InputEventKey], use [method OS.get_keycode_string] in combination with [method DisplayServer.keyboard_get_keycode_from_physical]:
diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp
index 2c46e1c20a..c29e83d624 100644
--- a/editor/event_listener_line_edit.cpp
+++ b/editor/event_listener_line_edit.cpp
@@ -79,7 +79,11 @@ String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, boo
if (!text.is_empty()) {
text += " " + TTR("or") + " ";
}
- text += mods_text + keycode_get_string(key->get_physical_keycode()) + " (" + TTR("Physical") + ")";
+ text += mods_text + keycode_get_string(key->get_physical_keycode()) + " (" + TTR("Physical");
+ if (key->get_location() != KeyLocation::UNSPECIFIED) {
+ text += " " + key->as_text_location();
+ }
+ text += ")";
}
if (key->get_key_label() != Key::NONE) {
if (!text.is_empty()) {
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index 0f483fcaef..22673bec64 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -64,6 +64,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, c
bool show_mods = false;
bool show_device = false;
bool show_key = false;
+ bool show_location = false;
if (mod.is_valid()) {
show_mods = true;
@@ -77,12 +78,17 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, c
if (k.is_valid()) {
show_key = true;
- if (k->get_keycode() == Key::NONE && k->get_physical_keycode() == Key::NONE && k->get_key_label() != Key::NONE) {
+ Key phys_key = k->get_physical_keycode();
+ if (k->get_keycode() == Key::NONE && phys_key == Key::NONE && k->get_key_label() != Key::NONE) {
key_mode->select(KEYMODE_UNICODE);
} else if (k->get_keycode() != Key::NONE) {
key_mode->select(KEYMODE_KEYCODE);
- } else if (k->get_physical_keycode() != Key::NONE) {
+ } else if (phys_key != Key::NONE) {
key_mode->select(KEYMODE_PHY_KEYCODE);
+ if (phys_key == Key::SHIFT || phys_key == Key::CTRL || phys_key == Key::ALT || phys_key == Key::META) {
+ key_location->select((int)k->get_location());
+ show_location = true;
+ }
} else {
// Invalid key.
event = Ref<InputEvent>();
@@ -103,6 +109,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, c
mod_container->set_visible(show_mods);
device_container->set_visible(show_device);
key_mode->set_visible(show_key);
+ location_container->set_visible(show_location);
additional_options_container->show();
// Update mode selector based on original key event.
@@ -240,6 +247,9 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven
k->set_physical_keycode(Key::NONE);
k->set_keycode(Key::NONE);
}
+ if (key_location->get_selected_id() == (int)KeyLocation::UNSPECIFIED) {
+ k->set_location(KeyLocation::UNSPECIFIED);
+ }
}
Ref<InputEventWithModifiers> mod = received_event;
@@ -433,6 +443,17 @@ void InputEventConfigurationDialog::_key_mode_selected(int p_mode) {
_set_event(k, original_event);
}
+void InputEventConfigurationDialog::_key_location_selected(int p_location) {
+ Ref<InputEventKey> k = event;
+ if (k.is_null()) {
+ return;
+ }
+
+ k->set_location((KeyLocation)p_location);
+
+ _set_event(k, original_event);
+}
+
void InputEventConfigurationDialog::_input_list_item_selected() {
TreeItem *selected = input_list_tree->get_selected();
@@ -594,6 +615,8 @@ void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p
// Select "All Devices" by default.
device_id_option->select(0);
+ // Also "all locations".
+ key_location->select(0);
}
if (!p_current_action_name.is_empty()) {
@@ -726,5 +749,24 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
key_mode->hide();
additional_options_container->add_child(key_mode);
+ // Key Location Selection
+
+ location_container = memnew(HBoxContainer);
+ location_container->hide();
+
+ Label *location_label = memnew(Label);
+ location_label->set_text(TTR("Physical location"));
+ location_container->add_child(location_label);
+
+ key_location = memnew(OptionButton);
+ key_location->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ key_location->add_item(TTR("Any"), (int)KeyLocation::UNSPECIFIED);
+ key_location->add_item(TTR("Left"), (int)KeyLocation::LEFT);
+ key_location->add_item(TTR("Right"), (int)KeyLocation::RIGHT);
+ key_location->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_key_location_selected));
+
+ location_container->add_child(key_location);
+ additional_options_container->add_child(location_container);
+
main_vbox->add_child(additional_options_container);
}
diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h
index bde0b73ade..3ef089be8b 100644
--- a/editor/input_event_configuration_dialog.h
+++ b/editor/input_event_configuration_dialog.h
@@ -99,6 +99,9 @@ private:
OptionButton *key_mode = nullptr;
+ HBoxContainer *location_container = nullptr;
+ OptionButton *key_location = nullptr;
+
void _set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection = true);
void _on_listen_input_changed(const Ref<InputEvent> &p_event);
void _on_listen_focus_changed();
@@ -110,6 +113,7 @@ private:
void _mod_toggled(bool p_checked, int p_index);
void _autoremap_command_or_control_toggled(bool p_checked);
void _key_mode_selected(int p_mode);
+ void _key_location_selected(int p_location);
void _device_selection_changed(int p_option_button_index);
void _set_current_device(int p_device);
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index bd194478d9..8e7f355114 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -124,6 +124,7 @@ void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicod
ev->set_physical_keycode(physical_keycode);
ev->set_key_label(fix_key_label(p_key_label, keycode));
ev->set_unicode(fix_unicode(unicode));
+ ev->set_location(godot_location_from_android_code(p_physical_keycode));
ev->set_pressed(p_pressed);
ev->set_echo(p_echo);
diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp
index f50437e82a..83ee98e8bc 100644
--- a/platform/android/android_keys_utils.cpp
+++ b/platform/android/android_keys_utils.cpp
@@ -38,3 +38,12 @@ Key godot_code_from_android_code(unsigned int p_code) {
}
return Key::UNKNOWN;
}
+
+KeyLocation godot_location_from_android_code(unsigned int p_code) {
+ for (int i = 0; android_godot_location_pairs[i].android_code != AKEYCODE_MAX; i++) {
+ if (android_godot_location_pairs[i].android_code == p_code) {
+ return android_godot_location_pairs[i].godot_code;
+ }
+ }
+ return KeyLocation::UNSPECIFIED;
+}
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index 5cf5628a8b..77c0911f2b 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -177,4 +177,24 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
Key godot_code_from_android_code(unsigned int p_code);
+// Key location determination.
+struct AndroidGodotLocationPair {
+ unsigned int android_code = 0;
+ KeyLocation godot_code = KeyLocation::UNSPECIFIED;
+};
+
+static AndroidGodotLocationPair android_godot_location_pairs[] = {
+ { AKEYCODE_ALT_LEFT, KeyLocation::LEFT },
+ { AKEYCODE_ALT_RIGHT, KeyLocation::RIGHT },
+ { AKEYCODE_SHIFT_LEFT, KeyLocation::LEFT },
+ { AKEYCODE_SHIFT_RIGHT, KeyLocation::RIGHT },
+ { AKEYCODE_CTRL_LEFT, KeyLocation::LEFT },
+ { AKEYCODE_CTRL_RIGHT, KeyLocation::RIGHT },
+ { AKEYCODE_META_LEFT, KeyLocation::LEFT },
+ { AKEYCODE_META_RIGHT, KeyLocation::RIGHT },
+ { AKEYCODE_MAX, KeyLocation::UNSPECIFIED }
+};
+
+KeyLocation godot_location_from_android_code(unsigned int p_code);
+
#endif // ANDROID_KEYS_UTILS_H
diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h
index 3d19222fa8..3fdcc07f0b 100644
--- a/platform/ios/display_server_ios.h
+++ b/platform/ios/display_server_ios.h
@@ -119,7 +119,7 @@ public:
// MARK: Keyboard
- void key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed);
+ void key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location);
bool is_keyboard_active() const;
// MARK: Motion
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index c31f503605..c660dc5697 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -247,7 +247,7 @@ void DisplayServerIOS::touches_canceled(int p_idx) {
// MARK: Keyboard
-void DisplayServerIOS::key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed) {
+void DisplayServerIOS::key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location) {
Ref<InputEventKey> ev;
ev.instantiate();
ev->set_echo(false);
@@ -270,6 +270,7 @@ void DisplayServerIOS::key(Key p_key, char32_t p_char, Key p_unshifted, Key p_ph
ev->set_key_label(p_unshifted);
ev->set_physical_keycode(p_physical);
ev->set_unicode(fix_unicode(p_char));
+ ev->set_location(p_location);
perform_event(ev);
}
diff --git a/platform/ios/key_mapping_ios.h b/platform/ios/key_mapping_ios.h
index 6cc61175bb..8874da3024 100644
--- a/platform/ios/key_mapping_ios.h
+++ b/platform/ios/key_mapping_ios.h
@@ -41,6 +41,7 @@ class KeyMappingIOS {
public:
static void initialize();
static Key remap_key(CFIndex p_keycode);
+ static KeyLocation key_location(CFIndex p_keycode);
};
#endif // KEY_MAPPING_IOS_H
diff --git a/platform/ios/key_mapping_ios.mm b/platform/ios/key_mapping_ios.mm
index d2c84884d1..61f28aa84b 100644
--- a/platform/ios/key_mapping_ios.mm
+++ b/platform/ios/key_mapping_ios.mm
@@ -38,6 +38,7 @@ struct HashMapHasherKeys {
};
HashMap<CFIndex, Key, HashMapHasherKeys> keyusage_map;
+HashMap<CFIndex, KeyLocation, HashMapHasherKeys> location_map;
void KeyMappingIOS::initialize() {
if (@available(iOS 13.4, *)) {
@@ -172,6 +173,15 @@ void KeyMappingIOS::initialize() {
keyusage_map[0x029D] = Key::GLOBE; // "Globe" key on smart connector / Mac keyboard.
keyusage_map[UIKeyboardHIDUsageKeyboardLANG1] = Key::JIS_EISU;
keyusage_map[UIKeyboardHIDUsageKeyboardLANG2] = Key::JIS_KANA;
+
+ location_map[UIKeyboardHIDUsageKeyboardLeftAlt] = KeyLocation::LEFT;
+ location_map[UIKeyboardHIDUsageKeyboardRightAlt] = KeyLocation::RIGHT;
+ location_map[UIKeyboardHIDUsageKeyboardLeftControl] = KeyLocation::LEFT;
+ location_map[UIKeyboardHIDUsageKeyboardRightControl] = KeyLocation::RIGHT;
+ location_map[UIKeyboardHIDUsageKeyboardLeftShift] = KeyLocation::LEFT;
+ location_map[UIKeyboardHIDUsageKeyboardRightShift] = KeyLocation::RIGHT;
+ location_map[UIKeyboardHIDUsageKeyboardLeftGUI] = KeyLocation::LEFT;
+ location_map[UIKeyboardHIDUsageKeyboardRightGUI] = KeyLocation::RIGHT;
}
}
@@ -184,3 +194,13 @@ Key KeyMappingIOS::remap_key(CFIndex p_keycode) {
}
return Key::NONE;
}
+
+KeyLocation KeyMappingIOS::key_location(CFIndex p_keycode) {
+ if (@available(iOS 13.4, *)) {
+ const KeyLocation *location = location_map.getptr(p_keycode);
+ if (location) {
+ return *location;
+ }
+ }
+ return KeyLocation::UNSPECIFIED;
+}
diff --git a/platform/ios/keyboard_input_view.mm b/platform/ios/keyboard_input_view.mm
index bc6eb63ed5..8b614662b7 100644
--- a/platform/ios/keyboard_input_view.mm
+++ b/platform/ios/keyboard_input_view.mm
@@ -116,8 +116,8 @@
- (void)deleteText:(NSInteger)charactersToDelete {
for (int i = 0; i < charactersToDelete; i++) {
- DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, true);
- DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, false);
+ DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
+ DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
}
}
@@ -137,8 +137,8 @@
key = Key::SPACE;
}
- DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, true);
- DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, false);
+ DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
+ DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
}
}
diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm
index 1f55670b68..6f6c04c2c8 100644
--- a/platform/ios/view_controller.mm
+++ b/platform/ios/view_controller.mm
@@ -78,13 +78,15 @@
us = u32lbl[0];
}
+ KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
+
if (!u32text.is_empty() && !u32text.begins_with("UIKey")) {
for (int i = 0; i < u32text.length(); i++) {
const char32_t c = u32text[i];
- DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true);
+ DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
}
} else {
- DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true);
+ DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
}
}
}
@@ -110,7 +112,9 @@
us = u32lbl[0];
}
- DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false);
+ KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
+
+ DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
}
}
}
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 13a0a9f877..1794efc3cd 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -3501,6 +3501,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
bool keypress = xkeyevent->type == KeyPress;
Key keycode = KeyMappingX11::get_keycode(keysym_keycode);
Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
+ KeyLocation key_location = KeyMappingX11::get_location(xkeyevent->keycode);
if (keycode >= Key::A + 32 && keycode <= Key::Z + 32) {
keycode -= 'a' - 'A';
@@ -3538,6 +3539,8 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
k->set_unicode(fix_unicode(tmp[i]));
}
+ k->set_location(key_location);
+
k->set_echo(false);
if (k->get_keycode() == Key::BACKTAB) {
@@ -3563,6 +3566,8 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
Key keycode = KeyMappingX11::get_keycode(keysym_keycode);
Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
+ KeyLocation key_location = KeyMappingX11::get_location(xkeyevent->keycode);
+
/* Phase 3, obtain a unicode character from the keysym */
// KeyMappingX11 also translates keysym to unicode.
@@ -3662,6 +3667,9 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
if (keypress) {
k->set_unicode(fix_unicode(unicode));
}
+
+ k->set_location(key_location);
+
k->set_echo(p_echo);
if (k->get_keycode() == Key::BACKTAB) {
diff --git a/platform/linuxbsd/x11/key_mapping_x11.cpp b/platform/linuxbsd/x11/key_mapping_x11.cpp
index c0e6b91d57..b589a2a573 100644
--- a/platform/linuxbsd/x11/key_mapping_x11.cpp
+++ b/platform/linuxbsd/x11/key_mapping_x11.cpp
@@ -1113,6 +1113,20 @@ void KeyMappingX11::initialize() {
xkeysym_unicode_map[0x13BD] = 0x0153;
xkeysym_unicode_map[0x13BE] = 0x0178;
xkeysym_unicode_map[0x20AC] = 0x20AC;
+
+ // Scancode to physical location map.
+ // Ctrl.
+ location_map[0x25] = KeyLocation::LEFT;
+ location_map[0x69] = KeyLocation::RIGHT;
+ // Shift.
+ location_map[0x32] = KeyLocation::LEFT;
+ location_map[0x3E] = KeyLocation::RIGHT;
+ // Alt.
+ location_map[0x40] = KeyLocation::LEFT;
+ location_map[0x6C] = KeyLocation::RIGHT;
+ // Meta.
+ location_map[0x85] = KeyLocation::LEFT;
+ location_map[0x86] = KeyLocation::RIGHT;
}
Key KeyMappingX11::get_keycode(KeySym p_keysym) {
@@ -1173,3 +1187,11 @@ char32_t KeyMappingX11::get_unicode_from_keysym(KeySym p_keysym) {
}
return 0;
}
+
+KeyLocation KeyMappingX11::get_location(unsigned int p_code) {
+ const KeyLocation *location = location_map.getptr(p_code);
+ if (location) {
+ return *location;
+ }
+ return KeyLocation::UNSPECIFIED;
+}
diff --git a/platform/linuxbsd/x11/key_mapping_x11.h b/platform/linuxbsd/x11/key_mapping_x11.h
index ae8fd67f27..a51ee5f48e 100644
--- a/platform/linuxbsd/x11/key_mapping_x11.h
+++ b/platform/linuxbsd/x11/key_mapping_x11.h
@@ -54,6 +54,7 @@ class KeyMappingX11 {
static inline HashMap<unsigned int, Key, HashMapHasherKeys> scancode_map;
static inline HashMap<Key, unsigned int, HashMapHasherKeys> scancode_map_inv;
static inline HashMap<KeySym, char32_t, HashMapHasherKeys> xkeysym_unicode_map;
+ static inline HashMap<unsigned int, KeyLocation, HashMapHasherKeys> location_map;
KeyMappingX11() {}
@@ -64,6 +65,7 @@ public:
static unsigned int get_xlibcode(Key p_keysym);
static Key get_scancode(unsigned int p_code);
static char32_t get_unicode_from_keysym(KeySym p_keysym);
+ static KeyLocation get_location(unsigned int p_code);
};
#endif // KEY_MAPPING_X11_H
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index cc343a6d64..64ba812a5b 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -75,6 +75,7 @@ public:
Key physical_keycode = Key::NONE;
Key key_label = Key::NONE;
uint32_t unicode = 0;
+ KeyLocation location = KeyLocation::UNSPECIFIED;
};
struct WindowData {
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 378688f78a..52554eb47b 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -477,6 +477,7 @@ void DisplayServerMacOS::_process_key_events() {
k->set_physical_keycode(ke.physical_keycode);
k->set_key_label(ke.key_label);
k->set_unicode(ke.unicode);
+ k->set_location(ke.location);
_push_input(k);
} else {
@@ -505,6 +506,7 @@ void DisplayServerMacOS::_process_key_events() {
k->set_keycode(ke.keycode);
k->set_physical_keycode(ke.physical_keycode);
k->set_key_label(ke.key_label);
+ k->set_location(ke.location);
if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == Key::NONE) {
k->set_unicode(key_event_buffer[i + 1].unicode);
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index 61065291d7..4505becbc2 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -606,6 +606,7 @@
ke.physical_keycode = KeyMappingMacOS::translate_key([event keyCode]);
ke.key_label = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], true);
ke.unicode = 0;
+ ke.location = KeyMappingMacOS::translate_location([event keyCode]);
ke.raw = false;
ds->push_to_key_event_buffer(ke);
@@ -671,6 +672,7 @@
ke.physical_keycode = KeyMappingMacOS::translate_key(key);
ke.key_label = KeyMappingMacOS::remap_key(key, mod, true);
ke.unicode = 0;
+ ke.location = KeyMappingMacOS::translate_location(key);
ds->push_to_key_event_buffer(ke);
}
@@ -698,6 +700,7 @@
ke.physical_keycode = KeyMappingMacOS::translate_key([event keyCode]);
ke.key_label = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], true);
ke.unicode = 0;
+ ke.location = KeyMappingMacOS::translate_location([event keyCode]);
ke.raw = true;
ds->push_to_key_event_buffer(ke);
diff --git a/platform/macos/key_mapping_macos.h b/platform/macos/key_mapping_macos.h
index 1bda4eb406..f5b0ff8d02 100644
--- a/platform/macos/key_mapping_macos.h
+++ b/platform/macos/key_mapping_macos.h
@@ -45,6 +45,7 @@ public:
static Key translate_key(unsigned int p_key);
static unsigned int unmap_key(Key p_key);
static Key remap_key(unsigned int p_key, unsigned int p_state, bool p_unicode);
+ static KeyLocation translate_location(unsigned int p_key);
// Mapping for menu shortcuts.
static String keycode_get_native_string(Key p_keycode);
diff --git a/platform/macos/key_mapping_macos.mm b/platform/macos/key_mapping_macos.mm
index db3fa4e02d..b5e72048e7 100644
--- a/platform/macos/key_mapping_macos.mm
+++ b/platform/macos/key_mapping_macos.mm
@@ -46,6 +46,7 @@ HashSet<unsigned int> numpad_keys;
HashMap<unsigned int, Key, HashMapHasherKeys> keysym_map;
HashMap<Key, unsigned int, HashMapHasherKeys> keysym_map_inv;
HashMap<Key, char32_t, HashMapHasherKeys> keycode_map;
+HashMap<unsigned int, KeyLocation, HashMapHasherKeys> location_map;
void KeyMappingMacOS::initialize() {
numpad_keys.insert(0x41); //kVK_ANSI_KeypadDecimal
@@ -321,6 +322,20 @@ void KeyMappingMacOS::initialize() {
keycode_map[Key::BAR] = '|';
keycode_map[Key::BRACERIGHT] = '}';
keycode_map[Key::ASCIITILDE] = '~';
+
+ // Keysym -> physical location.
+ // Ctrl.
+ location_map[0x3b] = KeyLocation::LEFT;
+ location_map[0x3e] = KeyLocation::RIGHT;
+ // Shift.
+ location_map[0x38] = KeyLocation::LEFT;
+ location_map[0x3c] = KeyLocation::RIGHT;
+ // Alt/Option.
+ location_map[0x3a] = KeyLocation::LEFT;
+ location_map[0x3d] = KeyLocation::RIGHT;
+ // Meta/Command (yes, right < left).
+ location_map[0x36] = KeyLocation::RIGHT;
+ location_map[0x37] = KeyLocation::LEFT;
}
bool KeyMappingMacOS::is_numpad_key(unsigned int p_key) {
@@ -396,6 +411,15 @@ Key KeyMappingMacOS::remap_key(unsigned int p_key, unsigned int p_state, bool p_
}
}
+// Translates a macOS keycode to a Godot key location.
+KeyLocation KeyMappingMacOS::translate_location(unsigned int p_key) {
+ const KeyLocation *location = location_map.getptr(p_key);
+ if (location) {
+ return *location;
+ }
+ return KeyLocation::UNSPECIFIED;
+}
+
String KeyMappingMacOS::keycode_get_native_string(Key p_keycode) {
const char32_t *key = keycode_map.getptr(p_keycode);
if (key) {
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index b4a190d47e..aacbe4879f 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -187,6 +187,7 @@ void DisplayServerWeb::_key_callback(const String &p_key_event_code, const Strin
Key keycode = dom_code2godot_scancode(p_key_event_code.utf8().get_data(), p_key_event_key.utf8().get_data(), false);
Key scancode = dom_code2godot_scancode(p_key_event_code.utf8().get_data(), p_key_event_key.utf8().get_data(), true);
+ KeyLocation location = dom_code2godot_key_location(p_key_event_code.utf8().get_data());
DisplayServerWeb::KeyEvent ke;
@@ -197,6 +198,7 @@ void DisplayServerWeb::_key_callback(const String &p_key_event_code, const Strin
ke.physical_keycode = scancode;
ke.key_label = fix_key_label(c, keycode);
ke.unicode = fix_unicode(c);
+ ke.location = location;
ke.mod = p_modifiers;
if (ds->key_event_pos >= ds->key_event_buffer.size()) {
@@ -1383,6 +1385,7 @@ void DisplayServerWeb::process_events() {
ev->set_physical_keycode(ke.physical_keycode);
ev->set_key_label(ke.key_label);
ev->set_unicode(ke.unicode);
+ ev->set_location(ke.location);
if (ke.raw) {
dom2godot_mod(ev, ke.mod, ke.keycode);
}
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index 140aef952b..682d10704f 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -95,6 +95,7 @@ private:
Key physical_keycode = Key::NONE;
Key key_label = Key::NONE;
uint32_t unicode = 0;
+ KeyLocation location = KeyLocation::UNSPECIFIED;
int mod = 0;
};
diff --git a/platform/web/dom_keys.inc b/platform/web/dom_keys.inc
index cd94b779c0..b20a3a46b9 100644
--- a/platform/web/dom_keys.inc
+++ b/platform/web/dom_keys.inc
@@ -223,3 +223,24 @@ Key dom_code2godot_scancode(EM_UTF8 const p_code[32], EM_UTF8 const p_key[32], b
return Key::NONE;
#undef DOM2GODOT
}
+
+KeyLocation dom_code2godot_key_location(EM_UTF8 const p_code[32]) {
+#define DOM2GODOT(m_str, m_godot_code) \
+ if (memcmp((const void *)m_str, (void *)p_code, strlen(m_str) + 1) == 0) { \
+ return KeyLocation::m_godot_code; \
+ }
+
+ DOM2GODOT("AltLeft", LEFT);
+ DOM2GODOT("AltRight", RIGHT);
+ DOM2GODOT("ControlLeft", LEFT);
+ DOM2GODOT("ControlRight", RIGHT);
+ DOM2GODOT("MetaLeft", LEFT);
+ DOM2GODOT("MetaRight", RIGHT);
+ DOM2GODOT("OSLeft", LEFT);
+ DOM2GODOT("OSRight", RIGHT);
+ DOM2GODOT("ShiftLeft", LEFT);
+ DOM2GODOT("ShiftRight", RIGHT);
+
+ return KeyLocation::UNSPECIFIED;
+#undef DOM2GODOT
+}
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b56954ae81..1a2a4389b1 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -165,20 +165,26 @@ DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() con
void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) {
use_raw_input = true;
- RAWINPUTDEVICE rid[1] = {};
- rid[0].usUsagePage = 0x01;
- rid[0].usUsage = 0x02;
+ RAWINPUTDEVICE rid[2] = {};
+ rid[0].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
+ rid[0].usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
rid[0].dwFlags = 0;
+ rid[1].usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
+ rid[1].usUsage = 0x06; // HID_USAGE_GENERIC_KEYBOARD
+ rid[1].dwFlags = 0;
+
if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) {
// Follow the defined window
rid[0].hwndTarget = windows[p_target_window].hWnd;
+ rid[1].hwndTarget = windows[p_target_window].hWnd;
} else {
// Follow the keyboard focus
rid[0].hwndTarget = 0;
+ rid[1].hwndTarget = 0;
}
- if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) {
+ if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {
// Registration failed.
use_raw_input = false;
}
@@ -3141,7 +3147,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WM_INPUT: {
- if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) {
+ if (!use_raw_input) {
break;
}
@@ -3159,7 +3165,32 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
RAWINPUT *raw = (RAWINPUT *)lpb;
- if (raw->header.dwType == RIM_TYPEMOUSE) {
+ if (raw->header.dwType == RIM_TYPEKEYBOARD) {
+ if (raw->data.keyboard.VKey == VK_SHIFT) {
+ // If multiple Shifts are held down at the same time,
+ // Windows natively only sends a KEYUP for the last one to be released.
+ if (raw->data.keyboard.Flags & RI_KEY_BREAK) {
+ if (GetAsyncKeyState(VK_SHIFT) < 0) {
+ // A Shift is released, but another Shift is still held
+ ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
+
+ KeyEvent ke;
+ ke.shift = false;
+ ke.alt = alt_mem;
+ ke.control = control_mem;
+ ke.meta = meta_mem;
+ ke.uMsg = WM_KEYUP;
+ ke.window_id = window_id;
+
+ ke.wParam = VK_SHIFT;
+ // data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.
+ // Bit 30 -> key was previously down, bit 31 -> key is being released.
+ ke.lParam = raw->data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;
+ key_event_buffer[key_event_pos++] = ke;
+ }
+ }
+ }
+ } else if (mouse_mode == MOUSE_MODE_CAPTURED && raw->header.dwType == RIM_TYPEMOUSE) {
Ref<InputEventMouseMotion> mm;
mm.instantiate();
@@ -4164,6 +4195,7 @@ void DisplayServerWindows::_process_key_events() {
}
Key key_label = keycode;
Key physical_keycode = KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));
+ KeyLocation location = KeyMappingWindows::get_location((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24));
static BYTE keyboard_state[256];
memset(keyboard_state, 0, 256);
@@ -4190,6 +4222,7 @@ void DisplayServerWindows::_process_key_events() {
}
k->set_keycode(keycode);
k->set_physical_keycode(physical_keycode);
+ k->set_location(location);
k->set_key_label(key_label);
if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {
diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp
index b376854c0c..20905d0fe9 100644
--- a/platform/windows/key_mapping_windows.cpp
+++ b/platform/windows/key_mapping_windows.cpp
@@ -46,6 +46,7 @@ HashMap<unsigned int, Key, HashMapHasherKeys> vk_map;
HashMap<unsigned int, Key, HashMapHasherKeys> scansym_map;
HashMap<Key, unsigned int, HashMapHasherKeys> scansym_map_inv;
HashMap<unsigned int, Key, HashMapHasherKeys> scansym_map_ext;
+HashMap<unsigned int, KeyLocation, HashMapHasherKeys> location_map;
void KeyMappingWindows::initialize() {
// VK_LBUTTON (0x01)
@@ -380,6 +381,15 @@ void KeyMappingWindows::initialize() {
scansym_map_ext[0x6C] = Key::LAUNCHMAIL;
scansym_map_ext[0x6D] = Key::LAUNCHMEDIA;
scansym_map_ext[0x78] = Key::MEDIARECORD;
+
+ // Scancode to physical location map.
+ // Shift.
+ location_map[0x2A] = KeyLocation::LEFT;
+ location_map[0x36] = KeyLocation::RIGHT;
+ // Meta.
+ location_map[0x5B] = KeyLocation::LEFT;
+ location_map[0x5C] = KeyLocation::RIGHT;
+ // Ctrl and Alt must be handled differently.
}
Key KeyMappingWindows::get_keysym(unsigned int p_code) {
@@ -424,3 +434,16 @@ bool KeyMappingWindows::is_extended_key(unsigned int p_code) {
p_code == VK_RIGHT ||
p_code == VK_DOWN;
}
+
+KeyLocation KeyMappingWindows::get_location(unsigned int p_code, bool p_extended) {
+ // Right- ctrl and alt have the same scancode as left, but are in the extended keys.
+ const Key *key = scansym_map.getptr(p_code);
+ if (key && (*key == Key::CTRL || *key == Key::ALT)) {
+ return p_extended ? KeyLocation::RIGHT : KeyLocation::LEFT;
+ }
+ const KeyLocation *location = location_map.getptr(p_code);
+ if (location) {
+ return *location;
+ }
+ return KeyLocation::UNSPECIFIED;
+}
diff --git a/platform/windows/key_mapping_windows.h b/platform/windows/key_mapping_windows.h
index a98aa7ed68..e6f184a2cc 100644
--- a/platform/windows/key_mapping_windows.h
+++ b/platform/windows/key_mapping_windows.h
@@ -47,6 +47,7 @@ public:
static unsigned int get_scancode(Key p_keycode);
static Key get_scansym(unsigned int p_code, bool p_extended);
static bool is_extended_key(unsigned int p_code);
+ static KeyLocation get_location(unsigned int p_code, bool p_extended);
};
#endif // KEY_MAPPING_WINDOWS_H
diff --git a/tests/core/input/test_input_event_key.h b/tests/core/input/test_input_event_key.h
index 3317941fad..80918542ce 100644
--- a/tests/core/input/test_input_event_key.h
+++ b/tests/core/input/test_input_event_key.h
@@ -85,6 +85,16 @@ TEST_CASE("[InputEventKey] Key correctly stores and retrieves unicode") {
CHECK(key.get_unicode() != 'y');
}
+TEST_CASE("[InputEventKey] Key correctly stores and retrieves location") {
+ InputEventKey key;
+
+ CHECK(key.get_location() == KeyLocation::UNSPECIFIED);
+
+ key.set_location(KeyLocation::LEFT);
+ CHECK(key.get_location() == KeyLocation::LEFT);
+ CHECK(key.get_location() != KeyLocation::RIGHT);
+}
+
TEST_CASE("[InputEventKey] Key correctly stores and checks echo") {
InputEventKey key;
@@ -144,32 +154,36 @@ TEST_CASE("[InputEventKey] Key correctly converts itself to text") {
TEST_CASE("[InputEventKey] Key correctly converts its state to a string representation") {
InputEventKey none_key;
- CHECK(none_key.to_string() == "InputEventKey: keycode=(Unset), mods=none, physical=false, pressed=false, echo=false");
+ CHECK(none_key.to_string() == "InputEventKey: keycode=(Unset), mods=none, physical=false, location=unspecified, pressed=false, echo=false");
// Set physical key to Escape.
none_key.set_physical_keycode(Key::ESCAPE);
- CHECK(none_key.to_string() == "InputEventKey: keycode=4194305 (Escape), mods=none, physical=true, pressed=false, echo=false");
+ CHECK(none_key.to_string() == "InputEventKey: keycode=4194305 (Escape), mods=none, physical=true, location=unspecified, pressed=false, echo=false");
InputEventKey key;
// Set physical to None, set keycode to Space.
key.set_keycode(Key::SPACE);
- CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, pressed=false, echo=false");
+ CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=unspecified, pressed=false, echo=false");
+
+ // Set location
+ key.set_location(KeyLocation::RIGHT);
+ CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=right, pressed=false, echo=false");
// Set pressed to true.
key.set_pressed(true);
- CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, pressed=true, echo=false");
+ CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=right, pressed=true, echo=false");
// set echo to true.
key.set_echo(true);
- CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, pressed=true, echo=true");
+ CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=right, pressed=true, echo=true");
// Press Ctrl and Alt.
key.set_ctrl_pressed(true);
key.set_alt_pressed(true);
#ifdef MACOS_ENABLED
- CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Option, physical=false, pressed=true, echo=true");
+ CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Option, physical=false, location=right, pressed=true, echo=true");
#else
- CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Alt, physical=false, pressed=true, echo=true");
+ CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Alt, physical=false, location=right, pressed=true, echo=true");
#endif
}
@@ -291,6 +305,34 @@ TEST_CASE("[IsMatch] Keys are correctly matched") {
CHECK(key2.is_match(match, true) == true);
CHECK(key2.is_match(no_match, true) == false);
+
+ // Physical key with location.
+ InputEventKey key3;
+ key3.set_keycode(Key::NONE);
+ key3.set_physical_keycode(Key::SHIFT);
+
+ Ref<InputEventKey> loc_ref = key.create_reference(Key::NONE);
+
+ loc_ref->set_keycode(Key::SHIFT);
+ loc_ref->set_physical_keycode(Key::SHIFT);
+
+ CHECK(key3.is_match(loc_ref, false) == true);
+ key3.set_location(KeyLocation::UNSPECIFIED);
+ CHECK(key3.is_match(loc_ref, false) == true);
+
+ loc_ref->set_location(KeyLocation::LEFT);
+ CHECK(key3.is_match(loc_ref, false) == true);
+
+ key3.set_location(KeyLocation::LEFT);
+ CHECK(key3.is_match(loc_ref, false) == true);
+
+ key3.set_location(KeyLocation::RIGHT);
+ CHECK(key3.is_match(loc_ref, false) == false);
+
+ // Keycode key with location.
+ key3.set_physical_keycode(Key::NONE);
+ key3.set_keycode(Key::SHIFT);
+ CHECK(key3.is_match(loc_ref, false) == true);
}
} // namespace TestInputEventKey