Skip to content

Commit

Permalink
Add physical_scancode (keyboard layout independent keycodes) to Inp…
Browse files Browse the repository at this point in the history
…utEventKey and InputMap.

Fix non-latin keyboard layout keycodes on Linux/X11 (fallback to physical keycodes).
  • Loading branch information
bruvzg committed May 6, 2021
1 parent 63391f6 commit dab4cf3
Show file tree
Hide file tree
Showing 28 changed files with 458 additions and 35 deletions.
42 changes: 39 additions & 3 deletions core/os/input_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ uint32_t InputEventKey::get_scancode() const {
return scancode;
}

void InputEventKey::set_physical_scancode(uint32_t p_scancode) {
physical_scancode = p_scancode;
}
uint32_t InputEventKey::get_physical_scancode() const {
return physical_scancode;
}

void InputEventKey::set_unicode(uint32_t p_unicode) {
unicode = p_unicode;
}
Expand Down Expand Up @@ -248,6 +255,20 @@ uint32_t InputEventKey::get_scancode_with_modifiers() const {
return sc;
}

uint32_t InputEventKey::get_physical_scancode_with_modifiers() const {
uint32_t sc = physical_scancode;
if (get_control())
sc |= KEY_MASK_CTRL;
if (get_alt())
sc |= KEY_MASK_ALT;
if (get_shift())
sc |= KEY_MASK_SHIFT;
if (get_metakey())
sc |= KEY_MASK_META;

return sc;
}

String InputEventKey::as_text() const {
String kc = keycode_get_string(scancode);
if (kc == String()) {
Expand Down Expand Up @@ -275,10 +296,19 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed
return false;
}

uint32_t code = get_scancode_with_modifiers();
uint32_t event_code = key->get_scancode_with_modifiers();
bool match = false;
if (get_scancode() == 0) {
uint32_t code = get_physical_scancode_with_modifiers();
uint32_t event_code = key->get_physical_scancode_with_modifiers();

match = get_physical_scancode() == key->get_physical_scancode() && (!key->is_pressed() || (code & event_code) == code);
} else {
uint32_t code = get_scancode_with_modifiers();
uint32_t event_code = key->get_scancode_with_modifiers();

match = get_scancode() == key->get_scancode() && (!key->is_pressed() || (code & event_code) == code);
}

bool match = get_scancode() == key->get_scancode() && (!key->is_pressed() || (code & event_code) == code);
if (match) {
if (p_pressed != nullptr) {
*p_pressed = key->is_pressed();
Expand Down Expand Up @@ -308,22 +338,28 @@ void InputEventKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scancode", "scancode"), &InputEventKey::set_scancode);
ClassDB::bind_method(D_METHOD("get_scancode"), &InputEventKey::get_scancode);

ClassDB::bind_method(D_METHOD("set_physical_scancode", "scancode"), &InputEventKey::set_physical_scancode);
ClassDB::bind_method(D_METHOD("get_physical_scancode"), &InputEventKey::get_physical_scancode);

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_echo", "echo"), &InputEventKey::set_echo);

ClassDB::bind_method(D_METHOD("get_scancode_with_modifiers"), &InputEventKey::get_scancode_with_modifiers);
ClassDB::bind_method(D_METHOD("get_physical_scancode_with_modifiers"), &InputEventKey::get_physical_scancode_with_modifiers);

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scancode"), "set_scancode", "get_scancode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "physical_scancode"), "set_physical_scancode", "get_physical_scancode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "unicode"), "set_unicode", "get_unicode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo");
}

InputEventKey::InputEventKey() {
pressed = false;
scancode = 0;
physical_scancode = 0;
unicode = 0; ///unicode
echo = false;
}
Expand Down
5 changes: 5 additions & 0 deletions core/os/input_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ class InputEventKey : public InputEventWithModifiers {
bool pressed; /// otherwise release

uint32_t scancode; ///< check keyboard.h , KeyCode enum, without modifier masks
uint32_t physical_scancode;
uint32_t unicode; ///unicode

bool echo; /// true if this is an echo key
Expand All @@ -283,13 +284,17 @@ class InputEventKey : public InputEventWithModifiers {
void set_scancode(uint32_t p_scancode);
uint32_t get_scancode() const;

void set_physical_scancode(uint32_t p_scancode);
uint32_t get_physical_scancode() const;

void set_unicode(uint32_t p_unicode);
uint32_t get_unicode() const;

void set_echo(bool p_enable);
virtual bool is_echo() const;

uint32_t get_scancode_with_modifiers() const;
uint32_t get_physical_scancode_with_modifiers() const;

virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
Expand Down
14 changes: 13 additions & 1 deletion doc/classes/InputEventKey.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
<link>https://docs.godotengine.org/en/3.3/tutorials/inputs/inputevent.html</link>
</tutorials>
<methods>
<method name="get_physical_scancode_with_modifiers" qualifiers="const">
<return type="int">
</return>
<description>
Returns the physical scancode combined with modifier keys such as [code]Shift[/code] or [code]Alt[/code]. See also [InputEventWithModifiers].
To get a human-readable representation of the [InputEventKey] with modifiers, use [code]OS.get_scancode_string(event.get_physical_scancode_with_modifiers())[/code] where [code]event[/code] is the [InputEventKey].
</description>
</method>
<method name="get_scancode_with_modifiers" qualifiers="const">
<return type="int">
</return>
Expand All @@ -23,11 +31,15 @@
<member name="echo" type="bool" setter="set_echo" getter="is_echo" default="false">
If [code]true[/code], the key was already pressed before this event. It means the user is holding the key down.
</member>
<member name="physical_scancode" type="int" setter="set_physical_scancode" getter="get_physical_scancode" default="0">
Key physical scancode, which corresponds to one of the [enum KeyList] constants. Represent the physical location of a key on the 101/102-key US QWERTY keyboard.
To get a human-readable representation of the [InputEventKey], use [code]OS.get_scancode_string(event.physical_scancode)[/code] where [code]event[/code] is the [InputEventKey].
</member>
<member name="pressed" type="bool" setter="set_pressed" getter="is_pressed" default="false">
If [code]true[/code], the key's state is pressed. If [code]false[/code], the key's state is released.
</member>
<member name="scancode" type="int" setter="set_scancode" getter="get_scancode" default="0">
The key scancode, which corresponds to one of the [enum KeyList] constants.
The key scancode, which corresponds to one of the [enum KeyList] constants. Represent key in the current keyboard layout.
To get a human-readable representation of the [InputEventKey], use [code]OS.get_scancode_string(event.scancode)[/code] where [code]event[/code] is the [InputEventKey].
</member>
<member name="unicode" type="int" setter="set_unicode" getter="get_unicode" default="0">
Expand Down
2 changes: 1 addition & 1 deletion editor/icons/icon_keyboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions editor/icons/icon_keyboard_physical.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 41 additions & 9 deletions editor/project_settings_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ void ProjectSettingsEditor::_notification(int p_what) {
translation_list->connect("button_pressed", this, "_translation_delete");
_update_actions();
popup_add->add_icon_item(get_icon("Keyboard", "EditorIcons"), TTR("Key "), INPUT_KEY); //"Key " - because the word 'key' has already been used as a key animation
popup_add->add_icon_item(get_icon("KeyboardPhysical", "EditorIcons"), TTR("Physical Key"), INPUT_KEY_PHYSICAL);
popup_add->add_icon_item(get_icon("JoyButton", "EditorIcons"), TTR("Joy Button"), INPUT_JOY_BUTTON);
popup_add->add_icon_item(get_icon("JoyAxis", "EditorIcons"), TTR("Joy Axis"), INPUT_JOY_MOTION);
popup_add->add_icon_item(get_icon("Mouse", "EditorIcons"), TTR("Mouse Button"), INPUT_MOUSE_BUTTON);
Expand Down Expand Up @@ -147,6 +148,7 @@ void ProjectSettingsEditor::_notification(int p_what) {
search_box->set_clear_button_enabled(true);
action_add_error->add_color_override("font_color", get_color("error_color", "Editor"));
popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY), get_icon("Keyboard", "EditorIcons"));
popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY_PHYSICAL), get_icon("KeyboardPhysical", "EditorIcons"));
popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_BUTTON), get_icon("JoyButton", "EditorIcons"));
popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_MOTION), get_icon("JoyAxis", "EditorIcons"));
popup_add->set_item_icon(popup_add->get_item_index(INPUT_MOUSE_BUTTON), get_icon("Mouse", "EditorIcons"));
Expand Down Expand Up @@ -357,7 +359,14 @@ void ProjectSettingsEditor::_press_a_key_confirm() {

Ref<InputEventKey> ie;
ie.instance();
ie->set_scancode(last_wait_for_key->get_scancode());
if (press_a_key_physical) {
ie->set_physical_scancode(last_wait_for_key->get_physical_scancode());
ie->set_scancode(0);
} else {
ie->set_physical_scancode(0);
ie->set_scancode(last_wait_for_key->get_scancode());
}

ie->set_shift(last_wait_for_key->get_shift());
ie->set_alt(last_wait_for_key->get_alt());
ie->set_control(last_wait_for_key->get_control());
Expand All @@ -375,8 +384,14 @@ void ProjectSettingsEditor::_press_a_key_confirm() {
if (aie.is_null()) {
continue;
}
if (aie->get_scancode_with_modifiers() == ie->get_scancode_with_modifiers()) {
return;
if (!press_a_key_physical) {
if (aie->get_scancode_with_modifiers() == ie->get_scancode_with_modifiers()) {
return;
}
} else {
if (aie->get_physical_scancode_with_modifiers() == ie->get_physical_scancode_with_modifiers()) {
return;
}
}
}

Expand Down Expand Up @@ -444,7 +459,7 @@ void ProjectSettingsEditor::_wait_for_key(const Ref<InputEvent> &p_event) {

if (k.is_valid() && k->is_pressed() && k->get_scancode() != 0) {
last_wait_for_key = p_event;
const String str = keycode_get_string(k->get_scancode_with_modifiers());
const String str = (press_a_key_physical) ? keycode_get_string(k->get_physical_scancode_with_modifiers()) + TTR(" (Physical)") : keycode_get_string(k->get_scancode_with_modifiers());

press_a_key_label->set_text(str);
press_a_key->get_ok()->set_disabled(false);
Expand All @@ -457,12 +472,20 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even

switch (add_type) {
case INPUT_KEY: {
press_a_key_physical = false;
press_a_key_label->set_text(TTR("Press a Key..."));
press_a_key->get_ok()->set_disabled(true);
last_wait_for_key = Ref<InputEvent>();
press_a_key->popup_centered(Size2(250, 80) * EDSCALE);
press_a_key->grab_focus();
} break;
case INPUT_KEY_PHYSICAL: {
press_a_key_physical = true;
press_a_key_label->set_text(TTR("Press a Key..."));
press_a_key->get_ok()->set_disabled(true);
last_wait_for_key = Ref<InputEvent>();
press_a_key->popup_centered(Size2(250, 80) * EDSCALE);
press_a_key->grab_focus();

} break;
case INPUT_MOUSE_BUTTON: {
device_index_label->set_text(TTR("Mouse Button Index:"));
Expand Down Expand Up @@ -538,7 +561,11 @@ void ProjectSettingsEditor::_edit_item(Ref<InputEvent> p_exiting_event) {
InputType ie_type;

if ((Ref<InputEventKey>(p_exiting_event)).is_valid()) {
ie_type = INPUT_KEY;
if ((Ref<InputEventKey>(p_exiting_event))->get_scancode() != 0) {
ie_type = INPUT_KEY;
} else {
ie_type = INPUT_KEY_PHYSICAL;
}

} else if ((Ref<InputEventJoypadButton>(p_exiting_event)).is_valid()) {
ie_type = INPUT_JOY_BUTTON;
Expand Down Expand Up @@ -729,10 +756,13 @@ void ProjectSettingsEditor::_update_actions() {

Ref<InputEventKey> k = event;
if (k.is_valid()) {
const String str = keycode_get_string(k->get_scancode_with_modifiers());

const String str = (k->get_scancode() == 0) ? keycode_get_string(k->get_physical_scancode_with_modifiers()) + TTR(" (Physical)") : keycode_get_string(k->get_scancode_with_modifiers());
action2->set_text(0, str);
action2->set_icon(0, get_icon("Keyboard", "EditorIcons"));
if ((k->get_scancode() != 0)) {
action2->set_icon(0, get_icon("Keyboard", "EditorIcons"));
} else {
action2->set_icon(0, get_icon("KeyboardPhysical", "EditorIcons"));
}
}

Ref<InputEventJoypadButton> jb = event;
Expand Down Expand Up @@ -1925,6 +1955,8 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
add_child(popup_add);
popup_add->connect("id_pressed", this, "_add_item");

press_a_key_physical = false;

press_a_key = memnew(ConfirmationDialog);
press_a_key->set_focus_mode(FOCUS_ALL);
add_child(press_a_key);
Expand Down
2 changes: 2 additions & 0 deletions editor/project_settings_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class ProjectSettingsEditor : public AcceptDialog {

enum InputType {
INPUT_KEY,
INPUT_KEY_PHYSICAL,
INPUT_JOY_BUTTON,
INPUT_JOY_MOTION,
INPUT_MOUSE_BUTTON
Expand Down Expand Up @@ -76,6 +77,7 @@ class ProjectSettingsEditor : public AcceptDialog {
OptionButton *type;
PopupMenu *popup_add;
ConfirmationDialog *press_a_key;
bool press_a_key_physical;
Label *press_a_key_label;
ConfirmationDialog *device_input;
OptionButton *device_id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1056,8 +1056,8 @@ public void run() {
int keyCode;
if ((keyCode = cc[i]) != 0) {
// Simulate key down and up...
GodotLib.key(0, keyCode, true);
GodotLib.key(0, keyCode, false);
GodotLib.key(0, 0, keyCode, true);
GodotLib.key(0, 0, keyCode, false);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public class GodotLib {
/**
* Forward regular key events from the main thread to the GL thread.
*/
public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed);
public static native void key(int p_keycode, int p_scancode, int p_unicode_char, boolean p_pressed);

/**
* Forward game device's key events from the main thread to the GL thread.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,12 @@ public void run() {
});
}
} else {
final int scanCode = event.getScanCode();
final int chr = event.getUnicodeChar(0);
queueEvent(new Runnable() {
@Override
public void run() {
GodotLib.key(keyCode, chr, false);
GodotLib.key(keyCode, scanCode, chr, false);
}
});
};
Expand Down Expand Up @@ -154,11 +155,12 @@ public void run() {
});
}
} else {
final int scanCode = event.getScanCode();
final int chr = event.getUnicodeChar(0);
queueEvent(new Runnable() {
@Override
public void run() {
GodotLib.key(keyCode, chr, true);
GodotLib.key(keyCode, scanCode, chr, true);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public void beforeTextChanged(final CharSequence pCharSequence, final int start,
@Override
public void run() {
for (int i = 0; i < count; ++i) {
GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true);
GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false);

if (mHasSelection) {
mHasSelection = false;
Expand Down Expand Up @@ -127,8 +127,8 @@ public void run() {
// Return keys are handled through action events
continue;
}
GodotLib.key(0, key, true);
GodotLib.key(0, key, false);
GodotLib.key(0, 0, key, true);
GodotLib.key(0, 0, key, false);
}
}
});
Expand All @@ -144,17 +144,17 @@ public boolean onEditorAction(final TextView pTextView, final int pActionID, fin
public void run() {
for (int i = 0; i < characters.length(); i++) {
final int ch = characters.codePointAt(i);
GodotLib.key(0, ch, true);
GodotLib.key(0, ch, false);
GodotLib.key(0, 0, ch, true);
GodotLib.key(0, 0, ch, false);
}
}
});
}

if (pActionID == EditorInfo.IME_ACTION_DONE) {
// Enter key has been pressed
GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, true);
GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, false);
GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);

this.mView.requestFocus();
return true;
Expand Down
4 changes: 2 additions & 2 deletions platform/android/java_godot_lib_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
}
}

JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
if (step == 0)
return;

os_android->process_key_event(p_scancode, p_unicode_char, p_pressed);
os_android->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed);
}

JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
Expand Down
2 changes: 1 addition & 1 deletion platform/android/java_godot_lib_jni.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNI
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y);
Expand Down
Loading

0 comments on commit dab4cf3

Please sign in to comment.