Work towards full android joystick support.

This commit is contained in:
Henrik Rydgard committed 2013-07-06 19:08:20 +02:00
1 parent f4578129ef
commit 1dec6e8154
14 files changed
+431 -231

No files matched your search

+78 -27
View File
@@ -39,7 +39,8 @@ static float left_joystick_x_async;
static float left_joystick_y_async;
static float right_joystick_x_async;
static float right_joystick_y_async;
static int key_queue_async[MAX_KEYQUEUESIZE];
static float hat_joystick_x_async;
static float hat_joystick_y_async;
static uint32_t pad_buttons_down;
@@ -117,10 +118,11 @@ extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_isLandscape(JNIEn
// For the Back button to work right.
extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_isAtTopLevel(JNIEnv *env, jclass) {
bool isAtTop = NativeIsAtTopLevel();
if (extraLog) {
ILOG("isAtTopLevel");
ILOG("isAtTopLevel %i", (int)isAtTop);
}
return NativeIsAtTopLevel();
return isAtTop;
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_audioConfig
@@ -148,8 +150,8 @@ extern "C" void Java_com_henrikrydgard_libnative_NativeApp_init
left_joystick_y_async = 0;
right_joystick_x_async = 0;
right_joystick_y_async = 0;
KeyQueueBlank(key_queue_async);
hat_joystick_x_async = 0;
hat_joystick_y_async = 0;
std::string apkPath = GetJavaString(env, japkpath);
ILOG("NativeApp::Init: APK path: %s", apkPath.c_str());
@@ -262,8 +264,9 @@ extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayRender(JN
lock_guard guard(input_state.lock);
pad_buttons_down |= pad_buttons_async_set;
pad_buttons_down &= ~pad_buttons_async_clear;
input_state.pad_buttons = pad_buttons_down;
KeyQueueCopyQueue(key_queue_async, input_state.key_queue);
UpdateInputState(&input_state);
}
@@ -271,6 +274,8 @@ extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayRender(JN
lock_guard guard(input_state.lock);
input_state.pad_lstick_x = left_joystick_x_async;
input_state.pad_lstick_y = left_joystick_y_async;
input_state.pad_rstick_x = right_joystick_x_async;
input_state.pad_rstick_y = right_joystick_y_async;
}
NativeUpdate(input_state);
@@ -372,49 +377,96 @@ static inline void AsyncUp(int padbutton) {
pad_buttons_async_clear |= padbutton;
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_keyDown(JNIEnv *, jclass, jint key) {
KeyQueueAddKey(key_queue_async, key);
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_keyDown(JNIEnv *, jclass, jint deviceId, jint key) {
KeyInput keyInput;
keyInput.deviceId = deviceId;
keyInput.keyCode = key;
keyInput.flags = KEY_DOWN;
NativeKey(keyInput);
// Pad mapping
switch (key) {
case 1: AsyncDown (PAD_BUTTON_BACK); break; // Back
case 2: AsyncDown (PAD_BUTTON_MENU); break; // Menu
case KEYCODE_BACK: AsyncDown(PAD_BUTTON_BACK); break; // Back
case KEYCODE_MENU: AsyncDown(PAD_BUTTON_MENU); break; // Menu
case KEYCODE_BUTTON_CROSS_PS3:
AsyncDown(PAD_BUTTON_A);
break;
case KEYCODE_BUTTON_CIRCLE_PS3:
AsyncDown (PAD_BUTTON_B);
AsyncDown(PAD_BUTTON_B);
break;
default: break;
}
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_keyUp(JNIEnv *, jclass, jint key) {
KeyQueueRemoveKey(key_queue_async, key);
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_keyUp(JNIEnv *, jclass, jint deviceId, jint key) {
KeyInput keyInput;
keyInput.deviceId = deviceId;
keyInput.keyCode = key;
keyInput.flags = KEY_UP;
NativeKey(keyInput);
// Pad mapping
switch (key) {
case 1: AsyncUp (PAD_BUTTON_BACK); break; // Back
case 2: AsyncUp (PAD_BUTTON_MENU); break; // Menu
case KEYCODE_BACK:
AsyncUp(PAD_BUTTON_BACK);
break; // Back
case KEYCODE_MENU:
AsyncUp(PAD_BUTTON_MENU);
break; // Menu
case KEYCODE_BUTTON_CROSS_PS3:
AsyncUp(PAD_BUTTON_A);
break;
case KEYCODE_BUTTON_CIRCLE_PS3:
AsyncUp (PAD_BUTTON_B);
AsyncUp(PAD_BUTTON_B);
break;
default:
break;
default: break;
}
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_joystickEvent(
JNIEnv *env, jclass, jint stick, jfloat x, jfloat y) {
if (stick == 0) {
left_joystick_x_async = x;
left_joystick_y_async = y;
} else if (stick == 1) {
right_joystick_x_async = x;
right_joystick_y_async = y;
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_beginJoystickEvent(
JNIEnv *env, jclass) {
// mutex lock?
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_joystickEvent(
JNIEnv *env, jclass, jint deviceId, jint axisId, jfloat value) {
switch (axisId) {
case JOYSTICK_AXIS_X:
left_joystick_x_async = value;
break;
case JOYSTICK_AXIS_Y:
left_joystick_y_async = -value;
break;
case JOYSTICK_AXIS_Z:
right_joystick_x_async = value;
break;
case JOYSTICK_AXIS_RZ:
right_joystick_y_async = -value;
break;
case JOYSTICK_AXIS_HAT_X:
hat_joystick_x_async = value;
break;
case JOYSTICK_AXIS_HAT_Y:
hat_joystick_y_async = -value;
break;
}
// Hat is just stupid. Force translate it to dpad events. TODO
AxisInput axis;
axis.axisId = axisId;
axis.deviceId = deviceId;
axis.value = value;
NativeAxis(axis);
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_endJoystickEvent(
JNIEnv *env, jclass) {
// mutex unlock?
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_mouseWheelEvent(
JNIEnv *env, jclass, jint stick, jfloat x, jfloat y) {
// TODO
@@ -437,4 +489,3 @@ extern "C" void Java_com_henrikrydgard_libnative_NativeApp_sendMessage(JNIEnv *e
ILOG("Message received: %s %s", msg.c_str(), prm.c_str());
NativeMessageReceived(msg.c_str(), prm.c_str());
}
-30
View File
@@ -6,33 +6,3 @@
// since native's keycodes are based on
// android keycodes.
typedef enum _keycode_t AndroidKeyCodes;
enum AndroidJoystickAxis {
// Field descriptor #15 I
JOYSTICK_AXIS_X = 0,
JOYSTICK_AXIS_Y = 1,
JOYSTICK_AXIS_PRESSURE = 2,
JOYSTICK_AXIS_SIZE = 3,
JOYSTICK_AXIS_TOUCH_MAJOR = 4,
JOYSTICK_AXIS_TOUCH_MINOR = 5,
JOYSTICK_AXIS_TOOL_MAJOR = 6,
JOYSTICK_AXIS_TOOL_MINOR = 7,
JOYSTICK_AXIS_ORIENTATION = 8,
JOYSTICK_AXIS_VSCROLL = 9,
JOYSTICK_AXIS_HSCROLL = 10,
JOYSTICK_AXIS_Z = 11,
JOYSTICK_AXIS_RX = 12,
JOYSTICK_AXIS_RY = 13,
JOYSTICK_AXIS_RZ = 14,
JOYSTICK_AXIS_HAT_X = 15,
JOYSTICK_AXIS_HAT_Y = 16,
JOYSTICK_AXIS_LTRIGGER = 17,
JOYSTICK_AXIS_RTRIGGER = 18,
JOYSTICK_AXIS_THROTTLE = 19,
JOYSTICK_AXIS_RUDDER = 20,
JOYSTICK_AXIS_WHEEL = 21,
JOYSTICK_AXIS_GAS = 22,
JOYSTICK_AXIS_BRAKE = 23,
JOYSTICK_AXIS_DISTANCE = 24,
JOYSTICK_AXIS_TILT = 25,
};
@@ -0,0 +1,103 @@
package com.henrikrydgard.libnative;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.SparseIntArray;
import android.view.InputDevice;
import android.view.InputDevice.MotionRange;
import android.view.KeyEvent;
import android.view.MotionEvent;
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
public class InputDeviceState {
// DEVICE_ID_PAD_0 from the cpp code. TODO: allocate these sequentially if we get more controllers.
private static int deviceId = 10;
private InputDevice mDevice;
private int[] mAxes;
InputDevice getDevice() { return mDevice; }
public InputDeviceState(InputDevice device) {
mDevice = device;
int numAxes = 0;
for (MotionRange range : device.getMotionRanges()) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
numAxes += 1;
}
}
mAxes = new int[numAxes];
int i = 0;
for (MotionRange range : device.getMotionRanges()) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
mAxes[i++] = range.getAxis();
}
}
}
public static float ProcessAxis(InputDevice.MotionRange range, float axisvalue) {
float absaxisvalue = Math.abs(axisvalue);
float deadzone = range.getFlat();
if (absaxisvalue <= deadzone) {
return 0.0f;
}
float normalizedvalue;
if (axisvalue < 0.0f) {
normalizedvalue = absaxisvalue / range.getMin();
} else {
normalizedvalue = absaxisvalue / range.getMax();
}
return normalizedvalue;
}
private static boolean isGameKey(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
return true;
default:
return KeyEvent.isGamepadButton(keyCode);
}
}
public boolean onKeyDown(KeyEvent event) {
int keyCode = event.getKeyCode();
if (event.getRepeatCount() == 0) {
if (isGameKey(keyCode)) {
NativeApp.keyDown(deviceId, keyCode);
return true;
}
}
return false;
}
public boolean onKeyUp(KeyEvent event) {
int keyCode = event.getKeyCode();
if (isGameKey(keyCode)) {
NativeApp.keyUp(deviceId, keyCode);
return true;
}
return false;
}
public boolean onJoystickMotion(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) {
return false;
}
NativeApp.beginJoystickEvent();
for (int i = 0; i < mAxes.length; i++) {
int axisId = mAxes[i];
float value = event.getAxisValue(axisId);
// TODO: Use processAxis or move that to the C++ code
NativeApp.joystickAxis(deviceId, axisId, value);
}
NativeApp.endJoystickEvent();
return true;
}
}
@@ -5,6 +5,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.util.List;
import java.util.UUID;
import android.annotation.SuppressLint;
@@ -32,6 +33,7 @@ import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -275,54 +277,91 @@ public class NativeActivity extends Activity {
public static boolean inputBoxCancelled;
@SuppressLint("NewApi")
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Eat these keys, to avoid accidental exits / other screwups.
// Maybe there's even more we need to eat on tablets?
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.isAltPressed()) {
NativeApp.keyDown(1004); // special custom keycode
} else {
NativeApp.keyDown(1);
InputDeviceState inputPlayerA;
String inputPlayerADesc;
// We simply grab the first input device to produce an event and ignore all others that are connected.
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private InputDeviceState getInputDeviceState(InputEvent event) {
InputDevice device = event.getDevice();
if (device == null) {
return null;
}
if (inputPlayerA == null) {
inputPlayerA = new InputDeviceState(device);
inputPlayerADesc = getInputDesc(device);
}
if (inputPlayerA.getDevice() == device) {
return inputPlayerA;
}
/*
if (inputPlayerB == null) {
inputPlyerB = new InputDeviceStats(device);
}
if (inputPlayerB.getDevice() == device) {
return inputPlayerB;
}*/
return null;
}
// We grab the keys before onKeyDown/... even see them. This is also better because it lets us
// distinguish devices.
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
InputDeviceState state = getInputDeviceState(event);
if (state == null) {
return super.dispatchKeyEvent(event);
}
return true;
case KeyEvent.KEYCODE_MENU:
NativeApp.keyDown(2);
return true;
case KeyEvent.KEYCODE_SEARCH:
NativeApp.keyDown(3);
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
return false;
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
// Joysticks are supported in Honeycomb MR1 and later via the onGenericMotionEvent method.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && event.getSource() == InputDevice.SOURCE_JOYSTICK) {
return false;
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
if (state.onKeyDown(event)) {
return true;
}
break;
case KeyEvent.ACTION_UP:
if (state.onKeyUp(event)) {
return true;
}
break;
}
default:
// send the rest of the keys through.
// TODO: get rid of the three special cases above by adjusting the native side of the code.
Log.d("JAMIE", "Key code: " + keyCode + ", KeyEvent: " + event);
NativeApp.keyDown(keyCode);
return true;
}
// Let's go through the old path (onKeyUp, onKeyDown).
return super.dispatchKeyEvent(event);
}
@TargetApi(16)
static public String getInputDesc(InputDevice input) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return input.getDescriptor();
} else {
List<InputDevice.MotionRange> motions = input.getMotionRanges();
String fakeid = "";
for (InputDevice.MotionRange range : motions)
fakeid += range.getAxis();
return fakeid;
}
}
@Override
@TargetApi(12)
public boolean onGenericMotionEvent(MotionEvent event) {
Log.d("JAMIE", "onGenericMotionEvent: " + event);
if (event.getSource() == InputDevice.SOURCE_JOYSTICK) {
float leftJoystickX = event.getAxisValue(MotionEvent.AXIS_X) * 1f;
float leftJoystickY = event.getAxisValue(MotionEvent.AXIS_Y) * -1f;
NativeApp.joystickEvent(0, leftJoystickX, leftJoystickY);
Log.d(TAG, "onGenericMotionEvent: " + event);
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
if (Build.VERSION.SDK_INT >= 12) {
InputDeviceState state = getInputDeviceState(event);
state.onJoystickMotion(event);
}
}
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_HOVER_MOVE:
@@ -335,49 +374,90 @@ public class NativeActivity extends Activity {
}
return super.onGenericMotionEvent(event);
}
@SuppressLint("NewApi")
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Eat these keys, to avoid accidental exits / other screwups.
// Maybe there's even more we need to eat on tablets?
Log.d("JAMIE", "KeyEvent: " + event);
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.isAltPressed()) {
NativeApp.keyUp(1004); // special custom keycode
NativeApp.keyDown(0, 1004); // special custom keycode
} else if (NativeApp.isAtTopLevel()) {
return false;
Log.i(TAG, "IsAtTopLevel returned false.");
return super.onKeyUp(keyCode, event);
} else {
NativeApp.keyUp(1);
NativeApp.keyDown(0, keyCode);
}
return true;
case KeyEvent.KEYCODE_MENU:
// Menu should be ignored from SDK 11 forwards. We send it to the app.
NativeApp.keyUp(2);
return true;
case KeyEvent.KEYCODE_SEARCH:
// Search probably should also be ignored. We send it to the app.
NativeApp.keyUp(3);
NativeApp.keyDown(0, keyCode);
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
return false;
// NativeApp should ignore these.
return super.onKeyDown(keyCode, event);
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
// Joysticks are supported in Honeycomb MR1 and later via the onGenericMotionEvent method.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && event.getSource() == InputDevice.SOURCE_JOYSTICK) {
return false;
return super.onKeyUp(keyCode, event);
}
// Fall through
default:
// send the rest of the keys through.
// TODO: get rid of the three special cases above by adjusting the native side of the code.
NativeApp.keyUp(keyCode);
// Log.d(TAG, "Key down: " + keyCode + ", KeyEvent: " + event);
NativeApp.keyDown(0, keyCode);
return true;
}
}
@SuppressLint("NewApi")
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.isAltPressed()) {
NativeApp.keyUp(0, 1004); // special custom keycode
} else if (NativeApp.isAtTopLevel()) {
Log.i(TAG, "IsAtTopLevel returned false.");
return super.onKeyUp(keyCode, event);
} else {
NativeApp.keyUp(0, keyCode);
}
return true;
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_SEARCH:
// Search probably should also be ignored. We send it to the app.
NativeApp.keyUp(0, keyCode);
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
return super.onKeyUp(keyCode, event);
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
// Joysticks are supported in Honeycomb MR1 and later via the onGenericMotionEvent method.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && event.getSource() == InputDevice.SOURCE_JOYSTICK) {
return super.onKeyUp(keyCode, event);
}
// Fall through
default:
// send the rest of the keys through.
// TODO: get rid of the three special cases above by adjusting the native side of the code.
// Log.d(TAG, "Key down: " + keyCode + ", KeyEvent: " + event);
NativeApp.keyUp(0, keyCode);
return true;
}
}
public String inputBox(String title, String defaultText, String defaultAction) {
final FrameLayout fl = new FrameLayout(this);
final EditText input = new EditText(this);
@@ -428,6 +508,7 @@ public class NativeActivity extends Activity {
send.setData(uri);
startActivity(Intent.createChooser(send, "E-mail the app author!"));
} else if (command.equals("launchMarket")) {
// Don't need this, can just use launchBrowser with a market:
// http://stackoverflow.com/questions/3442366/android-link-to-market-from-inside-another-app
// http://developer.android.com/guide/publishing/publishing.html#marketintent
} else if (command.equals("toast")) {
@@ -15,11 +15,13 @@ public class NativeApp {
public static native void shutdown();
public static native void keyDown(int key);
public static native void keyDown(int deviceId, int key);
public static native void keyUp(int deviceId, int key);
public static native void keyUp(int key);
public static native void joystickEvent(int stick, float x, float y);
public static native void beginJoystickEvent();
public static native void joystickAxis(int deviceId, int axis, float value);
public static native void endJoystickEvent();
public static native void mouseWheelEvent(float x, float y);
// will only be called between init() and shutdown()
+4
View File
@@ -11,6 +11,8 @@
// This is defined in input/input_state.h.
struct InputState;
struct TouchInput;
struct KeyInput;
struct AxisInput;
// The first function to get called, just write strings to the two pointers.
// This might get called multiple times in some implementations, you must be able to handle that.
@@ -47,6 +49,8 @@ void NativeUpdate(InputState &input);
// If you don't care about touch latency, just do a no-op implementation of this.
// time is not yet implemented. finger can be from 0 to 7, inclusive.
void NativeTouch(const TouchInput &touch);
void NativeKey(const KeyInput &key);
void NativeAxis(const AxisInput &axis);
// Called when it's time to render. If the device can keep up, this
// will also be called sixty times per second. Main thread.
+10 -7
View File
@@ -538,14 +538,17 @@ int main(int argc, char *argv[]) {
if (event.key.keysym.sym == SDLK_ESCAPE) {
quitRequested = 1;
}
KeyQueueAttemptTranslatedAdd(input_state.key_queue,
KeyMapRawSDLtoNative,
event.key.keysym.sym);
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = KeyMapRawSDLtoNative[event.key.keysym.sym];
key.deviceId = 0;
NativeKey(key);
} else if (event.type == SDL_KEYUP) {
KeyQueueAttemptTranslatedRemove(input_state.key_queue,
KeyMapRawSDLtoNative,
event.key.keysym.sym);
KeyInput key;
key.flags = KEY_UP;
key.keyCode = KeyMapRawSDLtoNative[event.key.keysym.sym];
key.deviceId = 0;
NativeKey(key);
} else if (event.type == SDL_MOUSEBUTTONDOWN) {
if (event.button.button == SDL_BUTTON_LEFT) {
input_state.pointer_x[0] = mx;
+9 -91
View File
@@ -1,96 +1,14 @@
#include "input_state.h"
#include <iostream>
#define max_queue MAX_KEYQUEUESIZE
void MakeRoomInQueueFront(int queue[])
{
// shift queue elements from front towards back
for (int i = max_queue - 1; i > 0; i--) {
queue[i] = queue[i - 1];
const char *GetDeviceName(int deviceId) {
switch (deviceId) {
case DEVICE_ID_DEFAULT: return "built-in";
case DEVICE_ID_KEYBOARD: return "kbd";
case DEVICE_ID_PAD_0: return "pad";
case DEVICE_ID_X360_0: return "x360";
case DEVICE_ID_ACCELEROMETER: return "accelerometer";
default:
return "unknown";
}
queue[0] = 0;
}
void FillGap(int queue[], int gap_loc)
{
// shift elements from back towards front
// until gap location is filled
for (int i = gap_loc; i < max_queue; i++) {
queue[i] = queue[i + 1];
}
queue[max_queue - 1] = 0;
}
void KeyQueueAddKey(int queue[], int key)
{
for (int i = 0; i < max_queue; i++) {
if (queue[i] == key)
return;
if (queue[i] == 0) {
queue[i] = key;
return;
}
}
// queue was full
MakeRoomInQueueFront(queue);
queue[0] = key;
}
void KeyQueueRemoveKey(int queue[], int key)
{
for (int i = 0; i < max_queue; i++)
if (queue[i] == key)
FillGap(queue, i);
}
void KeyQueueCopyQueue(int src[], int dst[])
{
for (int i = 0; i < max_queue; i++)
dst[i] = src[i];
}
void KeyQueueBlank(int queue[])
{
for (int i = 0; i < max_queue; i++)
queue[i] = 0;
}
int AttemptTranslate(const std::map<int, int> trans_table, int plat_key)
{
std::map<int, int>::const_iterator iter;
iter = trans_table.find(plat_key);
if (iter == trans_table.end())
return 0;
return iter->second;
}
void KeyQueueAttemptTranslatedAdd(int queue[],
const std::map<int, int> translation_table,
int platform_key)
{
int key = AttemptTranslate(translation_table, platform_key);
if (key == 0) {
std::cerr << "Warning: Platform key code translation table missing "
<< "(" << platform_key << ")\n";
return;
}
KeyQueueAddKey(queue, key);
}
void KeyQueueAttemptTranslatedRemove(int queue[],
const std::map<int, int> translation_table,
int platform_key)
{
int key = AttemptTranslate(translation_table, platform_key);
if (key == 0)
return;
KeyQueueRemoveKey(queue, key);
}
+38 -11
View File
@@ -1,10 +1,30 @@
#pragma once
// InputState is the simple way of getting input. All the input we have is collected
// to a canonical Xbox360-style pad fully automatically.
//
// Recommended for use in game UIs and games that don't have advanced needs.
//
// For more detailed and configurable input, implement NativeTouch, NativeKey and NativeAxis and do your
// own mapping. Might later move the mapping system from PPSSPP to native.
#include "math/lin/vec3.h"
#include "base/mutex.h"
#include "base/basictypes.h"
#include <map>
// Default device IDs
enum {
DEVICE_ID_DEFAULT = 0, // Old Android
DEVICE_ID_KEYBOARD = 1, // PC keyboard, android keyboards
DEVICE_ID_PAD_0 = 10, // Generic joypads
DEVICE_ID_X360_0 = 20, // XInput joypads
DEVICE_ID_ACCELEROMETER = 30,
};
const char *GetDeviceName(int deviceId);
enum {
PAD_BUTTON_A = 1,
PAD_BUTTON_B = 2,
@@ -44,7 +64,7 @@ enum {
#ifndef MAX_KEYQUEUESIZE
#define MAX_KEYQUEUESIZE 20
#endif
// Collection of all possible inputs, and automatically computed
// deltas where applicable.
struct InputState {
@@ -60,7 +80,7 @@ struct InputState {
memset(pointer_down, 0, sizeof(pointer_down));
}
// Gamepad style input
// Gamepad style input. For ease of use.
int pad_buttons; // bitfield
int pad_last_buttons;
int pad_buttons_down; // buttons just pressed this frame
@@ -84,8 +104,6 @@ struct InputState {
bool accelerometer_valid;
Vec3 acc;
int key_queue[MAX_KEYQUEUESIZE];
private:
DISALLOW_COPY_AND_ASSIGN(InputState);
};
@@ -118,11 +136,20 @@ struct TouchInput {
double timestamp;
};
enum {
KEY_DOWN = 1,
KEY_UP = 2,
};
// Key Queue Helpers
void KeyQueueAddKey(int [], int);
void KeyQueueRemoveKey(int [], int);
void KeyQueueCopyQueue(int src[], int dst[]);
void KeyQueueBlank(int []);
void KeyQueueAttemptTranslatedAdd(int [], const std::map<int, int>, int);
void KeyQueueAttemptTranslatedRemove(int [], const std::map<int, int>, int);
struct KeyInput {
int deviceId;
int keyCode; // Android keycodes are the canonical keycodes, everyone else map to them.
int flags;
};
struct AxisInput {
int deviceId;
int axisId; // Android axis Ids are the canonical ones.
float value;
int flags;
};
+31 -5
View File
@@ -216,12 +216,8 @@ typedef enum _keycode_t {
// Not native to android
// Xbox 360 controller
KEYCODE_XBOX_A = 5001,
KEYCODE_XBOX_B = 5002,
KEYCODE_XBOX_X = 5003,
KEYCODE_XBOX_Y = 5004,
KEYCODE_XBOX_START = 5005,
KEYCODE_XBOX_SELECT = 5006,
KEYCODE_XBOX_BACK = 5006,
KEYCODE_XBOX_BUMPER_L = 5007,
KEYCODE_XBOX_BUMPER_R = 5008,
KEYCODE_XBOX_DPAD_UP = 5009,
@@ -234,3 +230,33 @@ typedef enum _keycode_t {
// KEYCODE_XBOX_HOME = NEWKEY, // this can't be read on PC, right?
} keycode_t;
enum AndroidJoystickAxis {
// Field descriptor #15 I
JOYSTICK_AXIS_X = 0,
JOYSTICK_AXIS_Y = 1,
JOYSTICK_AXIS_PRESSURE = 2,
JOYSTICK_AXIS_SIZE = 3,
JOYSTICK_AXIS_TOUCH_MAJOR = 4,
JOYSTICK_AXIS_TOUCH_MINOR = 5,
JOYSTICK_AXIS_TOOL_MAJOR = 6,
JOYSTICK_AXIS_TOOL_MINOR = 7,
JOYSTICK_AXIS_ORIENTATION = 8,
JOYSTICK_AXIS_VSCROLL = 9,
JOYSTICK_AXIS_HSCROLL = 10,
JOYSTICK_AXIS_Z = 11,
JOYSTICK_AXIS_RX = 12,
JOYSTICK_AXIS_RY = 13,
JOYSTICK_AXIS_RZ = 14,
JOYSTICK_AXIS_HAT_X = 15,
JOYSTICK_AXIS_HAT_Y = 16,
JOYSTICK_AXIS_LTRIGGER = 17,
JOYSTICK_AXIS_RTRIGGER = 18,
JOYSTICK_AXIS_THROTTLE = 19,
JOYSTICK_AXIS_RUDDER = 20,
JOYSTICK_AXIS_WHEEL = 21,
JOYSTICK_AXIS_GAS = 22,
JOYSTICK_AXIS_BRAKE = 23,
JOYSTICK_AXIS_DISTANCE = 24,
JOYSTICK_AXIS_TILT = 25,
};
+1
View File
@@ -230,6 +230,7 @@
<ClInclude Include="image\zim_save.h" />
<ClInclude Include="input\gesture_detector.h" />
<ClInclude Include="input\input_state.h" />
<ClInclude Include="input\keycodes.h" />
<ClInclude Include="json\json_writer.h" />
<ClInclude Include="math\curves.h" />
<ClInclude Include="math\expression_parser.h" />
+1
View File
@@ -278,6 +278,7 @@
<Filter>math</Filter>
</ClInclude>
<ClInclude Include="ext\jpge\jpgd.h" />
<ClInclude Include="input\keycodes.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp">
+12 -3
View File
@@ -54,12 +54,21 @@ void ScreenManager::switchToNext() {
}
void ScreenManager::touch(const TouchInput &touch) {
if (!stack_.empty()) {
if (!stack_.empty())
stack_.back().screen->touch(touch);
return;
}
}
void ScreenManager::key(const KeyInput &key) {
if (!stack_.empty())
stack_.back().screen->key(key);
}
void ScreenManager::axis(const AxisInput &axis) {
if (!stack_.empty())
stack_.back().screen->axis(axis);
}
void ScreenManager::render() {
if (!stack_.empty()) {
switch (stack_.back().flags) {
+4
View File
@@ -44,6 +44,8 @@ public:
virtual void deviceLost() {}
virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
virtual void touch(const TouchInput &touch) {}
virtual void key(const KeyInput &key) {}
virtual void axis(const AxisInput &touch) {}
virtual void sendMessage(const char *msg, const char *value) {}
ScreenManager *screenManager() { return screenManager_; }
@@ -91,6 +93,8 @@ public:
// Instant touch, separate from the update() mechanism.
void touch(const TouchInput &touch);
void key(const KeyInput &key);
void axis(const AxisInput &touch);
// Generic facility for gross hacks :P
void sendMessage(const char *msg, const char *value);