mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-27 07:20:49 +00:00
Merge pull request #17670 from hrydgard/input-fixes
Control and menu navigation fixes
This commit is contained in:
commit
30f470a696
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -1260,6 +1260,8 @@ void MainScreen::CreateViews() {
|
||||
root_->SetDefaultFocusView(tabHolder_);
|
||||
}
|
||||
|
||||
root_->SetTag("mainroot");
|
||||
|
||||
auto u = GetI18NCategory(I18NCat::UPGRADE);
|
||||
|
||||
upgradeBar_ = 0;
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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]{};
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user