mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-14 10:09:32 +01:00
Work towards full android joystick support.
This commit is contained in:
1 parent
f4578129ef
commit
1dec6e8154
14 files changed
+431
-231
No files matched your search
+78
-27
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
};
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user