/** * Convert an Android key event to ASCII. */ static unsigned char key_ascii(struct android_app* app, AInputEvent* event) { int32_t code = AKeyEvent_getKeyCode(event); /* Handle a few special cases: */ switch (code) { case AKEYCODE_DEL: return 8; case AKEYCODE_FORWARD_DEL: return 127; case AKEYCODE_ESCAPE: return 27; } /* Get usable JNI context */ JNIEnv* env = app->activity->env; JavaVM* vm = app->activity->vm; (*vm)->AttachCurrentThread(vm, &env, NULL); jclass KeyEventClass = (*env)->FindClass(env, "android/view/KeyEvent"); jmethodID KeyEventConstructor = (*env)->GetMethodID(env, KeyEventClass, "<init>", "(II)V"); jobject keyEvent = (*env)->NewObject(env, KeyEventClass, KeyEventConstructor, AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event)); jmethodID KeyEvent_getUnicodeChar = (*env)->GetMethodID(env, KeyEventClass, "getUnicodeChar", "(I)I"); int ascii = (*env)->CallIntMethod(env, keyEvent, KeyEvent_getUnicodeChar, AKeyEvent_getMetaState(event)); /* LOGI("getUnicodeChar(%d) = %d ('%c')", AKeyEvent_getKeyCode(event), ascii, ascii); */ return ascii; }
/* * IN_Android_KeyEvent2UCS */ static wchar_t IN_Android_KeyEvent2UCS( const AInputEvent *event ) { JNIEnv *env = Sys_Android_GetJNIEnv(); static jclass mapClass; static jmethodID load, get; jobject map; unsigned int ucs; if( !mapClass ) { jclass mapClassRef; mapClassRef = (*env)->FindClass( env, "android/view/KeyCharacterMap" ); mapClass = (*env)->NewGlobalRef( env, mapClassRef ); load = (*env)->GetStaticMethodID( env, mapClass, "load", "(I)Landroid/view/KeyCharacterMap;" ); get = (*env)->GetMethodID( env, mapClass, "get", "(II)I" ); (*env)->DeleteLocalRef( env, mapClassRef ); } map = (*env)->CallStaticObjectMethod( env, mapClass, load, AInputEvent_getDeviceId( event ) ); ucs = (*env)->CallIntMethod( env, map, get, AKeyEvent_getKeyCode( event ), AKeyEvent_getMetaState( event ) ); (*env)->DeleteLocalRef( env, map ); return ucs; }
int32_t getKeyRune(JNIEnv* env, AInputEvent* e) { return (int32_t)(*env)->CallIntMethod( env, current_ctx, key_rune_method, AInputEvent_getDeviceId(e), AKeyEvent_getKeyCode(e), AKeyEvent_getMetaState(e) ); }
int WindowImplAndroid::getUnicode(AInputEvent* event) { // Retrieve activity states ActivityStates* states = getActivity(NULL); Lock lock(states->mutex); // Initializes JNI jint lResult; jint lFlags = 0; JavaVM* lJavaVM = states->activity->vm; JNIEnv* lJNIEnv = states->activity->env; JavaVMAttachArgs lJavaVMAttachArgs; lJavaVMAttachArgs.version = JNI_VERSION_1_6; lJavaVMAttachArgs.name = "NativeThread"; lJavaVMAttachArgs.group = NULL; lResult=lJavaVM->AttachCurrentThread(&lJNIEnv, &lJavaVMAttachArgs); if (lResult == JNI_ERR) err() << "Failed to initialize JNI, couldn't get the Unicode value" << std::endl; // Retrieve key data from the input event jlong downTime = AKeyEvent_getDownTime(event); jlong eventTime = AKeyEvent_getEventTime(event); jint action = AKeyEvent_getAction(event); jint code = AKeyEvent_getKeyCode(event); jint repeat = AKeyEvent_getRepeatCount(event); // not sure! jint metaState = AKeyEvent_getMetaState(event); jint deviceId = AInputEvent_getDeviceId(event); jint scancode = AKeyEvent_getScanCode(event); jint flags = AKeyEvent_getFlags(event); jint source = AInputEvent_getSource(event); // Construct a KeyEvent object from the event data jclass ClassKeyEvent = lJNIEnv->FindClass("android/view/KeyEvent"); jmethodID KeyEventConstructor = lJNIEnv->GetMethodID(ClassKeyEvent, "<init>", "(JJIIIIIIII)V"); jobject ObjectKeyEvent = lJNIEnv->NewObject(ClassKeyEvent, KeyEventConstructor, downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, flags, source); // Call its getUnicodeChar() method to get the Unicode value jmethodID MethodGetUnicode = lJNIEnv->GetMethodID(ClassKeyEvent, "getUnicodeChar", "(I)I"); int unicode = lJNIEnv->CallIntMethod(ObjectKeyEvent, MethodGetUnicode, metaState); lJNIEnv->DeleteLocalRef(ClassKeyEvent); lJNIEnv->DeleteLocalRef(ObjectKeyEvent); // Detach this thread from the JVM lJavaVM->DetachCurrentThread(); return unicode; }
extern "C" int AndroidGetKeyText( AInputEvent *event ) { int result = 0; // Attaches the current thread to the JVM. jint lResult; jint lFlags = 0; JavaVM* lJavaVM = engine.app->activity->vm; JNIEnv* lJNIEnv = engine.app->activity->env; JavaVMAttachArgs lJavaVMAttachArgs; lJavaVMAttachArgs.version = JNI_VERSION_1_6; lJavaVMAttachArgs.name = "NativeThread"; lJavaVMAttachArgs.group = NULL; lResult=lJavaVM->AttachCurrentThread(&lJNIEnv, &lJavaVMAttachArgs); if (lResult == JNI_ERR) { return 0; } // Retrieves NativeActivity. jobject lNativeActivity = engine.app->activity->clazz; jclass ClassNativeActivity = lJNIEnv->GetObjectClass(lNativeActivity); jmethodID MethodSetFlags = lJNIEnv->GetMethodID( ClassNativeActivity, "getKeyText", "(JJIIIIIII)I"); lprintf( "..." ); if( MethodSetFlags ) result = lJNIEnv->CallIntMethod( lNativeActivity, MethodSetFlags , AKeyEvent_getDownTime(event) , AKeyEvent_getEventTime(event) , AKeyEvent_getAction(event) , AKeyEvent_getKeyCode(event) , AKeyEvent_getRepeatCount(event) , AKeyEvent_getMetaState(event) , AInputEvent_getDeviceId(event) , AKeyEvent_getScanCode(event) , AKeyEvent_getFlags(event) ); else { lprintf( "Failed to get method." ); result = 0; } // Finished with the JVM. lJavaVM->DetachCurrentThread(); return result; }
bool InputService::onKeyboardEvent(AInputEvent* pEvent) { #ifdef INPUTSERVICE_LOG_EVENTS packt_Log_debug("AKeyEvent_getAction=%d", AKeyEvent_getAction(pEvent)); packt_Log_debug("AKeyEvent_getFlags=%d", AKeyEvent_getFlags(pEvent)); packt_Log_debug("AKeyEvent_getKeyCode=%d", AKeyEvent_getKeyCode(pEvent)); packt_Log_debug("AKeyEvent_getScanCode=%d", AKeyEvent_getScanCode(pEvent)); packt_Log_debug("AKeyEvent_getMetaState=%d", AKeyEvent_getMetaState(pEvent)); packt_Log_debug("AKeyEvent_getRepeatCount=%d", AKeyEvent_getRepeatCount(pEvent)); packt_Log_debug("AKeyEvent_getDownTime=%lld", AKeyEvent_getDownTime(pEvent)); packt_Log_debug("AKeyEvent_getEventTime=%lld", AKeyEvent_getEventTime(pEvent)); #endif const float ORTHOGONAL_MOVE = 1.0f; if (AKeyEvent_getAction(pEvent) == AKEY_EVENT_ACTION_DOWN) { switch (AKeyEvent_getKeyCode(pEvent)) { case AKEYCODE_DPAD_LEFT: mHorizontal = -ORTHOGONAL_MOVE; break; case AKEYCODE_DPAD_RIGHT: mHorizontal = ORTHOGONAL_MOVE; break; case AKEYCODE_DPAD_DOWN: mVertical = -ORTHOGONAL_MOVE; break; case AKEYCODE_DPAD_UP: mVertical = ORTHOGONAL_MOVE; break; case AKEYCODE_BACK: return false; } } else { switch (AKeyEvent_getKeyCode(pEvent)) { case AKEYCODE_DPAD_LEFT: case AKEYCODE_DPAD_RIGHT: mHorizontal = 0.0f; break; case AKEYCODE_DPAD_DOWN: case AKEYCODE_DPAD_UP: mVertical = 0.0f; break; case AKEYCODE_MENU: mMenuKey = true; break; case AKEYCODE_BACK: return false; } } return true; }
static int32_t handle_input(struct android_app* app, AInputEvent* event) { /* app->userData is available here */ if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { app_has_focus = true; return 1; } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { LOGI("Key event: action=%d keyCode=%d metaState=0x%x", AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event), AKeyEvent_getMetaState(event)); } return 0; }
static int32_t engine_handle_input(struct android_app *app, AInputEvent * event) { struct engine *engine = (struct engine *) app->userData; if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { // engine->animating = 1; return 1; } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { LOGI("Key event: action=%d keyCode=%d metaState=0x%x", AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event), AKeyEvent_getMetaState(event)); } return 0; }
int32_t on_key_event(android_app *app, AInputEvent *event) { user_data *p = (user_data *)app->userData; game_controller_input *old_keyboard_controller = GetController(p->old_input, 0); game_controller_input *new_keyboard_controller = GetController(p->new_input, 0); uint action = AKeyEvent_getAction(event); if (action == AKEY_EVENT_ACTION_MULTIPLE) { return 1; } bool32 is_down = action == AKEY_EVENT_ACTION_DOWN; int keycode = AKeyEvent_getKeyCode(event); int meta_state = AKeyEvent_getMetaState(event); if (keycode == 4) { return 0; } else if ((keycode == 51) || (keycode == 19)) { process_keyboard_message(&new_keyboard_controller->MoveUp, is_down); } else if ((keycode == 29) || (keycode == 21)) { process_keyboard_message(&new_keyboard_controller->MoveLeft, is_down); } else if ((keycode == 47) || (keycode == 20)) { process_keyboard_message(&new_keyboard_controller->MoveDown, is_down); } else if ((keycode == 32) || (keycode == 22)) { process_keyboard_message(&new_keyboard_controller->MoveRight, is_down); } else { __android_log_print(ANDROID_LOG_INFO, p->app_name, "key event: down %d, keycode %d, meta_state %x", is_down, keycode, meta_state); } return 1; }
bool OuyaInputView::dispatchKeyEvent(AInputEvent* keyEvent) { int64_t downTime = AKeyEvent_getDownTime(keyEvent); int64_t eventTime = AKeyEvent_getEventTime(keyEvent); int32_t action = AKeyEvent_getAction(keyEvent); int32_t code = AKeyEvent_getKeyCode(keyEvent); int32_t repeat = AKeyEvent_getRepeatCount(keyEvent); int32_t metaState = AKeyEvent_getMetaState(keyEvent); int32_t deviceId = AInputEvent_getDeviceId(keyEvent); int32_t scancode = AKeyEvent_getScanCode(keyEvent); int32_t flags = AKeyEvent_getFlags(keyEvent); int32_t source = AInputEvent_getSource(keyEvent); #if ENABLE_VERBOSE_LOGGING __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "downTime=%lld eventTime=%lld action=%d code=%d repeat=%d metaState=%d deviceId=%d scancode=%d flags=%d source=%d", downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, flags, source); #endif return javaDispatchKeyEvent(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, flags, source); }
bool AndroidGameApplication::onKeyEvent(AInputEvent* event) { int32 action = AKeyEvent_getAction(event); int32 flags = AKeyEvent_getFlags(event); int32 keyCode = AKeyEvent_getKeyCode(event); int32 scanCode = AKeyEvent_getScanCode(event); int32 state = AKeyEvent_getMetaState(event); int32 repeatCount = AKeyEvent_getRepeatCount(event); LOGI("AndroidGameApplication::onKeyEvent"); switch(action) { case AKEY_EVENT_ACTION_DOWN: { LOGI("AKEY_EVENT_ACTION_DOWN [keyCode = %d, scanCode = %d, repeatCount = %d, flags = 0x%X, state = 0x%X]", keyCode, scanCode, repeatCount, flags, state); for(std::list<IKeyboardController*>::iterator it = m_keyboardInputHandlers.begin(); it != m_keyboardInputHandlers.end(); ++it) { (*it)->onKeyDown(keyCode, flags); } } break; case AKEY_EVENT_ACTION_UP: { LOGI("AKEY_EVENT_ACTION_DOWN [keyCode = %d, scanCode = %d, repeatCount = %d, flags = 0x%X, state = 0x%X]", keyCode, scanCode, repeatCount, flags, state); for(std::list<IKeyboardController*>::iterator it = m_keyboardInputHandlers.begin(); it != m_keyboardInputHandlers.end(); ++it) { (*it)->onKeyUp(keyCode, flags); } } break; } return true; }
static int32_t handle_input(struct android_app* app, AInputEvent* event) { /* app->userData is available here */ if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { app_has_focus = true; // LOGI("Motion Event, pressed = %d", (pressed?1:0)); for (int i=0; i< AMotionEvent_getPointerCount(event); ++i) { int action = AMotionEvent_getAction(event); // LOGI("Motion Event, action = %d", action); if ((action & AMOTION_EVENT_ACTION_DOWN) ||(action & AMOTION_EVENT_ACTION_POINTER_DOWN)) { //LOGI("In"); if (!pressed) { pressed = true; float px = AMotionEvent_getX(event, i); float py = AMotionEvent_getY(event, i); cd->clickat(px,py); } } else pressed = false; } return 1; } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { LOGI("Key event: action=%d keyCode=%d metaState=0x%x", AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event), AKeyEvent_getMetaState(event)); } return 0; }
static void _log_event(AInputEvent *event) { if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { LOGD("DEBUG: Key event: action=%d, keycode=%d, meta=0x%x, source=%d", AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event), AKeyEvent_getMetaState(event), (int)(AInputEvent_getSource(event))); } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { LOGD("DEBUG: Motion event: deviceid=%d, pointerid=%d, sourceid=%d, action=%d, keycode=%d" "x,y=%f,%f, raw x,y=%f,%f, source=%d", (int)AInputEvent_getDeviceId(event), (int)AMotionEvent_getPointerId(event, 0), (int)AInputEvent_getSource(event), (int)AKeyEvent_getAction(event), (int)AKeyEvent_getKeyCode(event), (float)AMotionEvent_getX(event,0), (float)AMotionEvent_getY(event,0), (float)AMotionEvent_getRawX(event,0), (float)AMotionEvent_getRawY(event,0), (int)(AInputEvent_getSource(event))); } else { LOGD("DEBUG: other type of event, %d", AInputEvent_getType(event)); } }
/** * Process the next input event. */ static int32_t engine_handle_input( struct android_app* app, AInputEvent* event ) { JNIEnv *jni; (*jVM)->AttachCurrentThread(jVM, &jni, NULL); struct ENGINE* engine = (struct ENGINE*)app->userData; if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY){ int device = AInputEvent_getDeviceId(event); int action = AKeyEvent_getAction(event); int keyCode = AKeyEvent_getKeyCode(event); if(jni && g_pActivity){ if((*jni)->ExceptionCheck(jni)) { (*jni)->ExceptionDescribe(jni); (*jni)->ExceptionClear(jni); } (*jni)->CallIntMethod(jni, g_pActivity, javaOnNDKKey, device, keyCode, action, AKeyEvent_getMetaState(event)); if (!(keyCode == AKEYCODE_MENU || keyCode == AKEYCODE_BACK || keyCode == AKEYCODE_BUTTON_THUMBR || keyCode == AKEYCODE_VOLUME_UP || keyCode == AKEYCODE_VOLUME_DOWN || keyCode == AKEYCODE_BUTTON_SELECT)) { return 1; } } } else if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION ) { int device = AInputEvent_getDeviceId( event ); int nSourceId = AInputEvent_getSource( event ); int nPointerCount = AMotionEvent_getPointerCount( event ); int n; jboolean newTouch = JNI_TRUE; for( n = 0 ; n < nPointerCount ; ++n ) { int nPointerId = AMotionEvent_getPointerId( event, n ); int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event ); int nRawAction = AMotionEvent_getAction( event ); struct TOUCHSTATE *touchstate = 0; if( nSourceId == AINPUT_SOURCE_TOUCHPAD ) { touchstate = engine->touchstate_pad; } else { touchstate = engine->touchstate_screen; } if( nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP ) { int nPointerIndex = (AMotionEvent_getAction( event ) & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; nPointerId = AMotionEvent_getPointerId( event, nPointerIndex ); } if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN ) { touchstate[nPointerId].down = 1; } else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP || nAction == AMOTION_EVENT_ACTION_CANCEL ) { touchstate[nPointerId].down = 0; } if (touchstate[nPointerId].down == 1) { touchstate[nPointerId].x = AMotionEvent_getX( event, n ); touchstate[nPointerId].y = AMotionEvent_getY( event, n ); } if( jni && g_pActivity && isXperiaPlay) { // (*jni)->CallVoidMethod( jni, g_pActivity, javaOnNDKTouch, device, nSourceId, nRawAction, touchstate[nPointerId].x, touchstate[nPointerId].y, newTouch); (*jni)->CallVoidMethod( jni, g_pActivity, javaOnNDKTouch, device, nSourceId, nRawAction, touchstate[nPointerId].x, touchstate[nPointerId].y); } newTouch = JNI_FALSE; }
s32 CIrrDeviceAndroid::handleInput(android_app* app, AInputEvent* androidEvent) { CIrrDeviceAndroid* device = (CIrrDeviceAndroid*)app->userData; s32 status = 0; switch ( AInputEvent_getType(androidEvent) ) { case AINPUT_EVENT_TYPE_MOTION: { SEvent event; event.EventType = EET_TOUCH_INPUT_EVENT; s32 eventAction = AMotionEvent_getAction(androidEvent); s32 eventType = eventAction & AMOTION_EVENT_ACTION_MASK; #if 0 // Useful for debugging. We might have to pass some of those infos on at some point. // but preferably device independent (so iphone can use same irrlicht flags). int32_t flags = AMotionEvent_getFlags(androidEvent); os::Printer::log("flags: ", core::stringc(flags).c_str(), ELL_DEBUG); int32_t metaState = AMotionEvent_getMetaState(androidEvent); os::Printer::log("metaState: ", core::stringc(metaState).c_str(), ELL_DEBUG); int32_t edgeFlags = AMotionEvent_getEdgeFlags(androidEvent); os::Printer::log("edgeFlags: ", core::stringc(flags).c_str(), ELL_DEBUG); #endif bool touchReceived = true; switch (eventType) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_POINTER_DOWN: event.TouchInput.Event = ETIE_PRESSED_DOWN; break; case AMOTION_EVENT_ACTION_MOVE: event.TouchInput.Event = ETIE_MOVED; break; case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_POINTER_UP: case AMOTION_EVENT_ACTION_CANCEL: event.TouchInput.Event = ETIE_LEFT_UP; break; default: touchReceived = false; break; } if (touchReceived) { // Process all touches for move action. if (event.TouchInput.Event == ETIE_MOVED) { s32 pointerCount = AMotionEvent_getPointerCount(androidEvent); for (s32 i = 0; i < pointerCount; ++i) { event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, i); event.TouchInput.X = AMotionEvent_getX(androidEvent, i); event.TouchInput.Y = AMotionEvent_getY(androidEvent, i); device->postEventFromUser(event); } } else // Process one touch for other actions. { s32 pointerIndex = (eventAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, pointerIndex); event.TouchInput.X = AMotionEvent_getX(androidEvent, pointerIndex); event.TouchInput.Y = AMotionEvent_getY(androidEvent, pointerIndex); device->postEventFromUser(event); } status = 1; } } break; case AINPUT_EVENT_TYPE_KEY: { SEvent event; event.EventType = EET_KEY_INPUT_EVENT; int32_t keyCode = AKeyEvent_getKeyCode(androidEvent); // os::Printer::log("keyCode: ", core::stringc(keyCode).c_str(), ELL_DEBUG); int32_t keyAction = AKeyEvent_getAction(androidEvent); int32_t keyMetaState = AKeyEvent_getMetaState(androidEvent); if ( keyCode >= 0 && (u32)keyCode < device->KeyMap.size() ) event.KeyInput.Key = device->KeyMap[keyCode]; else event.KeyInput.Key = KEY_UNKNOWN; event.KeyInput.SystemKeyCode = (u32)keyCode; if ( keyAction == AKEY_EVENT_ACTION_DOWN ) event.KeyInput.PressedDown = true; else if ( keyAction == AKEY_EVENT_ACTION_UP ) event.KeyInput.PressedDown = false; else if ( keyAction == AKEY_EVENT_ACTION_MULTIPLE ) { // TODO: Multiple duplicate key events have occurred in a row, // or a complex string is being delivered. The repeat_count // property of the key event contains the number of times the // given key code should be executed. // I guess this might necessary for more complicated i18n key input, // but don't see yet how to handle this correctly. } /* no use for meta keys so far. if ( keyMetaState & AMETA_ALT_ON || keyMetaState & AMETA_ALT_LEFT_ON || keyMetaState & AMETA_ALT_RIGHT_ON ) ; // what is a sym? if ( keyMetaState & AMETA_SYM_ON ) ; */ if ( keyMetaState & AMETA_SHIFT_ON || keyMetaState & AMETA_SHIFT_LEFT_ON || keyMetaState & AMETA_SHIFT_RIGHT_ON ) event.KeyInput.Shift = true; else event.KeyInput.Shift = false; event.KeyInput.Control = false; // Having memory allocations + going through JNI for each key-press is pretty bad (slow). // So we do it only for those keys which are likely text-characters and avoid it for all other keys. // So it's fast for keys like game controller input and special keys. And text keys are typically // only used or entering text and not for gaming on Android, so speed likely doesn't matter there too much. if ( event.KeyInput.Key > 0 ) { // TODO: // Not sure why we have to attach a JNIEnv here, but it won't work when doing that in the constructor or // trying to use the activity->env. My best guess is that the event-handling happens in an own thread. // It means JNIEnvAttachedToVM will never get detached as I don't know a safe way where to do that // (we could attach & detach each time, but that would probably be slow) // Also - it has to be each time as it get's invalid when the application mode changes. if ( device->Initialized && device->Android && device->Android->activity && device->Android->activity->vm ) { JavaVMAttachArgs attachArgs; attachArgs.version = JNI_VERSION_1_6; attachArgs.name = 0; attachArgs.group = NULL; // Not a big problem calling it each time - it's a no-op when the thread already is attached. // And we have to do that as someone else can have detached the thread in the meantime. jint result = device->Android->activity->vm->AttachCurrentThread(&device->JNIEnvAttachedToVM, &attachArgs); if(result == JNI_ERR) { os::Printer::log("AttachCurrentThread for the JNI environment failed.", ELL_WARNING); device->JNIEnvAttachedToVM = 0; } if ( device->JNIEnvAttachedToVM ) { jni::CKeyEventWrapper * keyEventWrapper = new jni::CKeyEventWrapper(device->JNIEnvAttachedToVM, keyAction, keyCode); event.KeyInput.Char = keyEventWrapper->getUnicodeChar(keyMetaState); delete keyEventWrapper; } } if ( event.KeyInput.Key == KEY_BACK ) { event.KeyInput.Char = 0x08; // same key-code as on other operating systems. Otherwise we have to handle too much system specific stuff in the editbox. } //os::Printer::log("char-code: ", core::stringc((int)event.KeyInput.Char).c_str(), ELL_DEBUG); } else { // os::Printer::log("keyCode: ", core::stringc(keyCode).c_str(), ELL_DEBUG); event.KeyInput.Char = 0; } device->postEventFromUser(event); } break; default: break; } return status; }
int32_t onKey(const int32_t& action, void* data) { auto event = static_cast<AInputEvent*>(data); const int32_t metakey = AKeyEvent_getMetaState(event); const int32_t key = AKeyEvent_getKeyCode(event); const int32_t scanCode = AKeyEvent_getScanCode(event); int jopKey = Input::getJopKey(key); const int mod = ((metakey & AMETA_SHIFT_ON) != 0) * Keyboard::Modifier::Shift | ((metakey & AMETA_ALT_ON) != 0) * Keyboard::Modifier::Alt | ((metakey & AMETA_CTRL_ON) != 0) * Keyboard::Modifier::Control; int32_t device = AInputEvent_getSource(event); auto& windowRef = Engine::getMainWindow(); switch (action) { case AKEY_EVENT_ACTION_DOWN: { auto state = ActivityState::get(); if (jopKey != Keyboard::Unknown) { state->activeKey = jopKey; windowRef.getEventHandler()->keyPressed(jopKey, scanCode, mod); } if (Controller::controllersPresent()) { jopKey = Input::getJopControllerButton(key); if (jopKey != Controller::XBox::Unknown) { state->activeControllerButtons[jopKey] = true; windowRef.getEventHandler()->controllerButtonPressed(0, jopKey); } } return 1; } case AKEY_EVENT_ACTION_UP: { auto state = ActivityState::get(); if (jopKey != Keyboard::Unknown) { state->activeKey = -1; windowRef.getEventHandler()->keyReleased(jopKey, scanCode, mod); } if (Controller::controllersPresent()) { jopKey = Input::getJopControllerButton(key); if (jopKey != Controller::XBox::Unknown) { state->activeControllerButtons[jopKey] = false; windowRef.getEventHandler()->controllerButtonReleased(0, jopKey); } } return 1; } case AKEY_EVENT_ACTION_MULTIPLE: { if (jopKey != 0) { windowRef.getEventHandler()->keyPressed(jopKey, scanCode, mod); windowRef.getEventHandler()->keyReleased(jopKey, scanCode, mod); } if (Controller::controllersPresent()) { jopKey = Input::getJopControllerButton(key); if (jopKey != Controller::XBox::Unknown) { windowRef.getEventHandler()->controllerButtonPressed(0, jopKey); windowRef.getEventHandler()->controllerButtonReleased(0, jopKey); } } return 1; } } return 0; }
int WindowImplAndroid::processKeyEvent(AInputEvent* _event, ActivityStates* states) { int32_t device = AInputEvent_getSource(_event); int32_t action = AKeyEvent_getAction(_event); int32_t key = AKeyEvent_getKeyCode(_event); int32_t metakey = AKeyEvent_getMetaState(_event); Event event; event.key.code = androidKeyToSF(key); event.key.alt = metakey & AMETA_ALT_ON; event.key.control = false; event.key.shift = metakey & AMETA_SHIFT_ON; switch (action) { case AKEY_EVENT_ACTION_DOWN: event.type = Event::KeyPressed; forwardEvent(event); return 1; case AKEY_EVENT_ACTION_UP: event.type = Event::KeyReleased; forwardEvent(event); if (int unicode = getUnicode(_event)) { event.type = Event::TextEntered; event.text.unicode = unicode; forwardEvent(event); } return 1; case AKEY_EVENT_ACTION_MULTIPLE: // Since complex inputs don't get separate key down/up events // both have to be faked at once event.type = Event::KeyPressed; forwardEvent(event); event.type = Event::KeyReleased; forwardEvent(event); // This requires some special treatment, since this might represent // a repetition of key presses or a complete sequence if (key == AKEYCODE_UNKNOWN) { // This is a unique sequence, which is not yet exposed in the NDK // http://code.google.com/p/android/issues/detail?id=33998 return 0; } else if (int unicode = getUnicode(_event)) // This is a repeated sequence { event.type = Event::TextEntered; event.text.unicode = unicode; int32_t repeats = AKeyEvent_getRepeatCount(_event); for (int32_t i = 0; i < repeats; ++i) forwardEvent(event); return 1; } break; } return 0; }
static void android_input_poll(void *data) { (void)data; RARCH_PERFORMANCE_INIT(input_poll); RARCH_PERFORMANCE_START(input_poll); bool debug_enable = g_settings.input.debug_enable; struct android_app* android_app = (struct android_app*)g_android; uint64_t *lifecycle_state = &g_extern.lifecycle_state; *lifecycle_state &= ~((1ULL << RARCH_RESET) | (1ULL << RARCH_REWIND) | (1ULL << RARCH_FAST_FORWARD_KEY) | (1ULL << RARCH_FAST_FORWARD_HOLD_KEY) | (1ULL << RARCH_MUTE) | (1ULL << RARCH_SAVE_STATE_KEY) | (1ULL << RARCH_LOAD_STATE_KEY) | (1ULL << RARCH_STATE_SLOT_PLUS) | (1ULL << RARCH_STATE_SLOT_MINUS)); // Read all pending events. while (AInputQueue_hasEvents(android_app->inputQueue) > 0) { AInputEvent* event = NULL; if (AInputQueue_getEvent(android_app->inputQueue, &event) < 0) break; bool long_msg_enable = false; int32_t handled = 1; int action = 0; char msg[128]; msg[0] = 0; int source = AInputEvent_getSource(event); int id = AInputEvent_getDeviceId(event); if (id == zeus_second_id) id = zeus_id; int keycode = AKeyEvent_getKeyCode(event); int type_event = AInputEvent_getType(event); int state_id = -1; if (source & (AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD)) state_id = 0; // touch overlay is always player 1 else { for (unsigned i = 0; i < pads_connected; i++) if (state_device_ids[i] == id) state_id = i; } if (state_id < 0) { state_id = pads_connected; state_device_ids[pads_connected++] = id; input_autodetect_setup(android_app, msg, sizeof(msg), state_id, id, source); long_msg_enable = true; } if (keycode == AKEYCODE_BACK ) { int meta = AKeyEvent_getMetaState(event); if (!(meta & AMETA_ALT_ON)) { *lifecycle_state |= (1ULL << RARCH_QUIT_KEY); AInputQueue_finishEvent(android_app->inputQueue, event, handled); break; } } if (type_event == AINPUT_EVENT_TYPE_MOTION) { float x = 0.0f; float y = 0.0f; action = AMotionEvent_getAction(event); size_t motion_pointer = action >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; action &= AMOTION_EVENT_ACTION_MASK; if (source & ~(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_MOUSE)) { if (g_settings.input.dpad_emulation[state_id] != DPAD_EMULATION_NONE) { uint64_t *state_cur = &state[state_id]; x = AMotionEvent_getX(event, motion_pointer); y = AMotionEvent_getY(event, motion_pointer); *state_cur &= ~((1ULL << RETRO_DEVICE_ID_JOYPAD_LEFT) | (1ULL << RETRO_DEVICE_ID_JOYPAD_RIGHT) | (1ULL << RETRO_DEVICE_ID_JOYPAD_UP) | (1ULL << RETRO_DEVICE_ID_JOYPAD_DOWN)); *state_cur |= PRESSED_LEFT(x, y) ? (1ULL << RETRO_DEVICE_ID_JOYPAD_LEFT) : 0; *state_cur |= PRESSED_RIGHT(x, y) ? (1ULL << RETRO_DEVICE_ID_JOYPAD_RIGHT) : 0; *state_cur |= PRESSED_UP(x, y) ? (1ULL << RETRO_DEVICE_ID_JOYPAD_UP) : 0; *state_cur |= PRESSED_DOWN(x, y) ? (1ULL << RETRO_DEVICE_ID_JOYPAD_DOWN) : 0; } } else { bool keyup = (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL || action == AMOTION_EVENT_ACTION_POINTER_UP) || (source == AINPUT_SOURCE_MOUSE && action != AMOTION_EVENT_ACTION_DOWN); if (keyup && motion_pointer < MAX_TOUCH) { memmove(pointer + motion_pointer, pointer + motion_pointer + 1, (MAX_TOUCH - motion_pointer - 1) * sizeof(struct input_pointer)); if (pointer_count > 0) pointer_count--; } else { int pointer_max = min(AMotionEvent_getPointerCount(event), MAX_TOUCH); for (motion_pointer = 0; motion_pointer < pointer_max; motion_pointer++) { x = AMotionEvent_getX(event, motion_pointer); y = AMotionEvent_getY(event, motion_pointer); input_translate_coord_viewport(x, y, &pointer[motion_pointer].x, &pointer[motion_pointer].y, &pointer[motion_pointer].full_x, &pointer[motion_pointer].full_y); pointer_count = max(pointer_count, motion_pointer + 1); } } } if (debug_enable) snprintf(msg, sizeof(msg), "Pad %d : x = %.2f, y = %.2f, src %d.\n", state_id, x, y, source); } else if (type_event == AINPUT_EVENT_TYPE_KEY)
/** * Process the next input event. */ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { struct engine* engine = (struct engine*)app->userData; switch(AInputEvent_getType(event)) { case AINPUT_EVENT_TYPE_MOTION: { int32_t action = AMotionEvent_getAction(event ); int pointer = ( action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK ) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; int p = AMotionEvent_getPointerCount( event ); #ifdef DEBUG_TOUCH_INPUT LOGI( "pointer (full action)%04x (pointer)%d (number points)%d", action, pointer, p ); #endif { int n; for( n = 0; n < engine->nPoints; n++ ) { engine->points[n].flags.new_event = 0; if( engine->points[n].flags.end_event ) { int m; for( m = n; m < (engine->nPoints-1); m++ ) { if( engine->input_point_map[m+1] == m+1 ) engine->input_point_map[m] = m; else { if( engine->input_point_map[m+1] < n ) engine->input_point_map[m] = engine->input_point_map[m+1]; else engine->input_point_map[m] = engine->input_point_map[m+1] - 1; } engine->points[m] = engine->points[m+1]; } engine->nPoints--; n--; } } } switch( action & AMOTION_EVENT_ACTION_MASK ) { case AMOTION_EVENT_ACTION_DOWN: // primary pointer down. //if( engine->nPoints ) //{ // LOGI( "Pointer Event Down (pointer0) and there's already pointers..." ); //} engine->points[0].x = AMotionEvent_getX( event, pointer ); engine->points[0].y = AMotionEvent_getY( event, pointer ); engine->points[0].flags.new_event = 1; engine->points[0].flags.end_event = 0; engine->nPoints++; engine->input_point_map[0] = 0; break; case AMOTION_EVENT_ACTION_UP: // primary pointer up. engine->points[0].flags.new_event = 0; engine->points[0].flags.end_event = 1; break; case AMOTION_EVENT_ACTION_MOVE: { int n; for( n = 0; n < p; n++ ) { // points may have come in as 'new' in the wrong order, // reference the input point map to fill in the correct point location int actual = engine->input_point_map[n]; engine->points[actual].x = AMotionEvent_getX( event, n ); engine->points[actual].y = AMotionEvent_getY( event, n ); engine->points[actual].flags.new_event = 0; engine->points[actual].flags.end_event = 0; } } break; case AMOTION_EVENT_ACTION_POINTER_DOWN: // the new pointer might not be the last one, so we insert it. // at the end, before dispatch, new points are moved to the end // and mapping begins. this code should not reference the map if( pointer < engine->nPoints ) { int c; #ifdef DEBUG_TOUCH_INPUT LOGI( "insert point new. %d", engine->nPoints-1 ); #endif for( c = engine->nPoints; c >= pointer; c-- ) { #ifdef DEBUG_TOUCH_INPUT LOGI( "Set %d to %d", c, engine->input_point_map[c-1] ); #endif engine->input_point_map[c] = engine->input_point_map[c-1]; // save still in the same target... } #ifdef DEBUG_TOUCH_INPUT LOGI( "Set %d to %d", pointer, engine->nPoints ); #endif engine->input_point_map[pointer] = engine->nPoints; // and the new one maps to the last. // now just save in last and don't swap twice. engine->points[engine->nPoints].x = AMotionEvent_getX( event, pointer ); engine->points[engine->nPoints].y = AMotionEvent_getY( event, pointer ); pointer = engine->nPoints; } else { engine->points[pointer].x = AMotionEvent_getX( event, pointer ); engine->points[pointer].y = AMotionEvent_getY( event, pointer ); engine->input_point_map[pointer] = pointer; } // primary pointer down. engine->points[pointer].flags.new_event = 1; engine->points[pointer].flags.end_event = 0; // always initialize the engine->nPoints++; break; case AMOTION_EVENT_ACTION_POINTER_UP: { // a up pointer may be remapped already, set the actual entry for the point int actual = engine->input_point_map[pointer]; int n; engine->points[actual].flags.new_event = 0; engine->points[actual].flags.end_event = 1; #ifdef DEBUG_TOUCH_INPUT LOGI( "Set point %d (map %d) to ended", pointer, actual ); #endif // any release event will reset the other input points appropriately(?) for( n = 0; n < engine->nPoints; n++ ) { int other; if( engine->input_point_map[n] != n ) { int m; #ifdef DEBUG_TOUCH_INPUT LOGI( "reorder to natural input order" ); #endif memcpy( engine->tmp_points, engine->points, engine->nPoints * sizeof( struct input_point ) ); // m is the point currently mapped to this position. // data from engine[n] and engine[m] need to swap for( m = 0; m < engine->nPoints; m++ ) { engine->points[m] = engine->tmp_points[other = engine->input_point_map[m]]; engine->input_point_map[m] = m; #ifdef DEBUG_TOUCH_INPUT LOGI( "move point %d to %d", other, m ); #endif } break; } } } break; default: #ifdef DEBUG_TOUCH_INPUT LOGI( "Motion Event ignored..." ); #endif break; } { int n; #ifdef DEBUG_TOUCH_INPUT for( n = 0; n < engine->nPoints; n++ ) { LOGI( "Point : %d %d %g %g %d %d", n, engine->input_point_map[n], engine->points[n].x , engine->points[n].y, engine->points[n].flags.new_event, engine->points[n].flags.end_event ); } #endif } BagVidlibPureglSendTouchEvents( engine->nPoints, engine->points ); //engine->state.animating = 1; //engine->state.x = AMotionEvent_getX(event, 0); //engine->state.y = AMotionEvent_getY(event, 0); return 1; } case AINPUT_EVENT_TYPE_KEY: { int32_t key_val = AKeyEvent_getKeyCode(event); //int32_t key_char = AKeyEvent_getKeyChar(event); int32_t key_mods = AKeyEvent_getMetaState( event ); int32_t key_pressed = AKeyEvent_getAction( event ); int realmod = 0; //lprintf( "key char is %d (%c)", key_char, key_char ); if( ( key_mods & 0x3000 ) == 0x3000 ) realmod |= KEY_MOD_CTRL; if( ( key_mods & 0x12 ) == 0x12 ) realmod |= KEY_MOD_ALT; if( ( key_mods & 0x41 ) == 0x41 ) realmod |= KEY_MOD_SHIFT; key_mods = realmod; #ifdef DEBUG_KEY_INPUT LOGI("Received key event: %d %d %d\n", key_pressed, key_val, key_mods ); #endif { engine->key_text = AndroidGetKeyText( event ); lprintf( "Event translates to %d(%04x)%c", engine->key_text, engine->key_text ,engine->key_text ); } if( key_val ) { int used; if( key_pressed == AKEY_EVENT_ACTION_MULTIPLE ) { int count = AKeyEvent_getRepeatCount( event ); int n; for( n = 0; n < count; n++ ) { used = BagVidlibPureglSendKeyEvents( 1, key_val, key_mods ); used = BagVidlibPureglSendKeyEvents( 0, key_val, key_mods ); } } else used = BagVidlibPureglSendKeyEvents( (key_pressed==AKEY_EVENT_ACTION_DOWN)?1:0, key_val, key_mods ); return used; } break; } default: LOGI( "Unhandled Motion Event ignored..." ); break; } return 0; }
bool CAndroidKey::onKeyboardEvent(AInputEvent* event) { CXBMCApp::android_printf("%s", __PRETTY_FUNCTION__); if (event == NULL) return false; int32_t keycode = AKeyEvent_getKeyCode(event); int32_t flags = AKeyEvent_getFlags(event); int32_t state = AKeyEvent_getMetaState(event); int32_t repeatCount = AKeyEvent_getRepeatCount(event); // Check if we got some special key uint16_t sym = XBMCK_UNKNOWN; for (unsigned int index = 0; index < sizeof(keyMap) / sizeof(KeyMap); index++) { if (keycode == keyMap[index].nativeKey) { sym = keyMap[index].xbmcKey; break; } } // check if this is a key we don't want to handle if (sym == XBMCK_LAST) return false; uint16_t modifiers = 0; if (state & AMETA_ALT_LEFT_ON) modifiers |= XBMCKMOD_LALT; if (state & AMETA_ALT_RIGHT_ON) modifiers |= XBMCKMOD_RALT; if (state & AMETA_SHIFT_LEFT_ON) modifiers |= XBMCKMOD_LSHIFT; if (state & AMETA_SHIFT_RIGHT_ON) modifiers |= XBMCKMOD_RSHIFT; /* TODO: if (state & AMETA_SYM_ON) modifiers |= 0x000?;*/ switch (AKeyEvent_getAction(event)) { case AKEY_EVENT_ACTION_DOWN: CXBMCApp::android_printf("CXBMCApp: key down (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeatCount, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); XBMC_Key((uint8_t)keycode, sym, modifiers, false); return true; case AKEY_EVENT_ACTION_UP: CXBMCApp::android_printf("CXBMCApp: key up (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeatCount, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); XBMC_Key((uint8_t)keycode, sym, modifiers, true); return true; case AKEY_EVENT_ACTION_MULTIPLE: CXBMCApp::android_printf("CXBMCApp: key multiple (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeatCount, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); break; default: CXBMCApp::android_printf("CXBMCApp: unknown key (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeatCount, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); break; } return false; }
bool CAndroidKey::onKeyboardEvent(AInputEvent *event) { if (event == NULL) return false; int32_t flags = AKeyEvent_getFlags(event); int32_t state = AKeyEvent_getMetaState(event); int32_t action = AKeyEvent_getAction(event); int32_t repeat = AKeyEvent_getRepeatCount(event); int32_t keycode = AKeyEvent_getKeyCode(event); int32_t deviceId = AInputEvent_getDeviceId(event); CJNIKeyCharacterMap map = CJNIKeyCharacterMap::load(deviceId); uint16_t unicode = map.get(keycode, state); // Check if we got some special key uint16_t sym = XBMCK_UNKNOWN; for (unsigned int index = 0; index < sizeof(keyMap) / sizeof(KeyMap); index++) { if (keycode == keyMap[index].nativeKey) { sym = keyMap[index].xbmcKey; break; } } if (m_handleMediaKeys) { for (unsigned int index = 0; index < sizeof(MediakeyMap) / sizeof(KeyMap); index++) { if (keycode == MediakeyMap[index].nativeKey) { sym = MediakeyMap[index].xbmcKey; break; } } } // check if this is a key we don't want to handle if (sym == XBMCK_LAST || sym == XBMCK_UNKNOWN) { CXBMCApp::android_printf("CAndroidKey: key ignored (code: %d)", keycode); return false; } uint16_t modifiers = 0; if (state & AMETA_ALT_LEFT_ON) modifiers |= XBMCKMOD_LALT; if (state & AMETA_ALT_RIGHT_ON) modifiers |= XBMCKMOD_RALT; if (state & AMETA_SHIFT_LEFT_ON) modifiers |= XBMCKMOD_LSHIFT; if (state & AMETA_SHIFT_RIGHT_ON) modifiers |= XBMCKMOD_RSHIFT; if (state & AMETA_CTRL_LEFT_ON) modifiers |= XBMCKMOD_LCTRL; if (state & AMETA_CTRL_RIGHT_ON) modifiers |= XBMCKMOD_RCTRL; /* TODO: if (state & AMETA_SYM_ON) modifiers |= 0x000?;*/ switch (action) { case AKEY_EVENT_ACTION_DOWN: #if 1 CXBMCApp::android_printf("CAndroidKey: key down (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeat, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); #endif XBMC_Key((uint8_t)keycode, sym, modifiers, unicode, false); return true; case AKEY_EVENT_ACTION_UP: #if 1 CXBMCApp::android_printf("CAndroidKey: key up (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeat, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); #endif XBMC_Key((uint8_t)keycode, sym, modifiers, unicode, true); return true; case AKEY_EVENT_ACTION_MULTIPLE: #if 1 CXBMCApp::android_printf("CAndroidKey: key multiple (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeat, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); #endif break; default: #if 1 CXBMCApp::android_printf("CAndroidKey: unknown key (code: %d; repeat: %d; flags: 0x%0X; alt: %s; shift: %s; sym: %s)", keycode, repeat, flags, (state & AMETA_ALT_ON) ? "yes" : "no", (state & AMETA_SHIFT_ON) ? "yes" : "no", (state & AMETA_SYM_ON) ? "yes" : "no"); #endif break; } return false; }