Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Hardware Keyboard/Back Button Support #869

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 220 additions & 8 deletions sokol_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,7 @@ typedef enum sapp_keycode {
SAPP_KEYCODE_RIGHT_ALT = 346,
SAPP_KEYCODE_RIGHT_SUPER = 347,
SAPP_KEYCODE_MENU = 348,
SAPP_KEYCODE_BACK = 349, /*Android back button*/
} sapp_keycode;

/*
Expand Down Expand Up @@ -8092,20 +8093,221 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) {
_sapp_call_event(&_sapp.event);
return true;
}
_SOKOL_PRIVATE sapp_keycode _sapp_android_translate_key(int scancode) {
switch (scancode) {
case AKEYCODE_ESCAPE: return SAPP_KEYCODE_ESCAPE;
case AKEYCODE_TAB: return SAPP_KEYCODE_TAB;
case AKEYCODE_SHIFT_LEFT: return SAPP_KEYCODE_LEFT_SHIFT;
case AKEYCODE_SHIFT_RIGHT: return SAPP_KEYCODE_RIGHT_SHIFT;
case AKEYCODE_CTRL_LEFT: return SAPP_KEYCODE_LEFT_CONTROL;
case AKEYCODE_CTRL_RIGHT: return SAPP_KEYCODE_RIGHT_CONTROL;
case AKEYCODE_ALT_LEFT: return SAPP_KEYCODE_LEFT_ALT;
case AKEYCODE_ALT_RIGHT: return SAPP_KEYCODE_RIGHT_ALT;
case AKEYCODE_META_LEFT: return SAPP_KEYCODE_LEFT_SUPER;
case AKEYCODE_META_RIGHT: return SAPP_KEYCODE_RIGHT_SUPER;
case AKEYCODE_MENU: return SAPP_KEYCODE_MENU;
case AKEYCODE_NUM_LOCK: return SAPP_KEYCODE_NUM_LOCK;
case AKEYCODE_CAPS_LOCK: return SAPP_KEYCODE_CAPS_LOCK;
case AKEYCODE_SYSRQ: return SAPP_KEYCODE_PRINT_SCREEN;
case AKEYCODE_SCROLL_LOCK: return SAPP_KEYCODE_SCROLL_LOCK;
case AKEYCODE_BREAK: return SAPP_KEYCODE_PAUSE;
case AKEYCODE_FORWARD_DEL: return SAPP_KEYCODE_DELETE;
case AKEYCODE_DEL: return SAPP_KEYCODE_BACKSPACE;
case AKEYCODE_ENTER: return SAPP_KEYCODE_ENTER;
case AKEYCODE_MOVE_HOME: return SAPP_KEYCODE_HOME;
case AKEYCODE_MOVE_END: return SAPP_KEYCODE_END;
case AKEYCODE_PAGE_UP: return SAPP_KEYCODE_PAGE_UP;
case AKEYCODE_PAGE_DOWN: return SAPP_KEYCODE_PAGE_DOWN;
case AKEYCODE_INSERT: return SAPP_KEYCODE_INSERT;
case AKEYCODE_DPAD_LEFT: return SAPP_KEYCODE_LEFT;
case AKEYCODE_DPAD_RIGHT: return SAPP_KEYCODE_RIGHT;
case AKEYCODE_DPAD_DOWN: return SAPP_KEYCODE_DOWN;
case AKEYCODE_DPAD_UP: return SAPP_KEYCODE_UP;
case AKEYCODE_F1: return SAPP_KEYCODE_F1;
case AKEYCODE_F2: return SAPP_KEYCODE_F2;
case AKEYCODE_F3: return SAPP_KEYCODE_F3;
case AKEYCODE_F4: return SAPP_KEYCODE_F4;
case AKEYCODE_F5: return SAPP_KEYCODE_F5;
case AKEYCODE_F6: return SAPP_KEYCODE_F6;
case AKEYCODE_F7: return SAPP_KEYCODE_F7;
case AKEYCODE_F8: return SAPP_KEYCODE_F8;
case AKEYCODE_F9: return SAPP_KEYCODE_F9;
case AKEYCODE_F10: return SAPP_KEYCODE_F10;
case AKEYCODE_F11: return SAPP_KEYCODE_F11;
case AKEYCODE_F12: return SAPP_KEYCODE_F12;
case AKEYCODE_NUMPAD_DIVIDE: return SAPP_KEYCODE_KP_DIVIDE;
case AKEYCODE_NUMPAD_MULTIPLY: return SAPP_KEYCODE_KP_MULTIPLY;
case AKEYCODE_NUMPAD_SUBTRACT: return SAPP_KEYCODE_KP_SUBTRACT;
case AKEYCODE_NUMPAD_ADD: return SAPP_KEYCODE_KP_ADD;
case AKEYCODE_NUMPAD_0: return SAPP_KEYCODE_KP_0;
case AKEYCODE_NUMPAD_1: return SAPP_KEYCODE_KP_1;
case AKEYCODE_NUMPAD_2: return SAPP_KEYCODE_KP_2;
case AKEYCODE_NUMPAD_3: return SAPP_KEYCODE_KP_3;
case AKEYCODE_NUMPAD_4: return SAPP_KEYCODE_KP_4;
case AKEYCODE_NUMPAD_5: return SAPP_KEYCODE_KP_5;
case AKEYCODE_NUMPAD_6: return SAPP_KEYCODE_KP_6;
case AKEYCODE_NUMPAD_7: return SAPP_KEYCODE_KP_7;
case AKEYCODE_NUMPAD_8: return SAPP_KEYCODE_KP_8;
case AKEYCODE_NUMPAD_9: return SAPP_KEYCODE_KP_9;
case AKEYCODE_NUMPAD_DOT: return SAPP_KEYCODE_KP_DECIMAL;
case AKEYCODE_NUMPAD_EQUALS: return SAPP_KEYCODE_KP_EQUAL;
case AKEYCODE_NUMPAD_ENTER: return SAPP_KEYCODE_KP_ENTER;
case AKEYCODE_A: return SAPP_KEYCODE_A;
case AKEYCODE_B: return SAPP_KEYCODE_B;
case AKEYCODE_C: return SAPP_KEYCODE_C;
case AKEYCODE_D: return SAPP_KEYCODE_D;
case AKEYCODE_E: return SAPP_KEYCODE_E;
case AKEYCODE_F: return SAPP_KEYCODE_F;
case AKEYCODE_G: return SAPP_KEYCODE_G;
case AKEYCODE_H: return SAPP_KEYCODE_H;
case AKEYCODE_I: return SAPP_KEYCODE_I;
case AKEYCODE_J: return SAPP_KEYCODE_J;
case AKEYCODE_K: return SAPP_KEYCODE_K;
case AKEYCODE_L: return SAPP_KEYCODE_L;
case AKEYCODE_M: return SAPP_KEYCODE_M;
case AKEYCODE_N: return SAPP_KEYCODE_N;
case AKEYCODE_O: return SAPP_KEYCODE_O;
case AKEYCODE_P: return SAPP_KEYCODE_P;
case AKEYCODE_Q: return SAPP_KEYCODE_Q;
case AKEYCODE_R: return SAPP_KEYCODE_R;
case AKEYCODE_S: return SAPP_KEYCODE_S;
case AKEYCODE_T: return SAPP_KEYCODE_T;
case AKEYCODE_U: return SAPP_KEYCODE_U;
case AKEYCODE_V: return SAPP_KEYCODE_V;
case AKEYCODE_W: return SAPP_KEYCODE_W;
case AKEYCODE_X: return SAPP_KEYCODE_X;
case AKEYCODE_Y: return SAPP_KEYCODE_Y;
case AKEYCODE_Z: return SAPP_KEYCODE_Z;
case AKEYCODE_1: return SAPP_KEYCODE_1;
case AKEYCODE_2: return SAPP_KEYCODE_2;
case AKEYCODE_3: return SAPP_KEYCODE_3;
case AKEYCODE_4: return SAPP_KEYCODE_4;
case AKEYCODE_5: return SAPP_KEYCODE_5;
case AKEYCODE_6: return SAPP_KEYCODE_6;
case AKEYCODE_7: return SAPP_KEYCODE_7;
case AKEYCODE_8: return SAPP_KEYCODE_8;
case AKEYCODE_9: return SAPP_KEYCODE_9;
case AKEYCODE_0: return SAPP_KEYCODE_0;
case AKEYCODE_SPACE: return SAPP_KEYCODE_SPACE;
case AKEYCODE_MINUS: return SAPP_KEYCODE_MINUS;
case AKEYCODE_EQUALS: return SAPP_KEYCODE_EQUAL;
case AKEYCODE_LEFT_BRACKET: return SAPP_KEYCODE_LEFT_BRACKET;
case AKEYCODE_RIGHT_BRACKET: return SAPP_KEYCODE_RIGHT_BRACKET;
case AKEYCODE_BACKSLASH: return SAPP_KEYCODE_BACKSLASH;
case AKEYCODE_SEMICOLON: return SAPP_KEYCODE_SEMICOLON;
case AKEYCODE_APOSTROPHE: return SAPP_KEYCODE_APOSTROPHE;
case AKEYCODE_GRAVE: return SAPP_KEYCODE_GRAVE_ACCENT;
case AKEYCODE_COMMA: return SAPP_KEYCODE_COMMA;
case AKEYCODE_PERIOD: return SAPP_KEYCODE_PERIOD;
case AKEYCODE_SLASH: return SAPP_KEYCODE_SLASH;
case AKEYCODE_BACK: return SAPP_KEYCODE_BACK; /* Navigation back button */
default: return SAPP_KEYCODE_INVALID;
}
return SAPP_KEYCODE_INVALID;
}
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
_SOKOL_PRIVATE uint32_t _sapp_android_mods(const AInputEvent* e) {
uint32_t meta_state = AKeyEvent_getMetaState(e);
uint32_t mods = 0;
if (meta_state& AMETA_SHIFT_ON) {
mods |= SAPP_MODIFIER_SHIFT;
}
if (meta_state& AMETA_CTRL_ON) {
mods |= SAPP_MODIFIER_CTRL;
}
if (meta_state& AMETA_ALT_ON) {
mods |= SAPP_MODIFIER_ALT;
}
if (meta_state& AMETA_META_ON) {
mods |= SAPP_MODIFIER_SUPER;
}
return mods;
}
int _sapp_android_keycode_to_char(int eventType, int keyCode, int metaState)
{
ANativeActivity* activity =(ANativeActivity*)sapp_android_get_native_activity();
// Attaches the current thread to the JVM.
JavaVM *javaVM = activity->vm;
JNIEnv *jniEnv = activity->env;

jint result = (*javaVM)->AttachCurrentThread(javaVM, &jniEnv, NULL);
if(result == JNI_ERR){
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
return 0;
}

jclass class_key_event = (*jniEnv)->FindClass(jniEnv,"android/view/KeyEvent");
int unicodeKey;

if(metaState == 0){
jmethodID method_get_unicode_char = (*jniEnv)->GetMethodID(jniEnv,class_key_event, "getUnicodeChar", "()I");
jmethodID eventConstructor = (*jniEnv)->GetMethodID(jniEnv,class_key_event, "<init>", "(II)V");
jobject eventObj = (*jniEnv)->NewObject(jniEnv,class_key_event, eventConstructor, eventType, keyCode);
unicodeKey = (*jniEnv)->CallIntMethod(jniEnv,eventObj, method_get_unicode_char);
}else{
jmethodID method_get_unicode_char = (*jniEnv)->GetMethodID(jniEnv,class_key_event, "getUnicodeChar", "(I)I");
jmethodID eventConstructor = (*jniEnv)->GetMethodID(jniEnv,class_key_event, "<init>", "(II)V");
jobject eventObj = (*jniEnv)->NewObject(jniEnv,class_key_event, eventConstructor, eventType, keyCode);
unicodeKey = (*jniEnv)->CallIntMethod(jniEnv,eventObj, method_get_unicode_char, metaState);
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
}

(*javaVM)->DetachCurrentThread(javaVM);

return unicodeKey;
}
_SOKOL_PRIVATE void _sapp_android_char_event(uint32_t keycode, bool repeat,const AInputEvent* e) {
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
if (_sapp_events_enabled() ) {
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
_sapp.event.modifiers = _sapp_android_mods(e);
_sapp.event.char_code = _sapp_android_keycode_to_char(AInputEvent_getType(e),keycode,AKeyEvent_getMetaState(e));
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
_sapp.event.key_repeat = repeat;
_sapp_call_event(&_sapp.event);
}
}
_SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) {
if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) {
return false;
}
if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) {
/* FIXME: this should be hooked into a "really quit?" mechanism
so the app can ask the user for confirmation, this is currently
generally missing in sokol_app.h
*/
_sapp_android_shutdown();
return true;
if (!_sapp_events_enabled()) {
return false;
}
return false;
int32_t action = AKeyEvent_getAction(e);
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
// Don't process soft keyboard commands as they are not reliable through this interface.
if(AKeyEvent_getFlags(e)&AKEY_EVENT_FLAG_SOFT_KEYBOARD)return false;
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
sapp_event_type type = SAPP_EVENTTYPE_INVALID;
switch (action) {

skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
case AKEY_EVENT_ACTION_DOWN :
SOKOL_LOG("Key: down");
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
type = SAPP_EVENTTYPE_KEY_DOWN;
break;
case AKEY_EVENT_ACTION_UP:
SOKOL_LOG("Key: up");
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
type = SAPP_EVENTTYPE_KEY_UP;
break;
default:
break;
}
if (type == SAPP_EVENTTYPE_INVALID) {
return false;
}
bool repeat = AKeyEvent_getRepeatCount(e)>0;
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
_sapp_init_event(type);
_sapp.event.key_code = _sapp_android_translate_key(AKeyEvent_getKeyCode(e));
_sapp.event.modifiers = _sapp_android_mods(e);
_sapp.event.key_repeat = repeat;
_sapp_call_event(&_sapp.event);
if(type==SAPP_EVENTTYPE_KEY_DOWN){
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
_sapp_android_char_event(AKeyEvent_getKeyCode(e),repeat,e);
}
/* check if a CLIPBOARD_PASTED event must be sent too */
if (_sapp.clipboard.enabled &&
(type == SAPP_EVENTTYPE_KEY_DOWN) &&
(_sapp.event.modifiers == SAPP_MODIFIER_CTRL) &&
(_sapp.event.key_code == SAPP_KEYCODE_V))
{
_sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED);
_sapp_call_event(&_sapp.event);
}
return true;
}

_SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) {
Expand Down Expand Up @@ -8268,6 +8470,16 @@ _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) {
bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update();
process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK;
}
/* handle quit-requested, either from window or from sapp_request_quit() */
if (_sapp.quit_requested && !_sapp.quit_ordered) {
/* give user code a chance to intervene */
_sapp_android_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
/* if user code hasn't intervened, quit the app */
if (_sapp.quit_requested) {
_sapp.quit_ordered = true;
}
}
if(_sapp.quit_ordered) _sapp_android_shutdown();
skylersaleh marked this conversation as resolved.
Show resolved Hide resolved
}

/* cleanup thread */
Expand Down
Loading