#include "Controls.h" #include #include #include #include "Common/Data/Encoding/Utf8.h" #include "Common/Log.h" #include "Common/TimeUtil.h" #include "Common/Input/InputState.h" #include "Common/System/NativeApp.h" #include "Common/System/Display.h" #include "Core/KeyMap.h" static void controllerButtonPressed(BOOL pressed, InputKeyCode keyCode) { KeyInput key; key.deviceId = DEVICE_ID_PAD_0; key.flags = pressed ? KeyInputFlags::DOWN : KeyInputFlags::UP; key.keyCode = keyCode; NativeKey(key); } static void analogTriggerPressed(InputAxis axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.axisId = axis; axisInput.value = value; NativeAxis(&axisInput, 1); } bool InitController(GCController *controller) { GCExtendedGamepad *extendedProfile = controller.extendedGamepad; if (extendedProfile == nil) { return false; } for (GCControllerElement* element in controller.physicalInputProfile.allElements) { element.preferredSystemGestureState = GCSystemGestureStateDisabled; } const std::string controllerName = [controller.vendorName ?: @"Gamepad" UTF8String]; KeyMap::NotifyPadConnected(DEVICE_ID_PAD_0, controllerName); extendedProfile.buttonA.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_2); // Cross }; extendedProfile.buttonB.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_3); // Circle }; extendedProfile.buttonX.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_4); // Square }; extendedProfile.buttonY.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_1); // Triangle }; extendedProfile.leftShoulder.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_L1); // LTrigger }; extendedProfile.rightShoulder.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_R1); // RTrigger }; extendedProfile.dpad.up.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_DPAD_UP); }; extendedProfile.dpad.down.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_DPAD_DOWN); }; extendedProfile.dpad.left.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_DPAD_LEFT); }; extendedProfile.dpad.right.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_DPAD_RIGHT); }; extendedProfile.leftTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { INFO_LOG(Log::System, "ltrigger: %f %d", value, (int)pressed); analogTriggerPressed(JOYSTICK_AXIS_LTRIGGER, value); }; extendedProfile.rightTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { INFO_LOG(Log::System, "rtrigger: %f %d", value, (int)pressed); analogTriggerPressed(JOYSTICK_AXIS_RTRIGGER, value); }; // The optional buttons are nil on controllers that don't have them. if (extendedProfile.leftThumbstickButton != nil) { extendedProfile.leftThumbstickButton.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_THUMBL); }; } if (extendedProfile.rightThumbstickButton != nil) { extendedProfile.rightThumbstickButton.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_THUMBR); }; } if (extendedProfile.buttonOptions != nil) { extendedProfile.buttonOptions.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_SELECT); }; } if (extendedProfile.buttonMenu != nil) { extendedProfile.buttonMenu.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_BUTTON_START); }; } if (extendedProfile.buttonHome != nil) { extendedProfile.buttonHome.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { controllerButtonPressed(pressed, NKCODE_HOME); }; } extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.axisId = JOYSTICK_AXIS_X; axisInput.value = value; NativeAxis(&axisInput, 1); }; extendedProfile.leftThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.axisId = JOYSTICK_AXIS_Y; axisInput.value = -value; NativeAxis(&axisInput, 1); }; // Map right thumbstick as another analog stick, particularly useful for controllers // like the DualShock 3/4 when connected to an iOS device extendedProfile.rightThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.axisId = JOYSTICK_AXIS_Z; axisInput.value = value; NativeAxis(&axisInput, 1); }; extendedProfile.rightThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.axisId = JOYSTICK_AXIS_RZ; axisInput.value = -value; NativeAxis(&axisInput, 1); }; return true; } void ShutdownController(GCController *controller) { for (GCControllerElement* element in controller.physicalInputProfile.allElements) { element.preferredSystemGestureState = GCSystemGestureStateEnabled; } static const InputKeyCode keycodes[] = { NKCODE_BUTTON_1, NKCODE_BUTTON_2, NKCODE_BUTTON_3, NKCODE_BUTTON_4, NKCODE_BUTTON_L1, NKCODE_BUTTON_R1, NKCODE_DPAD_UP, NKCODE_DPAD_DOWN, NKCODE_DPAD_LEFT, NKCODE_DPAD_RIGHT, NKCODE_BUTTON_THUMBL, NKCODE_BUTTON_THUMBR, NKCODE_BUTTON_SELECT, NKCODE_BUTTON_START, NKCODE_HOME }; for (InputKeyCode keyCode : keycodes) { KeyInput key; key.deviceId = DEVICE_ID_PAD_0; key.flags = KeyInputFlags::UP; key.keyCode = keyCode; NativeKey(key); } static const InputAxis axes[] = { JOYSTICK_AXIS_X, JOYSTICK_AXIS_Y, JOYSTICK_AXIS_Z, JOYSTICK_AXIS_RZ, JOYSTICK_AXIS_LTRIGGER, JOYSTICK_AXIS_RTRIGGER }; AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; for (InputAxis axis : axes) { axisInput.axisId = axis; axisInput.value = 0.0f; NativeAxis(&axisInput, 1); } KeyMap::NotifyPadDisconnected(DEVICE_ID_PAD_0); } static void remoteButtonPressed(BOOL pressed, InputKeyCode keyCode) { // As keyboard keys, which the UI already understands (arrows, enter, escape), and which have // default in-game mappings. KeyInput key; key.deviceId = DEVICE_ID_KEYBOARD; key.flags = pressed ? KeyInputFlags::DOWN : KeyInputFlags::UP; key.keyCode = keyCode; NativeKey(key); } // A click on a remote without a separate ring (the 1st generation Siri Remote): an arrow at the // edges of the touch surface, enter in the middle. Handlers run on the main queue, so no locking. struct RemoteClickState { bool clicking = false; InputKeyCode key = NKCODE_UNKNOWN; }; // A click further out than this from the center is an arrow. The surface is -1..1 on each axis. static const float REMOTE_EDGE_CLICK = 0.5f; static void remoteClicked(RemoteClickState *s, GCControllerDirectionPad *touch, BOOL pressed) { if (pressed) { if (s->clicking) { return; } const float x = touch.xAxis.value; const float y = touch.yAxis.value; if (std::max(fabsf(x), fabsf(y)) < REMOTE_EDGE_CLICK) { s->key = NKCODE_ENTER; } else if (fabsf(x) > fabsf(y)) { s->key = x > 0.0f ? NKCODE_DPAD_RIGHT : NKCODE_DPAD_LEFT; } else { s->key = y > 0.0f ? NKCODE_DPAD_UP : NKCODE_DPAD_DOWN; } s->clicking = true; remoteButtonPressed(YES, s->key); } else if (s->clicking) { s->clicking = false; remoteButtonPressed(NO, s->key); } } bool InitSiriRemote(GCController *controller) { GCMicroGamepad *micro = controller.microGamepad; if (micro == nil || controller.extendedGamepad != nil) { return false; } micro.allowsRotation = NO; // Swiping on the touch surface isn't used for now. One swipe step per so much travel fought // with the ring presses. Maybe it could scroll. GCPhysicalInputProfile *profile = controller.physicalInputProfile; GCControllerDirectionPad *ring = profile.dpads[GCInputDirectionalCardinalDpad]; GCControllerButtonInput *center = profile.buttons[GCInputDirectionalCenterButton]; if (ring != nil && center != nil) { // The 2nd generation remote, whose ring has real buttons. This takes // GCSupportsMultipleMicroGamepads in Info.plist. buttonA isn't used, since it's pressed // along with every ring button. ring.up.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteButtonPressed(pressed, NKCODE_DPAD_UP); }; ring.down.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteButtonPressed(pressed, NKCODE_DPAD_DOWN); }; ring.left.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteButtonPressed(pressed, NKCODE_DPAD_LEFT); }; ring.right.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteButtonPressed(pressed, NKCODE_DPAD_RIGHT); }; center.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteButtonPressed(pressed, NKCODE_ENTER); }; } else { // Where the finger is when clicking, rather than how far it moved since it went down. micro.reportsAbsoluteDpadValues = YES; GCControllerDirectionPad *touch = micro.dpad; std::shared_ptr state = std::make_shared(); micro.buttonA.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteClicked(state.get(), touch, pressed); }; } // Play/pause. Escape opens the pause menu in-game. micro.buttonX.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteButtonPressed(pressed, NKCODE_ESCAPE); }; // The Menu (or Back) button. micro.buttonMenu.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { remoteButtonPressed(pressed, NKCODE_BACK); }; INFO_LOG(Log::System, "Siri Remote set up: %s (ring: %s)", [controller.vendorName ?: @"Remote" UTF8String], ring != nil ? "yes" : "no"); return true; } void TouchTracker::SendTouchEvent(CGPoint point, UIView *view, int code, int pointerId) { // From points to dp. The view covers the whole display. CGSize size = view.bounds.size; if (size.width <= 0.0 || size.height <= 0.0) { return; } TouchInput input; input.x = point.x * g_display.dp_xres / size.width; input.y = point.y * g_display.dp_yres / size.height; switch (code) { case 1: input.flags = TouchInputFlags::DOWN; break; case 2: input.flags = TouchInputFlags::UP; break; default: input.flags = TouchInputFlags::MOVE; break; } input.id = pointerId; NativeTouch(input); } int TouchTracker::ToTouchID(UITouch *uiTouch, bool allowAllocate) { // Find the id for the touch. for (int localId = 0; localId < (int)ARRAY_SIZE(touches_); ++localId) { if (touches_[localId] == uiTouch) { return localId; } } // Allocate a new one, perhaps? if (allowAllocate) { for (int localId = 0; localId < (int)ARRAY_SIZE(touches_); ++localId) { if (touches_[localId] == 0) { touches_[localId] = uiTouch; return localId; } } // None were free. Ignore? return 0; } return -1; } void TouchTracker::Began(NSSet *touches, UIView *view) { for (UITouch* touch in touches) { CGPoint point = [touch locationInView:view]; int touchId = ToTouchID(touch, true); SendTouchEvent(point, view, 1, touchId); } } void TouchTracker::Moved(NSSet *touches, UIView *view) { for (UITouch* touch in touches) { CGPoint point = [touch locationInView:view]; int touchId = ToTouchID(touch, true); SendTouchEvent(point, view, 0, touchId); } } void TouchTracker::Ended(NSSet *touches, UIView *view) { for (UITouch* touch in touches) { CGPoint point = [touch locationInView:view]; int touchId = ToTouchID(touch, false); if (touchId >= 0) { SendTouchEvent(point, view, 2, touchId); touches_[touchId] = nullptr; } } } void TouchTracker::Cancelled(NSSet *touches, UIView *view) { for (UITouch* touch in touches) { CGPoint point = [touch locationInView:view]; int touchId = ToTouchID(touch, false); if (touchId >= 0) { SendTouchEvent(point, view, 2, touchId); touches_[touchId] = nullptr; } } } #if !PPSSPP_PLATFORM(TVOS) void ProcessAccelerometerData(CMAccelerometerData *accData) { CMAcceleration acc = accData.acceleration; // INFO_LOG(SYSTEM, "%f %f %f", acc.x, acc.y, acc.z); // Might need to change these for portrait or inverse landscape NativeAccelerometer(-acc.x, -acc.y, -acc.z); } #endif void SendKeyboardChars(std::string_view str) { UTF8 chars(str); while (!chars.end()) { uint32_t codePoint = chars.next(); KeyInput input{}; input.deviceId = DEVICE_ID_KEYBOARD; input.flags = KeyInputFlags::CHAR; input.unicodeChar = codePoint; NativeKey(input); } } static void SendPresses(NSSet *presses, KeyInputFlags flags) { for (UIPress *press in presses) { if (!press.key) { // I guess we could support remotes and stuff. continue; } InputKeyCode code = HIDUsageToInputKeyCode(press.key.keyCode); if (code != NKCODE_UNKNOWN) { KeyInput input{}; input.deviceId = DEVICE_ID_KEYBOARD; input.keyCode = code; input.flags = flags; NativeKey(input); } if ((flags & KeyInputFlags::DOWN) && press.key.characters) { std::string chars([press.key.characters UTF8String]); SendKeyboardChars(chars); } } } void KeyboardPressesBegan(NSSet *presses, UIPressesEvent *event) { SendPresses(presses, KeyInputFlags::DOWN); } void KeyboardPressesEnded(NSSet *presses, UIPressesEvent *event) { SendPresses(presses, KeyInputFlags::UP); } InputKeyCode HIDUsageToInputKeyCode(UIKeyboardHIDUsage usage) { switch (usage) { case UIKeyboardHIDUsageKeyboardA: return NKCODE_A; case UIKeyboardHIDUsageKeyboardB: return NKCODE_B; case UIKeyboardHIDUsageKeyboardC: return NKCODE_C; case UIKeyboardHIDUsageKeyboardD: return NKCODE_D; case UIKeyboardHIDUsageKeyboardE: return NKCODE_E; case UIKeyboardHIDUsageKeyboardF: return NKCODE_F; case UIKeyboardHIDUsageKeyboardG: return NKCODE_G; case UIKeyboardHIDUsageKeyboardH: return NKCODE_H; case UIKeyboardHIDUsageKeyboardI: return NKCODE_I; case UIKeyboardHIDUsageKeyboardJ: return NKCODE_J; case UIKeyboardHIDUsageKeyboardK: return NKCODE_K; case UIKeyboardHIDUsageKeyboardL: return NKCODE_L; case UIKeyboardHIDUsageKeyboardM: return NKCODE_M; case UIKeyboardHIDUsageKeyboardN: return NKCODE_N; case UIKeyboardHIDUsageKeyboardO: return NKCODE_O; case UIKeyboardHIDUsageKeyboardP: return NKCODE_P; case UIKeyboardHIDUsageKeyboardQ: return NKCODE_Q; case UIKeyboardHIDUsageKeyboardR: return NKCODE_R; case UIKeyboardHIDUsageKeyboardS: return NKCODE_S; case UIKeyboardHIDUsageKeyboardT: return NKCODE_T; case UIKeyboardHIDUsageKeyboardU: return NKCODE_U; case UIKeyboardHIDUsageKeyboardV: return NKCODE_V; case UIKeyboardHIDUsageKeyboardW: return NKCODE_W; case UIKeyboardHIDUsageKeyboardX: return NKCODE_X; case UIKeyboardHIDUsageKeyboardY: return NKCODE_Y; case UIKeyboardHIDUsageKeyboardZ: return NKCODE_Z; case UIKeyboardHIDUsageKeyboard1: return NKCODE_1; case UIKeyboardHIDUsageKeyboard2: return NKCODE_2; case UIKeyboardHIDUsageKeyboard3: return NKCODE_3; case UIKeyboardHIDUsageKeyboard4: return NKCODE_4; case UIKeyboardHIDUsageKeyboard5: return NKCODE_5; case UIKeyboardHIDUsageKeyboard6: return NKCODE_6; case UIKeyboardHIDUsageKeyboard7: return NKCODE_7; case UIKeyboardHIDUsageKeyboard8: return NKCODE_8; case UIKeyboardHIDUsageKeyboard9: return NKCODE_9; case UIKeyboardHIDUsageKeyboard0: return NKCODE_0; case UIKeyboardHIDUsageKeyboardReturnOrEnter: return NKCODE_NUMPAD_ENTER; case UIKeyboardHIDUsageKeyboardEscape: return NKCODE_ESCAPE; case UIKeyboardHIDUsageKeyboardDeleteOrBackspace: return NKCODE_DEL; // really. case UIKeyboardHIDUsageKeyboardTab: return NKCODE_TAB; case UIKeyboardHIDUsageKeyboardSpacebar: return NKCODE_SPACE; case UIKeyboardHIDUsageKeyboardHyphen: return NKCODE_MINUS; case UIKeyboardHIDUsageKeyboardEqualSign: return NKCODE_EQUALS; case UIKeyboardHIDUsageKeyboardOpenBracket: return NKCODE_LEFT_BRACKET; case UIKeyboardHIDUsageKeyboardCloseBracket: return NKCODE_RIGHT_BRACKET; case UIKeyboardHIDUsageKeyboardBackslash: return NKCODE_BACKSLASH; case UIKeyboardHIDUsageKeyboardNonUSPound: return NKCODE_POUND; case UIKeyboardHIDUsageKeyboardSemicolon: return NKCODE_SEMICOLON; case UIKeyboardHIDUsageKeyboardQuote: return NKCODE_APOSTROPHE; case UIKeyboardHIDUsageKeyboardGraveAccentAndTilde: return NKCODE_GRAVE; case UIKeyboardHIDUsageKeyboardComma: return NKCODE_COMMA; case UIKeyboardHIDUsageKeyboardPeriod: return NKCODE_PERIOD; case UIKeyboardHIDUsageKeyboardSlash: return NKCODE_SLASH; case UIKeyboardHIDUsageKeyboardCapsLock: return NKCODE_CAPS_LOCK; case UIKeyboardHIDUsageKeyboardF1: return NKCODE_F1; case UIKeyboardHIDUsageKeyboardF2: return NKCODE_F2; case UIKeyboardHIDUsageKeyboardF3: return NKCODE_F3; case UIKeyboardHIDUsageKeyboardF4: return NKCODE_F4; case UIKeyboardHIDUsageKeyboardF5: return NKCODE_F5; case UIKeyboardHIDUsageKeyboardF6: return NKCODE_F6; case UIKeyboardHIDUsageKeyboardF7: return NKCODE_F7; case UIKeyboardHIDUsageKeyboardF8: return NKCODE_F8; case UIKeyboardHIDUsageKeyboardF9: return NKCODE_F9; case UIKeyboardHIDUsageKeyboardF10: return NKCODE_F10; case UIKeyboardHIDUsageKeyboardF11: return NKCODE_F11; case UIKeyboardHIDUsageKeyboardF12: return NKCODE_F12; case UIKeyboardHIDUsageKeyboardScrollLock: return NKCODE_SCROLL_LOCK; case UIKeyboardHIDUsageKeyboardInsert: return NKCODE_INSERT; case UIKeyboardHIDUsageKeyboardHome: return NKCODE_HOME; case UIKeyboardHIDUsageKeyboardPageUp: return NKCODE_PAGE_UP; case UIKeyboardHIDUsageKeyboardDeleteForward: return NKCODE_DEL; case UIKeyboardHIDUsageKeyboardEnd: return NKCODE_MOVE_END; case UIKeyboardHIDUsageKeyboardPageDown: return NKCODE_PAGE_DOWN; case UIKeyboardHIDUsageKeyboardRightArrow: return NKCODE_DPAD_RIGHT; case UIKeyboardHIDUsageKeyboardLeftArrow: return NKCODE_DPAD_LEFT; case UIKeyboardHIDUsageKeyboardDownArrow: return NKCODE_DPAD_DOWN; case UIKeyboardHIDUsageKeyboardUpArrow: return NKCODE_DPAD_UP; case UIKeyboardHIDUsageKeypadNumLock: return NKCODE_NUM_LOCK; case UIKeyboardHIDUsageKeypadSlash: return NKCODE_NUMPAD_DIVIDE; case UIKeyboardHIDUsageKeypadAsterisk: return NKCODE_NUMPAD_MULTIPLY; case UIKeyboardHIDUsageKeypadHyphen: return NKCODE_NUMPAD_SUBTRACT; case UIKeyboardHIDUsageKeypadPlus: return NKCODE_NUMPAD_ADD; case UIKeyboardHIDUsageKeypadEnter: return NKCODE_NUMPAD_ENTER; case UIKeyboardHIDUsageKeypad1: return NKCODE_NUMPAD_1; case UIKeyboardHIDUsageKeypad2: return NKCODE_NUMPAD_2; case UIKeyboardHIDUsageKeypad3: return NKCODE_NUMPAD_3; case UIKeyboardHIDUsageKeypad4: return NKCODE_NUMPAD_4; case UIKeyboardHIDUsageKeypad5: return NKCODE_NUMPAD_5; case UIKeyboardHIDUsageKeypad6: return NKCODE_NUMPAD_6; case UIKeyboardHIDUsageKeypad7: return NKCODE_NUMPAD_7; case UIKeyboardHIDUsageKeypad8: return NKCODE_NUMPAD_8; case UIKeyboardHIDUsageKeypad9: return NKCODE_NUMPAD_9; case UIKeyboardHIDUsageKeypad0: return NKCODE_NUMPAD_0; case UIKeyboardHIDUsageKeypadPeriod: return NKCODE_NUMPAD_COMMA; case UIKeyboardHIDUsageKeyboardNonUSBackslash: return NKCODE_BACKSLASH; case UIKeyboardHIDUsageKeyboardApplication: return NKCODE_APP_SWITCH; case UIKeyboardHIDUsageKeyboardPower: return NKCODE_POWER; case UIKeyboardHIDUsageKeypadEqualSign: return NKCODE_NUMPAD_EQUALS; case UIKeyboardHIDUsageKeyboardMenu: return NKCODE_MENU; case UIKeyboardHIDUsageKeyboardMute: return NKCODE_MUTE; case UIKeyboardHIDUsageKeyboardVolumeUp: return NKCODE_VOLUME_UP; case UIKeyboardHIDUsageKeypadComma: return NKCODE_NUMPAD_COMMA; case UIKeyboardHIDUsageKeyboardLeftShift: return NKCODE_SHIFT_LEFT; case UIKeyboardHIDUsageKeyboardRightShift: return NKCODE_SHIFT_RIGHT; case UIKeyboardHIDUsageKeyboardLeftControl: return NKCODE_CTRL_LEFT; case UIKeyboardHIDUsageKeyboardRightControl: return NKCODE_CTRL_RIGHT; case UIKeyboardHIDUsageKeyboardLeftAlt: return NKCODE_ALT_LEFT; case UIKeyboardHIDUsageKeyboardRightAlt: return NKCODE_ALT_RIGHT; case UIKeyboardHIDUsageKeyboardLeftGUI: return NKCODE_META_LEFT; case UIKeyboardHIDUsageKeyboardRightGUI: return NKCODE_META_RIGHT; default: return NKCODE_UNKNOWN; } }