Skip to content

Commit

Permalink
Merge pull request #17375 from hrydgard/ui-event-queue
Browse files Browse the repository at this point in the history
Queue up input events for processing, except in-game. Avoids some potential threading issues/hangs.
  • Loading branch information
hrydgard authored May 27, 2023
2 parents 2675d6e + 50aa598 commit 4704bc9
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 64 deletions.
27 changes: 14 additions & 13 deletions Common/UI/Root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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<std::mutex> lock(focusLock);
focusMoves.push_back(key.keyCode);
retval = true;
retval = KeyEventResult::ACCEPT;
}
}
if (key.flags & KEY_UP) {
Expand All @@ -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();

Expand All @@ -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,
Expand Down Expand Up @@ -376,10 +376,11 @@ bool AxisEvent(const AxisInput &axis, ViewGroup *root) {
}
break;
}
default:
break;
}

root->Axis(axis);
return true;
}

void UpdateViewHierarchy(ViewGroup *root) {
Expand Down
15 changes: 12 additions & 3 deletions Common/UI/Root.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions Common/UI/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}

Expand All @@ -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;
}
Expand All @@ -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);
}
}

Expand Down
7 changes: 4 additions & 3 deletions Common/UI/Screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }
Expand Down
133 changes: 105 additions & 28 deletions Common/UI/UIScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,76 @@ void UIScreen::DoRecreateViews() {
}
}

void UIScreen::touch(const TouchInput &touch) {
if (!ignoreInput_ && root_) {
UI::TouchEvent(touch, root_);
}
}

void UIScreen::axis(const AxisInput &axis) {
if (!ignoreInput_ && root_) {
UI::AxisEvent(axis, root_);
}
}

bool UIScreen::key(const KeyInput &key) {
if (!ignoreInput_ && root_) {
UI::KeyEvent(key, root_);
return false;
} else {
return false;
}
}

void UIScreen::UnsyncTouch(const TouchInput &touch) {
if (ClickDebug && root_ && (touch.flags & TOUCH_DOWN)) {
INFO_LOG(SYSTEM, "Touch down!");
std::vector<UI::View *> views;
root_->Query(touch.x, touch.y, views);
for (auto view : views) {
INFO_LOG(SYSTEM, "%s", view->DescribeLog().c_str());
}
}

std::lock_guard<std::mutex> guard(eventQueueLock_);
QueuedEvent ev{};
ev.type = QueuedEventType::TOUCH;
ev.touch = touch;
eventQueue_.push_back(ev);
}

void UIScreen::UnsyncAxis(const AxisInput &axis) {
std::lock_guard<std::mutex> guard(eventQueueLock_);
QueuedEvent ev{};
ev.type = QueuedEventType::AXIS;
ev.axis = axis;
eventQueue_.push_back(ev);
}

bool UIScreen::UnsyncKey(const KeyInput &key) {
bool retval = false;
if (root_) {
// TODO: Make key events async too. The return value is troublesome, though.
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;
}
}

std::lock_guard<std::mutex> guard(eventQueueLock_);
QueuedEvent ev{};
ev.type = QueuedEventType::KEY;
ev.key = key;
eventQueue_.push_back(ev);
return true;
}

void UIScreen::update() {
bool vertical = UseVerticalLayout();
if (vertical != lastVertical_) {
Expand All @@ -72,6 +142,41 @@ void UIScreen::update() {
if (root_) {
UpdateViewHierarchy(root_);
}

while (true) {
QueuedEvent ev{};
{
std::lock_guard<std::mutex> 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);
break;
case QueuedEventType::TOUCH:
if (ClickDebug && (ev.touch.flags & TOUCH_DOWN)) {
INFO_LOG(SYSTEM, "Touch down!");
std::vector<UI::View *> 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;
}
}
}

void UIScreen::deviceLost() {
Expand Down Expand Up @@ -148,34 +253,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<UI::View *> 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;
Expand Down
32 changes: 28 additions & 4 deletions Common/UI/UIScreen.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <set>
#include <mutex>
#include <string>
#include <deque>

#include "Common/Math/lin/vec3.h"
#include "Common/UI/Screen.h"
Expand All @@ -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();
Expand All @@ -25,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;

Expand Down Expand Up @@ -57,6 +78,9 @@ class UIScreen : public Screen {

bool recreateViews_ = true;
bool lastVertical_;

std::mutex eventQueueLock_;
std::deque<QueuedEvent> eventQueue_;
};

class UIDialogScreen : public UIScreen {
Expand Down
Loading

0 comments on commit 4704bc9

Please sign in to comment.