From cca613e785864a35cf7000f0d133cf3441b5c956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 1 May 2023 14:23:47 +0200 Subject: [PATCH 1/3] Initial work on queueing up UI events for processing. Key events excepted for now. --- Common/UI/UIScreen.cpp | 96 ++++++++++++++++++++++++++++++------------ Common/UI/UIScreen.h | 22 +++++++++- 2 files changed, 89 insertions(+), 29 deletions(-) diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 6b7a7b3c8693..c7a5f616fe67 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -60,6 +60,41 @@ void UIScreen::DoRecreateViews() { } } +void UIScreen::touch(const TouchInput &touch) { + if (root_) { + if (ClickDebug && (touch.flags & TOUCH_DOWN)) { + INFO_LOG(SYSTEM, "Touch down!"); + std::vector views; + root_->Query(touch.x, touch.y, views); + for (auto view : views) { + INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); + } + } + } + + std::lock_guard guard(eventQueueLock_); + QueuedEvent ev{}; + ev.type = QueuedEventType::TOUCH; + ev.touch = touch; + eventQueue_.push_back(ev); +} + +void UIScreen::axis(const AxisInput &axis) { + std::lock_guard guard(eventQueueLock_); + QueuedEvent ev{}; + ev.type = QueuedEventType::AXIS; + ev.axis = axis; + eventQueue_.push_back(ev); +} + +bool UIScreen::key(const KeyInput &key) { + if (root_ && !ignoreInput_) { + // TODO: Make key events async too. The return value is troublesome, though. + return UI::KeyEvent(key, root_); + } + return false; +} + void UIScreen::update() { bool vertical = UseVerticalLayout(); if (vertical != lastVertical_) { @@ -71,6 +106,39 @@ void UIScreen::update() { if (root_) { UpdateViewHierarchy(root_); + while (true) { + QueuedEvent ev{}; + { + std::lock_guard guard(eventQueueLock_); + if (!eventQueue_.empty()) { + ev = eventQueue_.front(); + eventQueue_.pop_front(); + } else { + break; + } + } + if (ignoreInput_) { + continue; + } + switch (ev.type) { + case QueuedEventType::KEY: + break; + case QueuedEventType::TOUCH: + if (ClickDebug && (ev.touch.flags & TOUCH_DOWN)) { + INFO_LOG(SYSTEM, "Touch down!"); + std::vector views; + root_->Query(ev.touch.x, ev.touch.y, views); + for (auto view : views) { + INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); + } + } + UI::TouchEvent(ev.touch, root_); + break; + case QueuedEventType::AXIS: + UI::AxisEvent(ev.axis, root_); + break; + } + } } } @@ -148,34 +216,6 @@ TouchInput UIScreen::transformTouch(const TouchInput &touch) { return updated; } -void UIScreen::touch(const TouchInput &touch) { - if (root_ && !ignoreInput_) { - if (ClickDebug && (touch.flags & TOUCH_DOWN)) { - INFO_LOG(SYSTEM, "Touch down!"); - std::vector views; - root_->Query(touch.x, touch.y, views); - for (auto view : views) { - INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); - } - } - - UI::TouchEvent(touch, root_); - } -} - -bool UIScreen::key(const KeyInput &key) { - if (root_ && !ignoreInput_) { - return UI::KeyEvent(key, root_); - } - return false; -} - -void UIScreen::axis(const AxisInput &axis) { - if (root_ && !ignoreInput_) { - UI::AxisEvent(axis, root_); - } -} - void UIScreen::TriggerFinish(DialogResult result) { // From here on, this dialog cannot receive input. ignoreInput_ = true; diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 1d5443a5fa8a..33c69034f972 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -1,6 +1,8 @@ #pragma once -#include +#include +#include +#include #include "Common/Math/lin/vec3.h" #include "Common/UI/Screen.h" @@ -13,6 +15,21 @@ namespace Draw { class DrawContext; } +enum class QueuedEventType : u8 { + KEY, + AXIS, + TOUCH, +}; + +struct QueuedEvent { + QueuedEventType type; + union { + TouchInput touch; + KeyInput key; + AxisInput axis; + }; +}; + class UIScreen : public Screen { public: UIScreen(); @@ -57,6 +74,9 @@ class UIScreen : public Screen { bool recreateViews_ = true; bool lastVertical_; + + std::mutex eventQueueLock_; + std::deque eventQueue_; }; class UIDialogScreen : public UIScreen { From 3715b1ffa1ceed566356188790c9d26eee1e77dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 26 May 2023 11:49:50 +0200 Subject: [PATCH 2/3] Split the functions up, so you don't have to care about the Unsync ones at all in most cases while still getting synced behavior. --- Common/UI/Root.cpp | 27 +++++++++++----------- Common/UI/Root.h | 15 +++++++++--- Common/UI/Screen.cpp | 12 +++++----- Common/UI/Screen.h | 7 +++--- Common/UI/UIScreen.cpp | 52 ++++++++++++++++++++++++++++++++++++------ Common/UI/UIScreen.h | 10 +++++--- UI/EmuScreen.cpp | 8 +++---- UI/EmuScreen.h | 8 ++++--- 8 files changed, 97 insertions(+), 42 deletions(-) diff --git a/Common/UI/Root.cpp b/Common/UI/Root.cpp index cb72afaa13f1..7c7f4c9301db 100644 --- a/Common/UI/Root.cpp +++ b/Common/UI/Root.cpp @@ -204,8 +204,8 @@ bool IsScrollKey(const KeyInput &input) { } } -bool KeyEvent(const KeyInput &key, ViewGroup *root) { - bool retval = false; +KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) { + KeyEventResult retval = KeyEventResult::PASS_THROUGH; // Ignore repeats for focus moves. if ((key.flags & (KEY_DOWN | KEY_IS_REPEAT)) == KEY_DOWN) { if (IsDPadKey(key) || IsScrollKey(key)) { @@ -218,13 +218,13 @@ bool KeyEvent(const KeyInput &key, ViewGroup *root) { // 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()) { - return false; + return KeyEventResult::IGNORE_KEY; } heldKeys.insert(hk); std::lock_guard lock(focusLock); focusMoves.push_back(key.keyCode); - retval = true; + retval = KeyEventResult::ACCEPT; } } if (key.flags & KEY_UP) { @@ -236,27 +236,28 @@ bool KeyEvent(const KeyInput &key, ViewGroup *root) { hk.triggerTime = 0.0; // irrelevant if (heldKeys.find(hk) != heldKeys.end()) { heldKeys.erase(hk); - retval = true; + retval = KeyEventResult::ACCEPT; } } } - retval = root->Key(key); - // Ignore volume keys and stuff here. Not elegant but need to propagate bools through the view hierarchy as well... switch (key.keyCode) { case NKCODE_VOLUME_DOWN: case NKCODE_VOLUME_UP: case NKCODE_VOLUME_MUTE: - retval = false; + retval = KeyEventResult::PASS_THROUGH; break; default: break; } - return retval; } +void KeyEvent(const KeyInput &key, ViewGroup *root) { + root->Key(key); +} + static void ProcessHeldKeys(ViewGroup *root) { double now = time_now_d(); @@ -283,16 +284,15 @@ static void ProcessHeldKeys(ViewGroup *root) { } } -bool TouchEvent(const TouchInput &touch, ViewGroup *root) { +void TouchEvent(const TouchInput &touch, ViewGroup *root) { focusForced = false; root->Touch(touch); if ((touch.flags & TOUCH_DOWN) && !focusForced) { EnableFocusMovement(false); } - return true; } -bool AxisEvent(const AxisInput &axis, ViewGroup *root) { +void AxisEvent(const AxisInput &axis, ViewGroup *root) { enum class DirState { NONE = 0, POS = 1, @@ -376,10 +376,11 @@ bool AxisEvent(const AxisInput &axis, ViewGroup *root) { } break; } + default: + break; } root->Axis(axis); - return true; } void UpdateViewHierarchy(ViewGroup *root) { diff --git a/Common/UI/Root.h b/Common/UI/Root.h index c19dc44acc7b..5a51eeb8e868 100644 --- a/Common/UI/Root.h +++ b/Common/UI/Root.h @@ -23,10 +23,19 @@ class ViewGroup; void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root, bool ignoreInsets); void UpdateViewHierarchy(ViewGroup *root); + +enum class KeyEventResult { + IGNORE_KEY, // Don't let it be processed. + PASS_THROUGH, // Let it be processed, but return false. + ACCEPT, // Let it be processed, but return true. +}; + // Hooks arrow keys for navigation -bool KeyEvent(const KeyInput &key, ViewGroup *root); -bool TouchEvent(const TouchInput &touch, ViewGroup *root); -bool AxisEvent(const AxisInput &axis, ViewGroup *root); +KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root); + +void KeyEvent(const KeyInput &key, ViewGroup *root); +void TouchEvent(const TouchInput &touch, ViewGroup *root); +void AxisEvent(const AxisInput &axis, ViewGroup *root); enum class UISound { SELECT = 0, diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 047ec0448371..0eeaa66214e9 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -82,11 +82,11 @@ void ScreenManager::touch(const TouchInput &touch) { if (touch.flags & TOUCH_RELEASE_ALL) { for (auto &layer : stack_) { Screen *screen = layer.screen; - layer.screen->touch(screen->transformTouch(touch)); + layer.screen->UnsyncTouch(screen->transformTouch(touch)); } } else if (!stack_.empty()) { Screen *screen = stack_.back().screen; - stack_.back().screen->touch(screen->transformTouch(touch)); + stack_.back().screen->UnsyncTouch(screen->transformTouch(touch)); } } @@ -96,10 +96,10 @@ bool ScreenManager::key(const KeyInput &key) { // Send key up to every screen layer, to avoid stuck keys. if (key.flags & KEY_UP) { for (auto &layer : stack_) { - result = layer.screen->key(key); + result = layer.screen->UnsyncKey(key); } } else if (!stack_.empty()) { - result = stack_.back().screen->key(key); + result = stack_.back().screen->UnsyncKey(key); } return result; } @@ -120,10 +120,10 @@ void ScreenManager::axis(const AxisInput &axis) { // Send center axis to every screen layer. if (axis.value == 0) { for (auto &layer : stack_) { - layer.screen->axis(axis); + layer.screen->UnsyncAxis(axis); } } else if (!stack_.empty()) { - stack_.back().screen->axis(axis); + stack_.back().screen->UnsyncAxis(axis); } } diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 2db57f1156aa..fca213fdb814 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -55,13 +55,14 @@ class Screen { virtual void postRender() {} virtual void resized() {} virtual void dialogFinished(const Screen *dialog, DialogResult result) {} - virtual void touch(const TouchInput &touch) {} - virtual bool key(const KeyInput &key) { return false; } - virtual void axis(const AxisInput &touch) {} virtual void sendMessage(const char *msg, const char *value) {} 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 RecreateViews() {} ScreenManager *screenManager() { return screenManager_; } diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index c7a5f616fe67..56dfd50d1506 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -61,6 +61,27 @@ void UIScreen::DoRecreateViews() { } void UIScreen::touch(const TouchInput &touch) { + if (!ignoreInput_) { + UI::TouchEvent(touch, root_); + } +} + +void UIScreen::axis(const AxisInput &axis) { + if (!ignoreInput_) { + UI::AxisEvent(axis, root_); + } +} + +bool UIScreen::key(const KeyInput &key) { + if (!ignoreInput_) { + UI::KeyEvent(key, root_); + return false; + } else { + return false; + } +} + +void UIScreen::UnsyncTouch(const TouchInput &touch) { if (root_) { if (ClickDebug && (touch.flags & TOUCH_DOWN)) { INFO_LOG(SYSTEM, "Touch down!"); @@ -79,7 +100,7 @@ void UIScreen::touch(const TouchInput &touch) { eventQueue_.push_back(ev); } -void UIScreen::axis(const AxisInput &axis) { +void UIScreen::UnsyncAxis(const AxisInput &axis) { std::lock_guard guard(eventQueueLock_); QueuedEvent ev{}; ev.type = QueuedEventType::AXIS; @@ -87,12 +108,28 @@ void UIScreen::axis(const AxisInput &axis) { eventQueue_.push_back(ev); } -bool UIScreen::key(const KeyInput &key) { - if (root_ && !ignoreInput_) { +bool UIScreen::UnsyncKey(const KeyInput &key) { + bool retval = false; + if (root_) { // TODO: Make key events async too. The return value is troublesome, though. - return UI::KeyEvent(key, root_); + switch (UI::UnsyncKeyEvent(key, root_)) { + case UI::KeyEventResult::ACCEPT: + retval = true; + break; + case UI::KeyEventResult::PASS_THROUGH: + retval = false; + break; + case UI::KeyEventResult::IGNORE_KEY: + return false; + } } - return false; + + std::lock_guard guard(eventQueueLock_); + QueuedEvent ev{}; + ev.type = QueuedEventType::KEY; + ev.key = key; + eventQueue_.push_back(ev); + return true; } void UIScreen::update() { @@ -122,6 +159,7 @@ void UIScreen::update() { } switch (ev.type) { case QueuedEventType::KEY: + key(ev.key); break; case QueuedEventType::TOUCH: if (ClickDebug && (ev.touch.flags & TOUCH_DOWN)) { @@ -132,10 +170,10 @@ void UIScreen::update() { INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); } } - UI::TouchEvent(ev.touch, root_); + touch(ev.touch); break; case QueuedEventType::AXIS: - UI::AxisEvent(ev.axis, root_); + axis(ev.axis); break; } } diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 33c69034f972..60eb1749b044 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -42,9 +42,13 @@ class UIScreen : public Screen { void deviceLost() override; void deviceRestored() override; - void touch(const TouchInput &touch) override; - bool key(const KeyInput &touch) override; - void axis(const AxisInput &touch) override; + virtual void touch(const TouchInput &touch); + virtual bool key(const KeyInput &touch); + virtual void axis(const AxisInput &touch); + + void UnsyncTouch(const TouchInput &touch) override; + bool UnsyncKey(const KeyInput &touch) override; + void UnsyncAxis(const AxisInput &touch) override; TouchInput transformTouch(const TouchInput &touch) override; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 623e606f1bb1..0d926f9531da 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -538,7 +538,7 @@ void EmuScreen::sendMessage(const char *message, const char *value) { } } -void EmuScreen::touch(const TouchInput &touch) { +void EmuScreen::UnsyncTouch(const TouchInput &touch) { Core_NotifyActivity(); if (chatMenu_ && chatMenu_->GetVisibility() == UI::V_VISIBLE) { @@ -802,11 +802,11 @@ void EmuScreen::onVKeyAnalog(int virtualKeyCode, float value) { limitMode = PSP_CoreParameter().analogFpsLimit == 60 ? FPSLimit::NORMAL : FPSLimit::ANALOG; } -bool EmuScreen::key(const KeyInput &key) { +bool EmuScreen::UnsyncKey(const KeyInput &key) { Core_NotifyActivity(); if (UI::IsFocusMovementEnabled()) { - if (UIScreen::key(key)) { + if (UIScreen::UnsyncKey(key)) { return true; } else if ((key.flags & KEY_DOWN) != 0 && UI::IsEscapeKey(key)) { if (chatMenu_) @@ -821,7 +821,7 @@ bool EmuScreen::key(const KeyInput &key) { return controlMapper_.Key(key, &pauseTrigger_); } -void EmuScreen::axis(const AxisInput &axis) { +void EmuScreen::UnsyncAxis(const AxisInput &axis) { Core_NotifyActivity(); return controlMapper_.Axis(axis); diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index a7ee1c23afec..7eda3988d413 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -50,9 +50,11 @@ class EmuScreen : public UIScreen { void sendMessage(const char *msg, const char *value) override; void resized() override; - void touch(const TouchInput &touch) override; - bool key(const KeyInput &key) override; - void axis(const AxisInput &axis) override; + // Note: Unlike your average boring UIScreen, here we override the Unsync* functions + // to get minimal latency and full control. We forward to UIScreen when needed. + void UnsyncTouch(const TouchInput &touch) override; + bool UnsyncKey(const KeyInput &key) override; + void UnsyncAxis(const AxisInput &axis) override; private: void CreateViews() override; From 50aa598ed1cec58d691d4cc4b4cc8164de77191c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 27 May 2023 00:31:10 +0200 Subject: [PATCH 3/3] Fix handling events in "empty" (no root) UIScreens --- Common/UI/UIScreen.cpp | 81 +++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 56dfd50d1506..3da8425421a9 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -61,19 +61,19 @@ void UIScreen::DoRecreateViews() { } void UIScreen::touch(const TouchInput &touch) { - if (!ignoreInput_) { + if (!ignoreInput_ && root_) { UI::TouchEvent(touch, root_); } } void UIScreen::axis(const AxisInput &axis) { - if (!ignoreInput_) { + if (!ignoreInput_ && root_) { UI::AxisEvent(axis, root_); } } bool UIScreen::key(const KeyInput &key) { - if (!ignoreInput_) { + if (!ignoreInput_ && root_) { UI::KeyEvent(key, root_); return false; } else { @@ -82,14 +82,12 @@ bool UIScreen::key(const KeyInput &key) { } void UIScreen::UnsyncTouch(const TouchInput &touch) { - if (root_) { - if (ClickDebug && (touch.flags & TOUCH_DOWN)) { - INFO_LOG(SYSTEM, "Touch down!"); - std::vector views; - root_->Query(touch.x, touch.y, views); - for (auto view : views) { - INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); - } + if (ClickDebug && root_ && (touch.flags & TOUCH_DOWN)) { + INFO_LOG(SYSTEM, "Touch down!"); + std::vector views; + root_->Query(touch.x, touch.y, views); + for (auto view : views) { + INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); } } @@ -143,39 +141,40 @@ void UIScreen::update() { if (root_) { UpdateViewHierarchy(root_); - while (true) { - QueuedEvent ev{}; - { - std::lock_guard guard(eventQueueLock_); - if (!eventQueue_.empty()) { - ev = eventQueue_.front(); - eventQueue_.pop_front(); - } else { - break; - } - } - if (ignoreInput_) { - continue; - } - switch (ev.type) { - case QueuedEventType::KEY: - key(ev.key); + } + + while (true) { + QueuedEvent ev{}; + { + std::lock_guard guard(eventQueueLock_); + if (!eventQueue_.empty()) { + ev = eventQueue_.front(); + eventQueue_.pop_front(); + } else { break; - case QueuedEventType::TOUCH: - if (ClickDebug && (ev.touch.flags & TOUCH_DOWN)) { - INFO_LOG(SYSTEM, "Touch down!"); - std::vector views; - root_->Query(ev.touch.x, ev.touch.y, views); - for (auto view : views) { - INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); - } + } + } + if (ignoreInput_) { + continue; + } + switch (ev.type) { + case QueuedEventType::KEY: + key(ev.key); + break; + case QueuedEventType::TOUCH: + if (ClickDebug && (ev.touch.flags & TOUCH_DOWN)) { + INFO_LOG(SYSTEM, "Touch down!"); + std::vector views; + root_->Query(ev.touch.x, ev.touch.y, views); + for (auto view : views) { + INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str()); } - touch(ev.touch); - break; - case QueuedEventType::AXIS: - axis(ev.axis); - break; } + touch(ev.touch); + break; + case QueuedEventType::AXIS: + axis(ev.axis); + break; } } }