diff --git a/Common/UI/Root.cpp b/Common/UI/Root.cpp index 7c7f4c9301..9a1f08b201 100644 --- a/Common/UI/Root.cpp +++ b/Common/UI/Root.cpp @@ -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 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 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::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 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::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 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 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 diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 4e1b736991..c67b3cfe9e 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -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(); } diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 7e9e4f8489..4a45d54186 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -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; diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 60eb1749b0..0a75c06541 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -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; diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index 861fa71035..f1360cf0bd 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -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; } } diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h index b3a4ba074a..53ffc3089b 100644 --- a/Common/UI/ViewGroup.h +++ b/Common/UI/ViewGroup.h @@ -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; }; diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 0747181e1c..dea53c39ba 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -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() { diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 1885473da3..f912319949 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -22,6 +22,7 @@ #include #include #include +#include #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 keyEventLog_; + + UI::TextView *lastKeyEvents_ = nullptr; void CreateViews() override; + void UpdateLogView(); UI::EventReturn OnImmersiveModeChange(UI::EventParams &e); UI::EventReturn OnRenderingBackend(UI::EventParams &e); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index c285e677d2..254f8a31e7 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1260,6 +1260,8 @@ void MainScreen::CreateViews() { root_->SetDefaultFocusView(tabHolder_); } + root_->SetTag("mainroot"); + auto u = GetI18NCategory(I18NCat::UPGRADE); upgradeBar_ = 0; diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index a0f8178592..0a508faac0 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -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))); } diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index d8c3e0e898..b1cffa5bfe 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -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) { diff --git a/Windows/XinputDevice.h b/Windows/XinputDevice.h index 93347a6722..858e3fc3ed 100644 --- a/Windows/XinputDevice.h +++ b/Windows/XinputDevice.h @@ -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]{}; }; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index e1b05a58e3..c82f6ccd35 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -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; } diff --git a/android/src/org/ppsspp/ppsspp/InputDeviceState.java b/android/src/org/ppsspp/ppsspp/InputDeviceState.java index b221032cd6..88e7bc820e 100644 --- a/android/src/org/ppsspp/ppsspp/InputDeviceState.java +++ b/android/src/org/ppsspp/ppsspp/InputDeviceState.java @@ -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; } diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 740eef13bf..e23355c802 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -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); }