Files
ppsspp/ios/Controls.mm
T
Henrik RydgårdandClaude Opus 5.5 5b05b34b4e tvOS: Fewer ifdefs, capability checks instead
The Siri Remote setup builds on iOS too, the device type comes from the
interface idiom, and requests for things the platform says it lacks are
refused by checking the property. What's left ifdeffed is API tvOS doesn't
have at all. Reports say tvOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:21:14 +02:00

549 lines
21 KiB
Plaintext

#include "Controls.h"
#include <algorithm>
#include <cmath>
#include <memory>
#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<RemoteClickState> state = std::make_shared<RemoteClickState>();
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<UIPress *> *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<UIPress *> *presses, UIPressesEvent *event) {
SendPresses(presses, KeyInputFlags::DOWN);
}
void KeyboardPressesEnded(NSSet<UIPress *> *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;
}
}