Merge pull request #17670 from hrydgard/input-fixes

Control and menu navigation fixes
This commit is contained in:
Henrik Rydgård 2023-07-06 17:44:06 +02:00 committed by GitHub
commit 30f470a696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 176 additions and 143 deletions

View File

@ -133,21 +133,24 @@ void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root, bool ignoreInsets
root->Layout();
}
void MoveFocus(ViewGroup *root, FocusDirection direction) {
if (!GetFocusedView()) {
static void MoveFocus(ViewGroup *root, FocusDirection direction) {
View *focusedView = GetFocusedView();
if (!focusedView) {
// Nothing was focused when we got in here. Focus the first non-group in the hierarchy.
root->SetFocus();
return;
}
NeighborResult neigh(0, 0);
neigh = root->FindNeighbor(GetFocusedView(), direction, neigh);
NeighborResult neigh;
neigh = root->FindNeighbor(focusedView, direction, neigh);
if (neigh.view) {
neigh.view->SetFocus();
root->SubviewFocused(neigh.view);
PlayUISound(UISound::SELECT);
} else {
INFO_LOG(SCECTRL, "No neighbor view");
}
}
@ -204,7 +207,7 @@ bool IsScrollKey(const KeyInput &input) {
}
}
KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
static KeyEventResult KeyEventToFocusMoves(const KeyInput &key) {
KeyEventResult retval = KeyEventResult::PASS_THROUGH;
// Ignore repeats for focus moves.
if ((key.flags & (KEY_DOWN | KEY_IS_REPEAT)) == KEY_DOWN) {
@ -215,6 +218,7 @@ KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
hk.deviceId = key.deviceId;
hk.triggerTime = time_now_d() + repeatDelay;
std::lock_guard<std::mutex> lock(focusLock);
// Check if the key is already held. If it is, ignore it. This is to avoid
// multiple key repeat mechanisms colliding.
if (heldKeys.find(hk) != heldKeys.end()) {
@ -222,7 +226,7 @@ KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
}
heldKeys.insert(hk);
std::lock_guard<std::mutex> lock(focusLock);
INFO_LOG(SCECTRL, "focus move: %d", key.keyCode);
focusMoves.push_back(key.keyCode);
retval = KeyEventResult::ACCEPT;
}
@ -240,6 +244,11 @@ KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
}
}
}
return retval;
}
KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
KeyEventResult retval = KeyEventToFocusMoves(key);
// Ignore volume keys and stuff here. Not elegant but need to propagate bools through the view hierarchy as well...
switch (key.keyCode) {
@ -249,6 +258,7 @@ KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
retval = KeyEventResult::PASS_THROUGH;
break;
default:
retval = KeyEventResult::ACCEPT;
break;
}
return retval;
@ -258,32 +268,6 @@ void KeyEvent(const KeyInput &key, ViewGroup *root) {
root->Key(key);
}
static void ProcessHeldKeys(ViewGroup *root) {
double now = time_now_d();
restart:
for (std::set<HeldKey>::iterator iter = heldKeys.begin(); iter != heldKeys.end(); ++iter) {
if (iter->triggerTime < now) {
KeyInput key;
key.keyCode = iter->key;
key.deviceId = iter->deviceId;
key.flags = KEY_DOWN;
KeyEvent(key, root);
std::lock_guard<std::mutex> lock(focusLock);
focusMoves.push_back(key.keyCode);
// Cannot modify the current item when looping over a set, so let's do this instead.
HeldKey hk = *iter;
heldKeys.erase(hk);
hk.triggerTime = now + repeatInterval;
heldKeys.insert(hk);
goto restart;
}
}
}
void TouchEvent(const TouchInput &touch, ViewGroup *root) {
focusForced = false;
root->Touch(touch);
@ -292,6 +276,11 @@ void TouchEvent(const TouchInput &touch, ViewGroup *root) {
}
}
static void FakeKeyEvent(const KeyInput &key, ViewGroup *root) {
KeyEventToFocusMoves(key);
KeyEvent(key, root);
}
void AxisEvent(const AxisInput &axis, ViewGroup *root) {
enum class DirState {
NONE = 0,
@ -299,9 +288,7 @@ void AxisEvent(const AxisInput &axis, ViewGroup *root) {
NEG = 2,
};
struct PrevState {
PrevState() : x(DirState::NONE), y(DirState::NONE) {
}
PrevState() : x(DirState::NONE), y(DirState::NONE) {}
DirState x;
DirState y;
};
@ -320,18 +307,18 @@ void AxisEvent(const AxisInput &axis, ViewGroup *root) {
// Cannot use the remapper since this is for the menu, so we provide our own
// axis->button emulation here.
auto GenerateKeyFromAxis = [&](DirState old, DirState cur, InputKeyCode neg_key, InputKeyCode pos_key) {
auto GenerateKeyFromAxis = [=](DirState old, DirState cur, InputKeyCode neg_key, InputKeyCode pos_key) {
if (old == cur)
return;
if (old == DirState::POS) {
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_UP }, root);
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_UP }, root);
} else if (old == DirState::NEG) {
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_UP }, root);
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_UP }, root);
}
if (cur == DirState::POS) {
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_DOWN }, root);
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_DOWN }, root);
} else if (cur == DirState::NEG) {
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_DOWN }, root);
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_DOWN }, root);
}
};
@ -383,6 +370,31 @@ void AxisEvent(const AxisInput &axis, ViewGroup *root) {
root->Axis(axis);
}
static void ProcessHeldKeys(ViewGroup *root) {
double now = time_now_d();
restart:
for (std::set<HeldKey>::iterator iter = heldKeys.begin(); iter != heldKeys.end(); ++iter) {
if (iter->triggerTime < now) {
KeyInput key;
key.keyCode = iter->key;
key.deviceId = iter->deviceId;
key.flags = KEY_DOWN;
KeyEvent(key, root);
std::lock_guard<std::mutex> lock(focusLock);
focusMoves.push_back(key.keyCode);
// Cannot modify the current item when looping over a set, so let's do this instead.
HeldKey hk = *iter;
heldKeys.erase(hk);
hk.triggerTime = now + repeatInterval;
heldKeys.insert(hk);
goto restart;
}
}
}
void UpdateViewHierarchy(ViewGroup *root) {
ProcessHeldKeys(root);
frameCount++;
@ -396,6 +408,7 @@ void UpdateViewHierarchy(ViewGroup *root) {
std::lock_guard<std::mutex> lock(focusLock);
EnableFocusMovement(true);
if (!GetFocusedView()) {
// Find a view to focus.
View *defaultView = root->GetDefaultFocusView();
// Can't focus what you can't see.
if (defaultView && defaultView->GetVisibility() == V_VISIBLE) {
@ -405,6 +418,7 @@ void UpdateViewHierarchy(ViewGroup *root) {
}
root->SubviewFocused(GetFocusedView());
} else {
INFO_LOG(SCECTRL, "Processing focus moves.");
for (size_t i = 0; i < focusMoves.size(); i++) {
switch (focusMoves[i]) {
case NKCODE_DPAD_LEFT: MoveFocus(root, FOCUS_LEFT); break;
@ -418,6 +432,7 @@ void UpdateViewHierarchy(ViewGroup *root) {
}
}
}
INFO_LOG(SCECTRL, "Clearing focus moves.");
focusMoves.clear();
}
@ -425,4 +440,4 @@ void UpdateViewHierarchy(ViewGroup *root) {
DispatchEvents();
}
}
} // namespace UI

View File

@ -11,16 +11,15 @@
#include "Common/Log.h"
#include "Common/TimeUtil.h"
ScreenManager::ScreenManager() {
uiContext_ = 0;
dialogFinished_ = 0;
}
#include "Core/KeyMap.h"
ScreenManager::~ScreenManager() {
shutdown();
}
void ScreenManager::switchScreen(Screen *screen) {
// TODO: inputLock_ ?
if (!nextStack_.empty() && screen == nextStack_.front().screen) {
ERROR_LOG(SYSTEM, "Already switching to this screen");
return;
@ -53,9 +52,8 @@ void ScreenManager::update() {
stack_.back().screen->update();
}
if (overlayScreen_) {
overlayScreen_->update();
}
// NOTE: We should not update the OverlayScreen. In fact, we must never update more than one
// UIScreen in here, because we might end up double-processing the stuff in Root.cpp.
g_iconCache.FrameUpdate();
}

View File

@ -59,9 +59,9 @@ public:
virtual void deviceLost() {}
virtual void deviceRestored() {}
virtual void UnsyncTouch(const TouchInput &touch) {}
virtual bool UnsyncKey(const KeyInput &touch) { return false; }
virtual void UnsyncAxis(const AxisInput &touch) {}
virtual void UnsyncTouch(const TouchInput &touch) = 0;
virtual bool UnsyncKey(const KeyInput &touch) = 0;
virtual void UnsyncAxis(const AxisInput &touch) = 0;
virtual void RecreateViews() {}
@ -98,7 +98,6 @@ typedef void(*PostRenderCallback)(UIContext *ui, void *userdata);
class ScreenManager {
public:
ScreenManager();
virtual ~ScreenManager();
void switchScreen(Screen *screen);
@ -154,8 +153,8 @@ private:
void switchToNext();
void processFinishDialog();
UIContext *uiContext_;
Draw::DrawContext *thin3DContext_;
UIContext *uiContext_ = nullptr;
Draw::DrawContext *thin3DContext_ = nullptr;
PostRenderCallback postRenderCb_ = nullptr;
void *postRenderUserdata_ = nullptr;

View File

@ -43,12 +43,12 @@ public:
void deviceRestored() override;
virtual void touch(const TouchInput &touch);
virtual bool key(const KeyInput &touch);
virtual void axis(const AxisInput &touch);
virtual bool key(const KeyInput &key);
virtual void axis(const AxisInput &axis);
void UnsyncTouch(const TouchInput &touch) override;
bool UnsyncKey(const KeyInput &touch) override;
void UnsyncAxis(const AxisInput &touch) override;
bool UnsyncKey(const KeyInput &key) override;
void UnsyncAxis(const AxisInput &axis) override;
TouchInput transformTouch(const TouchInput &touch) override;

View File

@ -401,10 +401,13 @@ static float GetDirectionScore(int originIndex, const View *origin, View *destin
}
NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, NeighborResult result) {
if (!IsEnabled())
if (!IsEnabled()) {
INFO_LOG(SCECTRL, "Not enabled");
return result;
if (GetVisibility() != V_VISIBLE)
}
if (GetVisibility() != V_VISIBLE) {
return result;
}
// First, find the position of the view in the list.
int num = -1;
@ -415,24 +418,6 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
}
}
if (direction == FOCUS_PREV || direction == FOCUS_NEXT) {
switch (direction) {
case FOCUS_PREV:
// If view not found, no neighbor to find.
if (num == -1)
return NeighborResult(0, 0.0f);
return NeighborResult(views_[(num + views_.size() - 1) % views_.size()], 0.0f);
case FOCUS_NEXT:
// If view not found, no neighbor to find.
if (num == -1)
return NeighborResult(0, 0.0f);
return NeighborResult(views_[(num + 1) % views_.size()], 0.0f);
default:
return NeighborResult(nullptr, 0.0f);
}
}
switch (direction) {
case FOCUS_UP:
case FOCUS_LEFT:
@ -468,12 +453,22 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
}
return result;
}
case FOCUS_PREV_PAGE:
case FOCUS_NEXT_PAGE:
return FindScrollNeighbor(view, Point(INFINITY, INFINITY), direction, result);
case FOCUS_PREV:
// If view not found, no neighbor to find.
if (num == -1)
return NeighborResult(nullptr, 0.0f);
return NeighborResult(views_[(num + views_.size() - 1) % views_.size()], 0.0f);
case FOCUS_NEXT:
// If view not found, no neighbor to find.
if (num == -1)
return NeighborResult(0, 0.0f);
return NeighborResult(views_[(num + 1) % views_.size()], 0.0f);
default:
ERROR_LOG(SYSTEM, "Bad focus direction %d", (int)direction);
return result;
}
}

View File

@ -15,9 +15,8 @@ class AnchorTranslateTween;
class ScrollView;
struct NeighborResult {
NeighborResult() : view(0), score(0) {}
NeighborResult() : view(nullptr), score(0) {}
NeighborResult(View *v, float s) : view(v), score(s) {}
View *view;
float score;
};

View File

@ -630,6 +630,7 @@ void TouchTestScreen::touch(const TouchInput &touch) {
}
}
// TODO: Move this screen out into its own file.
void TouchTestScreen::CreateViews() {
using namespace UI;
@ -638,9 +639,7 @@ void TouchTestScreen::CreateViews() {
root_ = new LinearLayout(ORIENT_VERTICAL);
LinearLayout *theTwo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
lastLastKeyEvent_ = theTwo->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
lastLastKeyEvent_->SetTextColor(0x80FFFFFF); // semi-transparent
lastKeyEvent_ = theTwo->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
lastKeyEvents_ = theTwo->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
root_->Add(theTwo);
@ -671,38 +670,48 @@ extern int display_xres;
extern int display_yres;
#endif
void TouchTestScreen::UpdateLogView() {
while (keyEventLog_.size() > 8) {
keyEventLog_.erase(keyEventLog_.begin());
}
std::string text;
for (auto &iter : keyEventLog_) {
text += iter + "\n";
}
if (lastKeyEvents_) {
lastKeyEvents_->SetText(text);
}
}
bool TouchTestScreen::key(const KeyInput &key) {
UIScreen::key(key);
char buf[512];
snprintf(buf, sizeof(buf), "Keycode: %d Device ID: %d [%s%s%s%s]", key.keyCode, key.deviceId,
snprintf(buf, sizeof(buf), "%s (%d) Device ID: %d [%s%s%s%s]", KeyMap::GetKeyName(key.keyCode).c_str(), key.keyCode, key.deviceId,
(key.flags & KEY_IS_REPEAT) ? "REP" : "",
(key.flags & KEY_UP) ? "UP" : "",
(key.flags & KEY_DOWN) ? "DOWN" : "",
(key.flags & KEY_CHAR) ? "CHAR" : "");
if (lastLastKeyEvent_ && lastKeyEvent_) {
lastLastKeyEvent_->SetText(lastKeyEvent_->GetText());
lastKeyEvent_->SetText(buf);
}
keyEventLog_.push_back(buf);
UpdateLogView();
return true;
}
void TouchTestScreen::axis(const AxisInput &axis) {
// This is mainly to catch axis events that would otherwise get translated
// into arrow keys, since seeing keyboard arrow key events appear when using
// a controller would be confusing for the user.
// This just filters out accelerometer events. We show everything else.
if (IgnoreAxisForMapping(axis.axisId))
return;
const float AXIS_LOG_THRESHOLD = AXIS_BIND_THRESHOLD * 0.5f;
if (axis.value > AXIS_LOG_THRESHOLD || axis.value < -AXIS_LOG_THRESHOLD) {
char buf[512];
snprintf(buf, sizeof(buf), "Axis: %d (value %1.3f) Device ID: %d",
axis.axisId, axis.value, axis.deviceId);
// Null-check just in case they weren't created yet.
if (lastLastKeyEvent_ && lastKeyEvent_) {
lastLastKeyEvent_->SetText(lastKeyEvent_->GetText());
lastKeyEvent_->SetText(buf);
}
char buf[512];
snprintf(buf, sizeof(buf), "Axis: %s (%d) (value %1.3f) Device ID: %d",
KeyMap::GetAxisName(axis.axisId).c_str(), axis.axisId, axis.value, axis.deviceId);
keyEventLog_.push_back(buf);
if (keyEventLog_.size() > 8) {
keyEventLog_.erase(keyEventLog_.begin());
}
UpdateLogView();
}
void TouchTestScreen::render() {

View File

@ -22,6 +22,7 @@
#include <set>
#include <mutex>
#include <vector>
#include <string>
#include "Common/UI/View.h"
#include "Common/UI/UIScreen.h"
@ -165,10 +166,12 @@ protected:
};
TrackedTouch touches_[MAX_TOUCH_POINTS]{};
UI::TextView *lastKeyEvent_ = nullptr;
UI::TextView *lastLastKeyEvent_ = nullptr;
std::vector<std::string> keyEventLog_;
UI::TextView *lastKeyEvents_ = nullptr;
void CreateViews() override;
void UpdateLogView();
UI::EventReturn OnImmersiveModeChange(UI::EventParams &e);
UI::EventReturn OnRenderingBackend(UI::EventParams &e);

View File

@ -1260,6 +1260,8 @@ void MainScreen::CreateViews() {
root_->SetDefaultFocusView(tabHolder_);
}
root_->SetTag("mainroot");
auto u = GetI18NCategory(I18NCat::UPGRADE);
upgradeBar_ = 0;

View File

@ -286,5 +286,6 @@ std::string OnScreenMessagesView::DescribeText() const {
void OSDOverlayScreen::CreateViews() {
root_ = new UI::AnchorLayout();
root_->SetTag("OSDOverlayScreen");
root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams(0.0f, 0.0f, 0.0f, 0.0f)));
}

View File

@ -161,7 +161,7 @@ XinputDevice::~XinputDevice() {
}
struct Stick {
Stick (float x_, float y_, float scale) : x(x_ * scale), y(y_ * scale) {}
Stick(float x_, float y_, float scale) : x(x_ * scale), y(y_ * scale) {}
float x;
float y;
};
@ -202,9 +202,8 @@ int XinputDevice::UpdateState() {
}
void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATION &vibration) {
static bool notified[XUSER_MAX_COUNT]{};
if (!notified[pad]) {
notified[pad] = true;
if (!notified_[pad]) {
notified_[pad] = true;
#if !PPSSPP_PLATFORM(UWP)
XINPUT_CAPABILITIES_EX caps{};
if (PPSSPP_XInputGetCapabilitiesEx != nullptr && PPSSPP_XInputGetCapabilitiesEx(1, pad, 0, &caps) == ERROR_SUCCESS) {
@ -221,23 +220,26 @@ void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATIO
AxisInput axis;
axis.deviceId = (InputDeviceID)(DEVICE_ID_XINPUT_0 + pad);
auto sendAxis = [&](InputAxis axisId, float value) {
axis.axisId = axisId;
axis.value = value;
NativeAxis(axis);
auto sendAxis = [&](InputAxis axisId, float value, int axisIndex) {
if (value != prevAxisValue_[pad][axisIndex]) {
prevAxisValue_[pad][axisIndex] = value;
axis.axisId = axisId;
axis.value = value;
NativeAxis(axis);
}
};
sendAxis(JOYSTICK_AXIS_X, (float)state.Gamepad.sThumbLX / 32767.0f);
sendAxis(JOYSTICK_AXIS_Y, (float)state.Gamepad.sThumbLY / 32767.0f);
sendAxis(JOYSTICK_AXIS_Z, (float)state.Gamepad.sThumbRX / 32767.0f);
sendAxis(JOYSTICK_AXIS_RZ, (float)state.Gamepad.sThumbRY / 32767.0f);
sendAxis(JOYSTICK_AXIS_X, (float)state.Gamepad.sThumbLX / 32767.0f, 0);
sendAxis(JOYSTICK_AXIS_Y, (float)state.Gamepad.sThumbLY / 32767.0f, 1);
sendAxis(JOYSTICK_AXIS_Z, (float)state.Gamepad.sThumbRX / 32767.0f, 2);
sendAxis(JOYSTICK_AXIS_RZ, (float)state.Gamepad.sThumbRY / 32767.0f, 3);
if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.bLeftTrigger, state.Gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD)) {
sendAxis(JOYSTICK_AXIS_LTRIGGER, (float)state.Gamepad.bLeftTrigger / 255.0f);
sendAxis(JOYSTICK_AXIS_LTRIGGER, (float)state.Gamepad.bLeftTrigger / 255.0f, 4);
}
if (NormalizedDeadzoneDiffers(prevState[pad].Gamepad.bRightTrigger, state.Gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD)) {
sendAxis(JOYSTICK_AXIS_RTRIGGER, (float)state.Gamepad.bRightTrigger / 255.0f);
sendAxis(JOYSTICK_AXIS_RTRIGGER, (float)state.Gamepad.bRightTrigger / 255.0f, 5);
}
prevState[pad] = state;
@ -245,11 +247,11 @@ void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATIO
}
void XinputDevice::ApplyButtons(int pad, const XINPUT_STATE &state) {
u32 buttons = state.Gamepad.wButtons;
const u32 buttons = state.Gamepad.wButtons;
u32 downMask = buttons & (~prevButtons[pad]);
u32 upMask = (~buttons) & prevButtons[pad];
prevButtons[pad] = buttons;
const u32 downMask = buttons & (~prevButtons_[pad]);
const u32 upMask = (~buttons) & prevButtons_[pad];
prevButtons_[pad] = buttons;
for (int i = 0; i < xinput_ctrl_map_size; i++) {
if (downMask & xinput_ctrl_map[i].from) {

View File

@ -18,5 +18,7 @@ private:
XINPUT_STATE prevState[4]{};
XINPUT_VIBRATION prevVibration[4]{};
double prevVibrationTime = 0.0;
u32 prevButtons[4]{};
float prevAxisValue_[4][6]{};
bool notified_[XUSER_MAX_COUNT]{};
u32 prevButtons_[4]{};
};

View File

@ -1169,20 +1169,19 @@ PermissionStatus System_GetPermissionStatus(SystemPermission permission) {
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_touch
(JNIEnv *, jclass, float x, float y, int code, int pointerId) {
float scaledX = x * g_display.dpi_scale_x;
float scaledY = y * g_display.dpi_scale_y;
if (!renderer_inited)
return;
TouchInput touch;
touch.id = pointerId;
touch.x = scaledX;
touch.y = scaledY;
touch.x = x * g_display.dpi_scale_x;
touch.y = y * g_display.dpi_scale_y;
touch.flags = code;
NativeTouch(touch);
}
extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_keyDown(JNIEnv *, jclass, jint deviceId, jint key, jboolean isRepeat) {
if (!renderer_inited)
return false;
KeyInput keyInput;
keyInput.deviceId = (InputDeviceID)deviceId;
keyInput.keyCode = (InputKeyCode)key;
@ -1194,6 +1193,8 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_keyDown(JNIEnv *, jclass, j
}
extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_keyUp(JNIEnv *, jclass, jint deviceId, jint key) {
if (!renderer_inited)
return false;
KeyInput keyInput;
keyInput.deviceId = (InputDeviceID)deviceId;
keyInput.keyCode = (InputKeyCode)key;
@ -1207,10 +1208,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_joystickAxis(
return;
AxisInput axis;
axis.axisId = (InputAxis)axisId;
axis.deviceId = (InputDeviceID)deviceId;
axis.axisId = (InputAxis)axisId;
axis.value = value;
NativeAxis(axis);
}
@ -1218,7 +1218,6 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_mouseWheelEvent(
JNIEnv *env, jclass, jint stick, jfloat x, jfloat y) {
if (!renderer_inited)
return false;
// TODO: Support mousewheel for android
return true;
}

View File

@ -18,6 +18,7 @@ public class InputDeviceState {
private InputDevice mDevice;
private int[] mAxes;
private float[] mAxisPrevValue;
private int sources;
@ -94,6 +95,12 @@ public class InputDeviceState {
return str;
}
public static boolean inputSourceIsJoystick(int source) {
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK ||
(source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD ||
(source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
}
public InputDeviceState(InputDevice device) {
sources = device.getSources();
// First, anything that's a gamepad is a gamepad, even if it has a keyboard or pointer.
@ -101,8 +108,7 @@ public class InputDeviceState {
this.deviceId = NativeApp.DEVICE_ID_PAD_0;
} else if ((sources & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD && device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
this.deviceId = NativeApp.DEVICE_ID_KEYBOARD;
} else if ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK ||
(sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
} else if (inputSourceIsJoystick(sources)) {
this.deviceId = NativeApp.DEVICE_ID_PAD_0;
} else if ((sources & InputDevice.SOURCE_CLASS_POINTER) == InputDevice.SOURCE_CLASS_POINTER) {
this.deviceId = NativeApp.DEVICE_ID_MOUSE;
@ -118,6 +124,7 @@ public class InputDeviceState {
}
mAxes = new int[numAxes];
mAxisPrevValue = new float[numAxes];
int i = 0;
for (MotionRange range : device.getMotionRanges()) {
@ -132,25 +139,31 @@ public class InputDeviceState {
NativeApp.sendMessage("inputDeviceConnected", device.getName());
}
// This is called from dispatchKeyEvent.
public boolean onKeyDown(KeyEvent event) {
int keyCode = event.getKeyCode();
boolean repeat = event.getRepeatCount() > 0;
return NativeApp.keyDown(deviceId, keyCode, repeat);
}
// This is called from dispatchKeyEvent.
public boolean onKeyUp(KeyEvent event) {
int keyCode = event.getKeyCode();
return NativeApp.keyUp(deviceId, keyCode);
}
public boolean onJoystickMotion(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) {
if (!inputSourceIsJoystick(event.getSource())) {
Log.i(TAG, "Not a joystick event: source = " + event.getSource());
return false;
}
for (int i = 0; i < mAxes.length; i++) {
int axisId = mAxes[i];
float value = event.getAxisValue(axisId);
NativeApp.joystickAxis(deviceId, axisId, value);
if (value != mAxisPrevValue[i]) {
NativeApp.joystickAxis(deviceId, axisId, value);
mAxisPrevValue[i] = value;
}
}
return true;
}

View File

@ -984,11 +984,7 @@ public abstract class NativeActivity extends Activity {
// XInput device on Android returns source 1281 or 0x501, which equals GAMEPAD | KEYBOARD.
// Shield Remote returns 769 or 0x301 which equals DPAD | KEYBOARD.
// Don't disable passthrough if app at top level.
if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD ||
(sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK ||
(sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD))
{
if (InputDeviceState.inputSourceIsJoystick(sources)) {
passThrough = false;
}
@ -1083,7 +1079,7 @@ public abstract class NativeActivity extends Activity {
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) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
// Pass through / ignore
return super.onKeyDown(keyCode, event);
}