Godot version:
All 3.0 branches (stable/master)
OS/device including version:
Android
Issue description:
Godot v3 does not support physical keyboards on Android. It does not recognize input events.
Steps to reproduce:
Minimal reproduction project:
I've identified why this isn't working and am working on a sensible fix.
The problem is occurring in GodotView.java, about line 358:
if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|| (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
|| (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
// do a bunch of stuff
}
else
{
// keyboard input
}
The problem is occurring because according to the constants, the input can be both SOURCE_DPAD and SOURCE_KEYBOARD at the same time.
The constant for SOURCE_DPAD is 513
10 0000 0001
and the constant for SOURCE_KEYBOARD is 257
1 0000 0001
and the actual keyboard input (on the emulator at least) is 769
11 0000 0001
Thus the keyboard input is passing the test for dpad, and it never entering the 'else' part of the code.
_Progress report:_
Luckily the main functionality for keyboard input already exists (dating back to 2014 or so, some of it?) .. however it is currently broken by the above bug.
I've prepared a rather simple fix but I'm trying my best to ensure it does not impact on other input methods. I might need help on this, I may end up posting a test APK. I do have an android wireless gamepad myself but not tested it yet with Godot.
Anyway the fix so far is to replace the rather cumbersome if statement with a function, both in onKeyDown and onKeyUp (to avoid duplication of code):
private boolean isKeyEventSourceGameDevice(int source) {
// Physical keyboard input (SOURCE_KEYBOARD) can also be a SOURCE_DPAD
// We need to distinguish physical keyboard from DPAD
if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD)
return false;
// Is the source from a non-keyboard game device we are interested in?
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|| (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
|| (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
}
Once this is in it correctly sends keyboard events to Godot.
However what made things rather difficult is there seems to be a bug in the google android emulator. It has keyboard support but seems to do repeats by repeatedly sending keydown and keyup events with repeat count of zero. Which is fine for typing, but rather confusing for game input.
Anyway I've tried it out on my android tv box with a wireless keyboard and it doesn't have this problem on actual device, it seems to work as expected.
Another issue I've just tracked down is the old code calls super.onKeyDown(keyCode, event); regardless of whether we have processed the keyboard input. This is doing something weird when the cursor keys are pressed, perhaps there is some default action for the cursors on Android causing it to lose focus. An easy fix for this for now is to simply return true and not call the super function - this is after all what the current broken code was doing.
Spent the day doing more work on this issue, thank you to @wombatstampede for helping me with testing.
I have prepared a test APK (arm only) that will show you the keys as you are pressing physical keyboard, and also shows you the source value for the keyEvent:
https://github.com/lawnjelly/GodotTweaks/blob/master/InputTests/KeyboardTest5.apk
wombatstampede reports that the existing godot keyboard input works for him on his phone (with in built physical keyboard) already in Godot 3.1, leading me to believe it is reporting a source of 257 (SOURCE_KEYBOARD). However, every other keyboard that I have attached either on the android emulator or via wireless on devices, or indeed the tv controller on my tv box reports a source of 769 (SOURCE_KEYBOARD | SOURCE_DPAD). This would explain why for some people keyboard input works, and not for others.
After getting some negative consequences from the technique in the previous post I am now currently of the view that the best way of getting keyboard input without interfering with joystick / gamepad buttons may be to change the if statement from:
// Is the source from a non-keyboard game device we are interested in?
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|| (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
|| (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
to
// Is the source from a non-keyboard game device we are interested in?
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|| (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
i.e. remove the SOURCE_DPAD from consideration. It seems almost as if there has been a translation error by the Android developers where dpad has been translated to/from another language, and the value is now meaningless.
Please let me know the results of testing any keyboards / gamepad buttons / remote controls with the test APK, especially the 'onKeyDown source'.
Essentially the physical keyboard is working fine now I'm just trying to minimize the chance of knock on effects breaking something else in the Android input. If these tests go ok, then I'll prepare a pull request for the changes.
Most helpful comment
_Progress report:_
Luckily the main functionality for keyboard input already exists (dating back to 2014 or so, some of it?) .. however it is currently broken by the above bug.
I've prepared a rather simple fix but I'm trying my best to ensure it does not impact on other input methods. I might need help on this, I may end up posting a test APK. I do have an android wireless gamepad myself but not tested it yet with Godot.
Anyway the fix so far is to replace the rather cumbersome if statement with a function, both in onKeyDown and onKeyUp (to avoid duplication of code):
Once this is in it correctly sends keyboard events to Godot.
However what made things rather difficult is there seems to be a bug in the google android emulator. It has keyboard support but seems to do repeats by repeatedly sending keydown and keyup events with repeat count of zero. Which is fine for typing, but rather confusing for game input.
Anyway I've tried it out on my android tv box with a wireless keyboard and it doesn't have this problem on actual device, it seems to work as expected.
Another issue I've just tracked down is the old code calls
super.onKeyDown(keyCode, event);regardless of whether we have processed the keyboard input. This is doing something weird when the cursor keys are pressed, perhaps there is some default action for the cursors on Android causing it to lose focus. An easy fix for this for now is to simply return true and not call the super function - this is after all what the current broken code was doing.