mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge pull request #21857 from hrydgard/more-input-refactor
Rework input queueing for UI, remove Unsync* events
This commit is contained in:
@@ -308,9 +308,6 @@ jobs:
|
||||
brew install ldid dpkg pillow
|
||||
# Check Xcode version
|
||||
xcodebuild -version
|
||||
# List available Xcode versions
|
||||
ls /Applications | grep Xcode
|
||||
sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer
|
||||
|
||||
- name: Create iOS Version.txt file
|
||||
if: matrix.id == 'ios'
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "Common/Common.h"
|
||||
|
||||
// The Native App API.
|
||||
//
|
||||
// Implement these functions and you've got a native app. These are called
|
||||
@@ -17,6 +19,19 @@ struct AxisInput;
|
||||
class GraphicsContext;
|
||||
class AudioBackend;
|
||||
|
||||
enum class KeyModifier {
|
||||
NONE = 0,
|
||||
LCTRL = 1,
|
||||
RCTRL = 2,
|
||||
LSHIFT = 4,
|
||||
RSHIFT = 8,
|
||||
LALT = 16,
|
||||
RALT = 32,
|
||||
LMETA = 64,
|
||||
RMETA = 128,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(KeyModifier);
|
||||
|
||||
// The first function to get called, just write strings to the two pointers.
|
||||
// This might get called multiple times in some implementations, you must be able to handle that.
|
||||
void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, bool *landscape, std::string *version);
|
||||
@@ -55,6 +70,7 @@ bool NativeKey(const KeyInput &key);
|
||||
void NativeAxis(const AxisInput *axis, size_t count);
|
||||
void NativeAccelerometer(float tiltX, float tiltY, float tiltZ);
|
||||
void NativeMouseDelta(float dx, float dy);
|
||||
KeyModifier NativeGetKeyModifiers(); // TODO: Move this to input handling somewhere? But don't want it in each backend...
|
||||
|
||||
// Called when it's process a frame, including rendering. If the device can keep up, this
|
||||
// will be called sixty times per second. Main thread.
|
||||
|
||||
@@ -51,11 +51,11 @@ PopupScreen::PopupScreen(std::string_view title, std::string_view button1, std::
|
||||
alpha_ = 0.0f; // inherited
|
||||
}
|
||||
|
||||
void PopupScreen::touch(const TouchInput &touch) {
|
||||
bool PopupScreen::touch(const TouchInput &touch) {
|
||||
if (!box_ || (touch.flags & TouchInputFlags::DOWN) == 0) {
|
||||
// Handle down-presses here.
|
||||
UIDialogScreen::touch(touch);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extra bounds to avoid closing the dialog while trying to aim for something
|
||||
@@ -65,7 +65,7 @@ void PopupScreen::touch(const TouchInput &touch) {
|
||||
TriggerFinish(DR_CANCEL);
|
||||
}
|
||||
|
||||
UIDialogScreen::touch(touch);
|
||||
return UIDialogScreen::touch(touch);
|
||||
}
|
||||
|
||||
bool PopupScreen::key(const KeyInput &key) {
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
virtual void CreatePopupContents(UI::ViewGroup *parent) = 0;
|
||||
void CreateViews() override;
|
||||
bool isTransparent() const override { return true; }
|
||||
void touch(const TouchInput &touch) override;
|
||||
bool touch(const TouchInput &touch) override;
|
||||
bool key(const KeyInput &key) override;
|
||||
|
||||
void TriggerFinish(DialogResult result) override;
|
||||
|
||||
+1
-22
@@ -193,7 +193,7 @@ bool IsScrollKey(const KeyInput &input) {
|
||||
}
|
||||
}
|
||||
|
||||
static KeyEventResult KeyEventToFocusMoves(const KeyInput &key) {
|
||||
KeyEventResult KeyEventToFocusMoves(const KeyInput &key) {
|
||||
KeyEventResult retval = KeyEventResult::PASS_THROUGH;
|
||||
// Ignore repeats for focus moves.
|
||||
if ((key.flags & KeyInputFlags::DOWN) && !(key.flags & KeyInputFlags::IS_REPEAT)) {
|
||||
@@ -231,27 +231,6 @@ static KeyEventResult KeyEventToFocusMoves(const KeyInput &key) {
|
||||
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) {
|
||||
case NKCODE_VOLUME_DOWN:
|
||||
case NKCODE_VOLUME_UP:
|
||||
case NKCODE_VOLUME_MUTE:
|
||||
retval = KeyEventResult::PASS_THROUGH;
|
||||
break;
|
||||
default:
|
||||
if (!(key.flags & KeyInputFlags::IS_REPEAT)) {
|
||||
// If a repeat, we follow what KeyEventToFocusMoves set it to.
|
||||
// Otherwise we signal that we used the key, always.
|
||||
retval = KeyEventResult::ACCEPT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool KeyEvent(const KeyInput &key, ViewGroup *root) {
|
||||
return root->Key(key);
|
||||
}
|
||||
|
||||
+1
-2
@@ -34,8 +34,7 @@ enum class KeyEventResult {
|
||||
ACCEPT, // Let it be processed, but return true.
|
||||
};
|
||||
|
||||
// Hooks arrow keys for navigation
|
||||
KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root);
|
||||
KeyEventResult KeyEventToFocusMoves(const KeyInput &key);
|
||||
|
||||
bool KeyEvent(const KeyInput &key, ViewGroup *root);
|
||||
void TouchEvent(const TouchInput &touch, ViewGroup *root);
|
||||
|
||||
+80
-44
@@ -59,7 +59,6 @@ void ScreenManager::switchScreen(Screen *screen) {
|
||||
ERROR_LOG(Log::UI, "Can't switch to empty screen");
|
||||
return;
|
||||
}
|
||||
// TODO: inputLock_ ?
|
||||
|
||||
INFO_LOG(Log::UI, "ScreenManager::switchScreen('%s')", screen->tag());
|
||||
|
||||
@@ -106,8 +105,6 @@ void ScreenManager::cancelScreensAbove(Screen *screen) {
|
||||
}
|
||||
|
||||
void ScreenManager::update() {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
|
||||
if (cancelScreensAbove_) {
|
||||
bool found = false;
|
||||
for (int i = (int)stack_.size() - 1; i >= 0; i--) {
|
||||
@@ -129,14 +126,75 @@ void ScreenManager::update() {
|
||||
// NOTE: This is not a full UIScreen update, to avoid double global event processing.
|
||||
overlayScreen_->update();
|
||||
}
|
||||
// The background screen doesn't need updating.
|
||||
|
||||
// Only the front screen gets update().
|
||||
if (!stack_.empty()) {
|
||||
stack_.back().screen->update();
|
||||
passInputToMapper_ = stack_.back().screen->PassInputToMapper();
|
||||
}
|
||||
|
||||
// Process queued events.
|
||||
|
||||
std::deque<QueuedEvent> events;
|
||||
{
|
||||
std::lock_guard<std::mutex> eventGuard(eventQueueLock_);
|
||||
events = std::move(eventQueue_);
|
||||
eventQueue_.clear();
|
||||
}
|
||||
|
||||
for (const QueuedEvent &ev : events) {
|
||||
switch (ev.type) {
|
||||
case QueuedEventType::TOUCH:
|
||||
{
|
||||
const TouchInput &touch = ev.touch;
|
||||
// Send release all events to every screen layer.
|
||||
if (touch.flags & TouchInputFlags::RELEASE_ALL) {
|
||||
for (auto &layer : stack_) {
|
||||
Screen *screen = layer.screen;
|
||||
layer.screen->touch(screen->transformTouch(touch));
|
||||
}
|
||||
} else if (!stack_.empty()) {
|
||||
// Let the overlay know about touch-downs, to be able to dismiss popups.
|
||||
bool skip = false;
|
||||
if (overlayScreen_ && (touch.flags & TouchInputFlags::DOWN)) {
|
||||
skip = overlayScreen_->touch(overlayScreen_->transformTouch(touch));
|
||||
}
|
||||
if (!skip) {
|
||||
Screen *screen = stack_.back().screen;
|
||||
stack_.back().screen->touch(screen->transformTouch(touch));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QueuedEventType::KEY:
|
||||
{
|
||||
const KeyInput &key = ev.key;
|
||||
// Send key up to every screen layer, to avoid stuck keys.
|
||||
if (key.flags & KeyInputFlags::UP) {
|
||||
for (auto &layer : stack_) {
|
||||
layer.screen->key(key);
|
||||
}
|
||||
} else if (!stack_.empty()) {
|
||||
stack_.back().screen->key(key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QueuedEventType::AXIS:
|
||||
{
|
||||
const AxisInput &axis = ev.axis;
|
||||
if (!stack_.empty()) {
|
||||
stack_.back().screen->axis(axis);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ERROR_LOG(Log::UI, "Unknown queued event type: %d", (int)ev.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenManager::switchToNext() {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
if (nextStack_.empty()) {
|
||||
ERROR_LOG(Log::System, "switchToNext: No nextStack_!");
|
||||
}
|
||||
@@ -160,44 +218,28 @@ void ScreenManager::switchToNext() {
|
||||
}
|
||||
|
||||
void ScreenManager::touch(const TouchInput &touch) {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
// Send release all events to every screen layer.
|
||||
if (touch.flags & TouchInputFlags::RELEASE_ALL) {
|
||||
for (auto &layer : stack_) {
|
||||
Screen *screen = layer.screen;
|
||||
layer.screen->UnsyncTouch(screen->transformTouch(touch));
|
||||
}
|
||||
} else if (!stack_.empty()) {
|
||||
// Let the overlay know about touch-downs, to be able to dismiss popups.
|
||||
bool skip = false;
|
||||
if (overlayScreen_ && (touch.flags & TouchInputFlags::DOWN)) {
|
||||
skip = overlayScreen_->UnsyncTouch(overlayScreen_->transformTouch(touch));
|
||||
}
|
||||
if (!skip) {
|
||||
Screen *screen = stack_.back().screen;
|
||||
stack_.back().screen->UnsyncTouch(screen->transformTouch(touch));
|
||||
}
|
||||
}
|
||||
QueuedEvent ev{};
|
||||
ev.type = QueuedEventType::TOUCH;
|
||||
ev.touch = touch;
|
||||
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
||||
eventQueue_.push_back(ev);
|
||||
}
|
||||
|
||||
bool ScreenManager::key(const KeyInput &key) {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
bool result = false;
|
||||
// Send key up to every screen layer, to avoid stuck keys.
|
||||
if (key.flags & KeyInputFlags::UP) {
|
||||
for (auto &layer : stack_) {
|
||||
result = layer.screen->UnsyncKey(key);
|
||||
}
|
||||
} else if (!stack_.empty()) {
|
||||
result = stack_.back().screen->UnsyncKey(key);
|
||||
}
|
||||
return result;
|
||||
void ScreenManager::key(const KeyInput &key) {
|
||||
QueuedEvent ev{};
|
||||
ev.type = QueuedEventType::KEY;
|
||||
ev.key = key;
|
||||
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
||||
eventQueue_.push_back(ev);
|
||||
}
|
||||
|
||||
void ScreenManager::axis(const AxisInput *axes, size_t count) {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
if (!stack_.empty()) {
|
||||
stack_.back().screen->UnsyncAxis(axes, count);
|
||||
QueuedEvent ev{};
|
||||
ev.type = QueuedEventType::AXIS;
|
||||
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
ev.axis = axes[i];
|
||||
eventQueue_.push_back(ev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +256,6 @@ void ScreenManager::deviceRestored(Draw::DrawContext *draw) {
|
||||
|
||||
void ScreenManager::resized() {
|
||||
INFO_LOG(Log::UI, "ScreenManager::resized(dp: %dx%d)", g_display.dp_xres, g_display.dp_yres);
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
// Have to notify the whole stack, otherwise there will be problems when going back
|
||||
// to non-top screens.
|
||||
for (auto &layer : stack_) {
|
||||
@@ -359,7 +400,6 @@ void ScreenManager::sendMessage(UIMessage message, const char *value) {
|
||||
}
|
||||
|
||||
void ScreenManager::shutdown() {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
for (const auto &layer : stack_)
|
||||
delete layer.screen;
|
||||
stack_.clear();
|
||||
@@ -373,7 +413,6 @@ void ScreenManager::shutdown() {
|
||||
}
|
||||
|
||||
void ScreenManager::push(Screen *screen, int layerFlags) {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
screen->setScreenManager(this);
|
||||
if (screen->isTransparent()) {
|
||||
layerFlags |= LAYER_TRANSPARENT;
|
||||
@@ -404,7 +443,6 @@ void ScreenManager::push(Screen *screen, int layerFlags) {
|
||||
}
|
||||
|
||||
void ScreenManager::pop() {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
if (!stack_.empty()) {
|
||||
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
|
||||
|
||||
@@ -420,7 +458,6 @@ void ScreenManager::pop() {
|
||||
}
|
||||
|
||||
void ScreenManager::RecreateAllViews() {
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
for (auto &layer : stack_) {
|
||||
layer.screen->RecreateViews();
|
||||
}
|
||||
@@ -454,7 +491,6 @@ Screen *ScreenManager::dialogParent(const Screen *dialog) const {
|
||||
void ScreenManager::processFinishDialog() {
|
||||
if (dialogFinished_) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
// Another dialog may have been pushed before the render, so search for it.
|
||||
Screen *caller = dialogParent(dialogFinished_);
|
||||
bool erased = false;
|
||||
|
||||
+42
-8
@@ -16,6 +16,7 @@
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -75,6 +76,30 @@ enum class ViewLayoutMode {
|
||||
IgnoreBottomInset,
|
||||
};
|
||||
|
||||
enum class InputMode {
|
||||
None = 0,
|
||||
Keyboard = 1,
|
||||
Mouse = 2,
|
||||
Other = 4,
|
||||
ImDebuggerToggle = 8, // ugly hack. debugger and pause goes through.
|
||||
};
|
||||
ENUM_CLASS_BITOPS(InputMode);
|
||||
|
||||
enum class QueuedEventType : u8 {
|
||||
KEY,
|
||||
AXIS,
|
||||
TOUCH,
|
||||
};
|
||||
|
||||
struct QueuedEvent {
|
||||
QueuedEventType type;
|
||||
union {
|
||||
TouchInput touch;
|
||||
KeyInput key;
|
||||
AxisInput axis;
|
||||
};
|
||||
};
|
||||
|
||||
class Screen {
|
||||
public:
|
||||
Screen() = default;
|
||||
@@ -94,14 +119,16 @@ public:
|
||||
|
||||
virtual void focusChanged(ScreenFocusChange focusChange);
|
||||
|
||||
// Return value of UnsyncTouch is only used to let the overlay screen block touches.
|
||||
virtual bool UnsyncTouch(const TouchInput &touch) = 0;
|
||||
// Return value of UnsyncKey is used to not block certain system keys like volume when unhandled, on Android.
|
||||
virtual bool UnsyncKey(const KeyInput &touch) = 0;
|
||||
virtual void UnsyncAxis(const AxisInput *axes, size_t count) = 0;
|
||||
virtual bool touch(const TouchInput &touch) = 0;
|
||||
virtual bool key(const KeyInput &key) = 0;
|
||||
virtual void axis(const AxisInput &axis) = 0;
|
||||
|
||||
virtual void RecreateViews() {}
|
||||
|
||||
// NOTE: This is polled every frame, not called on every input event. This is
|
||||
// a step towards eventually removing the ScreenManager input mutex.
|
||||
virtual InputMode PassInputToMapper() const { return InputMode::None; }
|
||||
|
||||
ScreenManager *screenManager() { return screenManager_; }
|
||||
const ScreenManager *screenManager() const { return screenManager_; }
|
||||
void setScreenManager(ScreenManager *sm) { screenManager_ = sm; }
|
||||
@@ -136,7 +163,7 @@ enum {
|
||||
LAYER_TRANSPARENT = 2,
|
||||
};
|
||||
|
||||
typedef void(*PostRenderCallback)(UIContext *ui, void *userdata);
|
||||
typedef void (*PostRenderCallback)(UIContext *ui, void *userdata);
|
||||
|
||||
class ScreenManager {
|
||||
public:
|
||||
@@ -173,7 +200,7 @@ public:
|
||||
|
||||
// Instant touch, separate from the update() mechanism.
|
||||
void touch(const TouchInput &touch);
|
||||
bool key(const KeyInput &key);
|
||||
void key(const KeyInput &key);
|
||||
void axis(const AxisInput *axes, size_t count);
|
||||
|
||||
void sendMessage(UIMessage message, const char *value);
|
||||
@@ -192,7 +219,9 @@ public:
|
||||
// Will delete any existing overlay screen.
|
||||
void SetBackgroundOverlayScreens(Screen *backgroundScreen, Screen *overlayScreen);
|
||||
|
||||
std::recursive_mutex inputLock_;
|
||||
InputMode PassInputToMapper() const {
|
||||
return passInputToMapper_;
|
||||
}
|
||||
|
||||
private:
|
||||
void pop();
|
||||
@@ -225,4 +254,9 @@ private:
|
||||
std::vector<Layer> nextStack_;
|
||||
|
||||
std::unordered_map<int64_t, int> lastAxis_;
|
||||
|
||||
std::mutex eventQueueLock_;
|
||||
std::deque<QueuedEvent> eventQueue_;
|
||||
|
||||
InputMode passInputToMapper_ = InputMode::None;
|
||||
};
|
||||
|
||||
+11
-91
@@ -33,8 +33,6 @@ void UIScreen::DoRecreateViews() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(screenManager()->inputLock_);
|
||||
|
||||
UI::PersistMap persisted;
|
||||
bool persisting = root_ != nullptr;
|
||||
if (persisting) {
|
||||
@@ -66,10 +64,20 @@ void UIScreen::DoRecreateViews() {
|
||||
WipeRequesterToken();
|
||||
}
|
||||
|
||||
void UIScreen::touch(const TouchInput &touch) {
|
||||
bool UIScreen::touch(const TouchInput &touch) {
|
||||
if (ClickDebug && root_ && (touch.flags & TouchInputFlags::DOWN)) {
|
||||
INFO_LOG(Log::System, "Touch down!");
|
||||
std::vector<UI::View *> views;
|
||||
root_->Query(touch.x, touch.y, views);
|
||||
for (auto view : views) {
|
||||
INFO_LOG(Log::System, "%s", view->DescribeLog().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreInput_ && root_) {
|
||||
UI::TouchEvent(touch, root_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UIScreen::axis(const AxisInput &axis) {
|
||||
@@ -86,59 +94,6 @@ bool UIScreen::key(const KeyInput &key) {
|
||||
}
|
||||
}
|
||||
|
||||
bool UIScreen::UnsyncTouch(const TouchInput &touch) {
|
||||
if (ClickDebug && root_ && (touch.flags & TouchInputFlags::DOWN)) {
|
||||
INFO_LOG(Log::System, "Touch down!");
|
||||
std::vector<UI::View *> views;
|
||||
root_->Query(touch.x, touch.y, views);
|
||||
for (auto view : views) {
|
||||
INFO_LOG(Log::System, "%s", view->DescribeLog().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
QueuedEvent ev{};
|
||||
ev.type = QueuedEventType::TOUCH;
|
||||
ev.touch = touch;
|
||||
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
||||
eventQueue_.push_back(ev);
|
||||
return false;
|
||||
}
|
||||
|
||||
void UIScreen::UnsyncAxis(const AxisInput *axes, size_t count) {
|
||||
QueuedEvent ev{};
|
||||
ev.type = QueuedEventType::AXIS;
|
||||
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
ev.axis = axes[i];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
QueuedEvent ev{};
|
||||
ev.type = QueuedEventType::KEY;
|
||||
ev.key = key;
|
||||
|
||||
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
||||
eventQueue_.push_back(ev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void UIScreen::update() {
|
||||
DeviceOrientation orientation = GetDeviceOrientation();
|
||||
if (orientation != lastOrientation_) {
|
||||
@@ -148,41 +103,6 @@ void UIScreen::update() {
|
||||
|
||||
DoRecreateViews();
|
||||
|
||||
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 & TouchInputFlags::DOWN)) {
|
||||
INFO_LOG(Log::System, "Touch down!");
|
||||
std::vector<UI::View *> views;
|
||||
root_->Query(ev.touch.x, ev.touch.y, views);
|
||||
for (auto view : views) {
|
||||
INFO_LOG(Log::System, "%s", view->DescribeLog().c_str());
|
||||
}
|
||||
}
|
||||
touch(ev.touch);
|
||||
break;
|
||||
case QueuedEventType::AXIS:
|
||||
axis(ev.axis);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (root_) {
|
||||
DialogResult result = UpdateViewHierarchy(root_);
|
||||
if (result != DR_NONE) {
|
||||
|
||||
+3
-26
@@ -17,21 +17,6 @@ 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();
|
||||
@@ -42,13 +27,9 @@ public:
|
||||
void deviceLost() override;
|
||||
void deviceRestored(Draw::DrawContext *draw) override;
|
||||
|
||||
virtual void touch(const TouchInput &touch);
|
||||
virtual bool key(const KeyInput &key);
|
||||
virtual void axis(const AxisInput &axis);
|
||||
|
||||
bool UnsyncTouch(const TouchInput &touch) override;
|
||||
bool UnsyncKey(const KeyInput &key) override;
|
||||
void UnsyncAxis(const AxisInput *axes, size_t count) override;
|
||||
bool touch(const TouchInput &touch) override;
|
||||
bool key(const KeyInput &key) override;
|
||||
void axis(const AxisInput &axis) override;
|
||||
|
||||
TouchInput transformTouch(const TouchInput &touch) override;
|
||||
|
||||
@@ -92,10 +73,6 @@ protected:
|
||||
|
||||
bool recreateViews_ = true;
|
||||
DeviceOrientation lastOrientation_ = DeviceOrientation::Landscape;
|
||||
|
||||
private:
|
||||
std::mutex eventQueueLock_;
|
||||
std::deque<QueuedEvent> eventQueue_;
|
||||
};
|
||||
|
||||
class UIDialogScreen : public UIScreen {
|
||||
|
||||
@@ -339,7 +339,7 @@ float GetTargetScore(const Point2D &originPos, int originIndex, const View *orig
|
||||
const float vertOverlap = VerticalOverlap(origin->GetBounds(), destination->GetBounds());
|
||||
if (horizOverlap == 1.0f && vertOverlap == 1.0f) {
|
||||
if (direction != FocusMove::PREV_PAGE && direction != FocusMove::NEXT_PAGE) {
|
||||
INFO_LOG(Log::UI, "Contain overlap: %s, %s", origin->Tag().c_str(), destination->Tag().c_str());
|
||||
// INFO_LOG(Log::UI, "Contain overlap: %s, %s", origin->Tag().c_str(), destination->Tag().c_str());
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ static void load_integration_callback(int result, const char *error_message, rc_
|
||||
}
|
||||
case RC_MISSING_VALUE:
|
||||
// This is fine, proceeding to login.
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, ac->T("RAIntegration is enabled, but %1 was not found."));
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, ApplySafeSubstitutions(ac->T("RAIntegration is enabled, but %1 was not found."), RAINTEGRATION_FILENAME));
|
||||
break;
|
||||
case RC_ABORTED:
|
||||
// This is fine(-ish), proceeding to login.
|
||||
|
||||
@@ -514,25 +514,9 @@ void AnalogCalibrationScreen::update() {
|
||||
UIScreen::update();
|
||||
}
|
||||
|
||||
bool AnalogCalibrationScreen::key(const KeyInput &key) {
|
||||
bool retval = UIScreen::key(key);
|
||||
|
||||
// Allow testing auto-rotation. If it collides with UI keys, too bad.
|
||||
g_controlMapper.Key(key);
|
||||
|
||||
if (UI::IsEscapeKey(key)) {
|
||||
TriggerFinish(DR_BACK);
|
||||
return retval;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void AnalogCalibrationScreen::axis(const AxisInput &axis) {
|
||||
// We DON'T call UIScreen::Axis here! Otherwise it'll try to move the UI focus around.
|
||||
// UIScreen::axis(axis);
|
||||
|
||||
// Instead we just send the input directly to the mapper, that we'll visualize.
|
||||
g_controlMapper.Axis(&axis, 1);
|
||||
}
|
||||
|
||||
std::string_view AnalogCalibrationScreen::GetTitle() const {
|
||||
|
||||
@@ -130,11 +130,14 @@ public:
|
||||
AnalogCalibrationScreen(const Path &gamePath);
|
||||
~AnalogCalibrationScreen();
|
||||
|
||||
bool key(const KeyInput &key) override;
|
||||
void axis(const AxisInput &axis) override;
|
||||
|
||||
void update() override;
|
||||
|
||||
InputMode PassInputToMapper() const {
|
||||
return InputMode::Other | InputMode::Keyboard | InputMode::Mouse;
|
||||
}
|
||||
|
||||
const char *tag() const override { return "AnalogSetup"; }
|
||||
|
||||
protected:
|
||||
|
||||
+2
-1
@@ -629,7 +629,7 @@ void FrameDumpTestScreen::update() {
|
||||
}
|
||||
}
|
||||
|
||||
void TouchTestScreen::touch(const TouchInput &touch) {
|
||||
bool TouchTestScreen::touch(const TouchInput &touch) {
|
||||
UIBaseDialogScreen::touch(touch);
|
||||
if (touch.flags & TouchInputFlags::DOWN) {
|
||||
bool found = false;
|
||||
@@ -678,6 +678,7 @@ void TouchTestScreen::touch(const TouchInput &touch) {
|
||||
WARN_LOG(Log::System, "Touch release without touch down");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Move this screen out into its own file.
|
||||
|
||||
+1
-1
@@ -169,7 +169,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void touch(const TouchInput &touch) override;
|
||||
bool touch(const TouchInput &touch) override;
|
||||
void DrawForeground(UIContext &dc) override;
|
||||
|
||||
bool key(const KeyInput &key) override;
|
||||
|
||||
+352
-366
@@ -21,6 +21,7 @@
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
#include "Common/System/NativeApp.h"
|
||||
#include "Common/Render/TextureAtlas.h"
|
||||
#include "Common/GPU/OpenGL/GLFeatures.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
@@ -757,11 +758,334 @@ static void ShowFpsLimitNotice() {
|
||||
void EmuScreen::OnVKey(VirtKey virtualKeyCode, bool down) {
|
||||
if (!IsOnTop())
|
||||
return;
|
||||
queuedVirtKeys_.push_back(std::make_pair(virtualKeyCode, down));
|
||||
}
|
||||
|
||||
auto sc = GetI18NCategory(I18NCat::SCREEN);
|
||||
void EmuScreen::ProcessQueuedVKeys() {
|
||||
for (auto iter : queuedVirtKeys_) {
|
||||
ProcessVKey(iter.first, iter.second);
|
||||
}
|
||||
queuedVirtKeys_.clear();
|
||||
}
|
||||
|
||||
// Synchronized processing of virtkeys.
|
||||
void EmuScreen::ProcessVKey(VirtKey virtKey, bool down) {
|
||||
auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS);
|
||||
auto sc = GetI18NCategory(I18NCat::SCREEN);
|
||||
|
||||
switch (virtualKeyCode) {
|
||||
switch (virtKey) {
|
||||
case VIRTKEY_PAUSE:
|
||||
// Note: We don't check NetworkWarnUserIfOnlineAndCantSpeed, because we can keep
|
||||
// running in the background of the menu.
|
||||
if (down) {
|
||||
pauseTrigger_ = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_SCREENSHOT:
|
||||
if (down) {
|
||||
TakeUserScreenshot();
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_DEBUGGER:
|
||||
if (down) {
|
||||
g_Config.bShowImDebugger = !g_Config.bShowImDebugger;
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_TOGGLE_TILT:
|
||||
if (down) {
|
||||
g_Config.bTiltInputEnabled = !g_Config.bTiltInputEnabled;
|
||||
if (!g_Config.bTiltInputEnabled) {
|
||||
// Reset whatever got tilted.
|
||||
switch (g_Config.iTiltInputType) {
|
||||
case TILT_ANALOG:
|
||||
__CtrlSetAnalogXY(0, 0, 0);
|
||||
break;
|
||||
case TILT_ACTION_BUTTON:
|
||||
__CtrlUpdateButtons(0, CTRL_CROSS | CTRL_CIRCLE | CTRL_SQUARE | CTRL_TRIANGLE);
|
||||
break;
|
||||
case TILT_DPAD:
|
||||
__CtrlUpdateButtons(0, CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT);
|
||||
break;
|
||||
case TILT_TRIGGER_BUTTONS:
|
||||
__CtrlUpdateButtons(0, CTRL_LTRIGGER | CTRL_RTRIGGER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_OPENCHAT:
|
||||
if (down) {
|
||||
if (g_Config.bEnableNetworkChat && !g_Config.bShowImDebugger) {
|
||||
UI::EventParams e{};
|
||||
g_controlMapper.ForceReleaseVKey(VIRTKEY_OPENCHAT);
|
||||
OpenChat(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_AXIS_SWAP_TOGGLE:
|
||||
if (down) {
|
||||
g_controlMapper.ToggleSwapAxes();
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, mc->T("AxisSwap")); // best string we have.
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_DEVMENU:
|
||||
if (down) {
|
||||
UI::EventParams e{};
|
||||
OnDevMenu.Trigger(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_MOUSE:
|
||||
if (down) {
|
||||
g_Config.bMouseControl = !g_Config.bMouseControl;
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TEXTURE_DUMP:
|
||||
if (down) {
|
||||
g_Config.bSaveNewTextures = !g_Config.bSaveNewTextures;
|
||||
if (g_Config.bSaveNewTextures) {
|
||||
g_OSD.Show(OSDType::MESSAGE_SUCCESS, sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0, "savetexturechanged");
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0, "savetexturechanged");
|
||||
}
|
||||
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TEXTURE_REPLACE:
|
||||
if (down) {
|
||||
g_Config.bReplaceTextures = !g_Config.bReplaceTextures;
|
||||
if (g_Config.bReplaceTextures) {
|
||||
g_OSD.Show(OSDType::MESSAGE_SUCCESS, sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0, "replacetexturechanged");
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("replaceTextures_false", "Textures are no longer being replaced"), 2.0, "replacetexturechanged");
|
||||
}
|
||||
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_MUTE_TOGGLE:
|
||||
if (down) {
|
||||
g_Config.bEnableSound = !g_Config.bEnableSound;
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_SCREEN_ROTATION_VERTICAL:
|
||||
if (down) {
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL;
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_SCREEN_ROTATION_VERTICAL180:
|
||||
if (down) {
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180;
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_SCREEN_ROTATION_HORIZONTAL:
|
||||
if (down) {
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL;
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_SCREEN_ROTATION_HORIZONTAL180:
|
||||
if (down) {
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL180;
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_WLAN:
|
||||
if (down) {
|
||||
// Let's not allow the user to toggle wlan while connected, could get confusing.
|
||||
if (!g_netInited) {
|
||||
auto n = GetI18NCategory(I18NCat::NETWORKING);
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
g_Config.bEnableWlan = !g_Config.bEnableWlan;
|
||||
// Try to avoid adding more strings so we piece together a message from existing ones.
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, StringFromFormat(
|
||||
"%s: %s", n->T_cstr("Enable networking"), g_Config.bEnableWlan ? di->T_cstr("Enabled") : di->T_cstr("Disabled")), 2.0, "toggle_wlan");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_FULLSCREEN:
|
||||
if (down) {
|
||||
// TODO: Limit to platforms that can support fullscreen.
|
||||
g_Config.bFullScreen = !g_Config.bFullScreen;
|
||||
System_ApplyFullscreenState();
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_TOUCH_CONTROLS:
|
||||
if (down) {
|
||||
if (g_Config.bShowTouchControls) {
|
||||
// This just messes with opacity if enabled, so you can touch the screen again to bring them back.
|
||||
if (GamepadGetOpacity() < 0.01f) {
|
||||
GamepadTouch();
|
||||
} else {
|
||||
GamepadResetTouch();
|
||||
}
|
||||
} else {
|
||||
// If touch controls are disabled though, they'll get enabled.
|
||||
g_Config.bShowTouchControls = true;
|
||||
RecreateViews();
|
||||
GamepadTouch();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_REWIND:
|
||||
if (down) {
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
|
||||
if (SaveState::CanRewind()) {
|
||||
SaveState::Rewind(&ShowMessageAfterSaveStateAction);
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("norewind", "No rewind save states available"), 2.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_PAUSE_NO_MENU:
|
||||
if (down) {
|
||||
if (!NetworkWarnUserIfOnlineAndCantSpeed()) {
|
||||
// We re-use debug break/resume to implement pause/resume without a menu.
|
||||
if (coreState == CORE_STEPPING_CPU) { // should we check reason?
|
||||
Core_Resume();
|
||||
} else {
|
||||
Core_Break(BreakReason::UIPause);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_EXIT_APP:
|
||||
if (down) {
|
||||
if (!bootPending_) {
|
||||
std::string confirmExitMessage = GetConfirmExitMessage();
|
||||
if (!confirmExitMessage.empty()) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
auto mm = GetI18NCategory(I18NCat::MAINMENU);
|
||||
confirmExitMessage += '\n';
|
||||
confirmExitMessage += di->T("Are you sure you want to exit?");
|
||||
screenManager()->push(new UI::MessagePopupScreen(di->T("Exit"), confirmExitMessage, di->T("Yes"), di->T("No"), [](bool result) {
|
||||
if (result) {
|
||||
System_ExitApp();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
System_ExitApp();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_FRAME_ADVANCE:
|
||||
if (down) {
|
||||
// Can't do this reliably in an async fashion, so we just set a variable.
|
||||
// Is this used by anyone? There's no user-friendly way to resume, other than PAUSE_NO_MENU or the debugger.
|
||||
if (!NetworkWarnUserIfOnlineAndCantSpeed()) {
|
||||
if (Core_IsStepping()) {
|
||||
Core_Resume();
|
||||
frameStep_ = true;
|
||||
} else {
|
||||
Core_Break(BreakReason::FrameAdvance);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_SPEED_TOGGLE:
|
||||
if (down) {
|
||||
if (!NetworkWarnUserIfOnlineAndCantSpeed()) {
|
||||
// Cycle through enabled speeds.
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1;
|
||||
} else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit2 >= 0) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2;
|
||||
} else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 || PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL;
|
||||
}
|
||||
|
||||
ShowFpsLimitNotice();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_RESET_EMULATION:
|
||||
if (down) {
|
||||
System_PostUIMessage(UIMessage::REQUEST_GAME_RESET);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
case VIRTKEY_RECORD:
|
||||
if (down) {
|
||||
if (g_Config.bDumpFrames == g_Config.bDumpAudio) {
|
||||
g_Config.bDumpFrames = !g_Config.bDumpFrames;
|
||||
g_Config.bDumpAudio = !g_Config.bDumpAudio;
|
||||
} else {
|
||||
// This hotkey should always toggle both audio and video together.
|
||||
// So let's make sure that's the only outcome even if video OR audio was already being dumped.
|
||||
if (g_Config.bDumpFrames) {
|
||||
AVIDump::Stop();
|
||||
AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight);
|
||||
g_Config.bDumpAudio = true;
|
||||
} else {
|
||||
WAVDump::Reset();
|
||||
g_Config.bDumpFrames = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case VIRTKEY_SAVE_STATE:
|
||||
if (down) {
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
|
||||
SaveState::SaveSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &ShowMessageAfterSaveStateAction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_LOAD_STATE:
|
||||
if (down) {
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
|
||||
if (g_Config.bConfirmLoadState) {
|
||||
std::string prefix = SaveState::GetGamePrefix(g_paramSFO);
|
||||
int slot = g_Config.iCurrentStateSlot;
|
||||
screenManager()->push(new LoadStateConfirmScreen(prefix, slot, [prefix, slot](bool result) {
|
||||
if (result) {
|
||||
SaveState::LoadSlot(prefix, slot, &ShowMessageAfterSaveStateAction);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
SaveState::LoadSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &ShowMessageAfterSaveStateAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_PREVIOUS_SLOT:
|
||||
if (down) {
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
SaveState::PrevSlot();
|
||||
System_PostUIMessage(UIMessage::SAVESTATE_DISPLAY_SLOT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_NEXT_SLOT:
|
||||
if (down) {
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
SaveState::NextSlot();
|
||||
System_PostUIMessage(UIMessage::SAVESTATE_DISPLAY_SLOT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_FASTFORWARD:
|
||||
if (down && !NetworkWarnUserIfOnlineAndCantSpeed() && !bootPending_) {
|
||||
/*
|
||||
@@ -806,294 +1130,7 @@ void EmuScreen::OnVKey(VirtKey virtualKeyCode, bool down) {
|
||||
case VIRTKEY_RAPID_FIRE:
|
||||
__CtrlSetRapidFire(down, g_Config.iRapidFireInterval);
|
||||
break;
|
||||
default:
|
||||
// To make sure we're not in an async context.
|
||||
if (down) {
|
||||
queuedVirtKeys_.push_back(virtualKeyCode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EmuScreen::ProcessQueuedVKeys() {
|
||||
for (auto iter : queuedVirtKeys_) {
|
||||
ProcessVKey(iter);
|
||||
}
|
||||
queuedVirtKeys_.clear();
|
||||
}
|
||||
|
||||
// Synchronized processing of virtkeys.
|
||||
void EmuScreen::ProcessVKey(VirtKey virtKey) {
|
||||
auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS);
|
||||
auto sc = GetI18NCategory(I18NCat::SCREEN);
|
||||
|
||||
switch (virtKey) {
|
||||
case VIRTKEY_PAUSE:
|
||||
// Note: We don't check NetworkWarnUserIfOnlineAndCantSpeed, because we can keep
|
||||
// running in the background of the menu.
|
||||
pauseTrigger_ = true;
|
||||
break;
|
||||
|
||||
case VIRTKEY_SCREENSHOT:
|
||||
TakeUserScreenshot();
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_DEBUGGER:
|
||||
g_Config.bShowImDebugger = !g_Config.bShowImDebugger;
|
||||
break;
|
||||
case VIRTKEY_TOGGLE_TILT:
|
||||
g_Config.bTiltInputEnabled = !g_Config.bTiltInputEnabled;
|
||||
if (!g_Config.bTiltInputEnabled) {
|
||||
// Reset whatever got tilted.
|
||||
switch (g_Config.iTiltInputType) {
|
||||
case TILT_ANALOG:
|
||||
__CtrlSetAnalogXY(0, 0, 0);
|
||||
break;
|
||||
case TILT_ACTION_BUTTON:
|
||||
__CtrlUpdateButtons(0, CTRL_CROSS | CTRL_CIRCLE | CTRL_SQUARE | CTRL_TRIANGLE);
|
||||
break;
|
||||
case TILT_DPAD:
|
||||
__CtrlUpdateButtons(0, CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT);
|
||||
break;
|
||||
case TILT_TRIGGER_BUTTONS:
|
||||
__CtrlUpdateButtons(0, CTRL_LTRIGGER | CTRL_RTRIGGER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_OPENCHAT:
|
||||
if (g_Config.bEnableNetworkChat && !g_Config.bShowImDebugger) {
|
||||
UI::EventParams e{};
|
||||
g_controlMapper.ForceReleaseVKey(VIRTKEY_OPENCHAT);
|
||||
OpenChat(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_AXIS_SWAP_TOGGLE:
|
||||
g_controlMapper.ToggleSwapAxes();
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, mc->T("AxisSwap")); // best string we have.
|
||||
break;
|
||||
|
||||
case VIRTKEY_DEVMENU:
|
||||
{
|
||||
UI::EventParams e{};
|
||||
OnDevMenu.Trigger(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_MOUSE:
|
||||
g_Config.bMouseControl = !g_Config.bMouseControl;
|
||||
break;
|
||||
|
||||
case VIRTKEY_TEXTURE_DUMP:
|
||||
g_Config.bSaveNewTextures = !g_Config.bSaveNewTextures;
|
||||
if (g_Config.bSaveNewTextures) {
|
||||
g_OSD.Show(OSDType::MESSAGE_SUCCESS, sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0, "savetexturechanged");
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0, "savetexturechanged");
|
||||
}
|
||||
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
|
||||
break;
|
||||
|
||||
case VIRTKEY_TEXTURE_REPLACE:
|
||||
g_Config.bReplaceTextures = !g_Config.bReplaceTextures;
|
||||
if (g_Config.bReplaceTextures) {
|
||||
g_OSD.Show(OSDType::MESSAGE_SUCCESS, sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0, "replacetexturechanged");
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("replaceTextures_false", "Textures are no longer being replaced"), 2.0, "replacetexturechanged");
|
||||
}
|
||||
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
|
||||
break;
|
||||
|
||||
case VIRTKEY_MUTE_TOGGLE:
|
||||
g_Config.bEnableSound = !g_Config.bEnableSound;
|
||||
break;
|
||||
|
||||
case VIRTKEY_SCREEN_ROTATION_VERTICAL:
|
||||
{
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL;
|
||||
break;
|
||||
}
|
||||
case VIRTKEY_SCREEN_ROTATION_VERTICAL180:
|
||||
{
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180;
|
||||
break;
|
||||
}
|
||||
case VIRTKEY_SCREEN_ROTATION_HORIZONTAL:
|
||||
{
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL;
|
||||
break;
|
||||
}
|
||||
case VIRTKEY_SCREEN_ROTATION_HORIZONTAL180:
|
||||
{
|
||||
DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation());
|
||||
config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL180;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIRTKEY_TOGGLE_WLAN:
|
||||
// Let's not allow the user to toggle wlan while connected, could get confusing.
|
||||
if (!g_netInited) {
|
||||
auto n = GetI18NCategory(I18NCat::NETWORKING);
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
g_Config.bEnableWlan = !g_Config.bEnableWlan;
|
||||
// Try to avoid adding more strings so we piece together a message from existing ones.
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, StringFromFormat(
|
||||
"%s: %s", n->T_cstr("Enable networking"), g_Config.bEnableWlan ? di->T_cstr("Enabled") : di->T_cstr("Disabled")), 2.0, "toggle_wlan");
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_FULLSCREEN:
|
||||
// TODO: Limit to platforms that can support fullscreen.
|
||||
g_Config.bFullScreen = !g_Config.bFullScreen;
|
||||
System_ApplyFullscreenState();
|
||||
break;
|
||||
|
||||
case VIRTKEY_TOGGLE_TOUCH_CONTROLS:
|
||||
if (g_Config.bShowTouchControls) {
|
||||
// This just messes with opacity if enabled, so you can touch the screen again to bring them back.
|
||||
if (GamepadGetOpacity() < 0.01f) {
|
||||
GamepadTouch();
|
||||
} else {
|
||||
GamepadResetTouch();
|
||||
}
|
||||
} else {
|
||||
// If touch controls are disabled though, they'll get enabled.
|
||||
g_Config.bShowTouchControls = true;
|
||||
RecreateViews();
|
||||
GamepadTouch();
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_REWIND:
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
|
||||
if (SaveState::CanRewind()) {
|
||||
SaveState::Rewind(&ShowMessageAfterSaveStateAction);
|
||||
} else {
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("norewind", "No rewind save states available"), 2.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_PAUSE_NO_MENU:
|
||||
if (!NetworkWarnUserIfOnlineAndCantSpeed()) {
|
||||
// We re-use debug break/resume to implement pause/resume without a menu.
|
||||
if (coreState == CORE_STEPPING_CPU) { // should we check reason?
|
||||
Core_Resume();
|
||||
} else {
|
||||
Core_Break(BreakReason::UIPause);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_EXIT_APP:
|
||||
{
|
||||
if (!bootPending_) {
|
||||
std::string confirmExitMessage = GetConfirmExitMessage();
|
||||
if (!confirmExitMessage.empty()) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
auto mm = GetI18NCategory(I18NCat::MAINMENU);
|
||||
confirmExitMessage += '\n';
|
||||
confirmExitMessage += di->T("Are you sure you want to exit?");
|
||||
screenManager()->push(new UI::MessagePopupScreen(di->T("Exit"), confirmExitMessage, di->T("Yes"), di->T("No"), [](bool result) {
|
||||
if (result) {
|
||||
System_ExitApp();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
System_ExitApp();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case VIRTKEY_FRAME_ADVANCE:
|
||||
// Can't do this reliably in an async fashion, so we just set a variable.
|
||||
// Is this used by anyone? There's no user-friendly way to resume, other than PAUSE_NO_MENU or the debugger.
|
||||
if (!NetworkWarnUserIfOnlineAndCantSpeed()) {
|
||||
if (Core_IsStepping()) {
|
||||
Core_Resume();
|
||||
frameStep_ = true;
|
||||
} else {
|
||||
Core_Break(BreakReason::FrameAdvance);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_SPEED_TOGGLE:
|
||||
if (!NetworkWarnUserIfOnlineAndCantSpeed()) {
|
||||
// Cycle through enabled speeds.
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1;
|
||||
} else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit2 >= 0) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2;
|
||||
} else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 || PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL;
|
||||
}
|
||||
|
||||
ShowFpsLimitNotice();
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTKEY_RESET_EMULATION:
|
||||
System_PostUIMessage(UIMessage::REQUEST_GAME_RESET);
|
||||
break;
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
case VIRTKEY_RECORD:
|
||||
if (g_Config.bDumpFrames == g_Config.bDumpAudio) {
|
||||
g_Config.bDumpFrames = !g_Config.bDumpFrames;
|
||||
g_Config.bDumpAudio = !g_Config.bDumpAudio;
|
||||
} else {
|
||||
// This hotkey should always toggle both audio and video together.
|
||||
// So let's make sure that's the only outcome even if video OR audio was already being dumped.
|
||||
if (g_Config.bDumpFrames) {
|
||||
AVIDump::Stop();
|
||||
AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight);
|
||||
g_Config.bDumpAudio = true;
|
||||
} else {
|
||||
WAVDump::Reset();
|
||||
g_Config.bDumpFrames = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case VIRTKEY_SAVE_STATE:
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
|
||||
SaveState::SaveSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &ShowMessageAfterSaveStateAction);
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_LOAD_STATE:
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
|
||||
if (g_Config.bConfirmLoadState) {
|
||||
std::string prefix = SaveState::GetGamePrefix(g_paramSFO);
|
||||
int slot = g_Config.iCurrentStateSlot;
|
||||
screenManager()->push(new LoadStateConfirmScreen(prefix, slot, [prefix, slot](bool result) {
|
||||
if (result) {
|
||||
SaveState::LoadSlot(prefix, slot, &ShowMessageAfterSaveStateAction);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
SaveState::LoadSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &ShowMessageAfterSaveStateAction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_PREVIOUS_SLOT:
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
SaveState::PrevSlot();
|
||||
System_PostUIMessage(UIMessage::SAVESTATE_DISPLAY_SLOT);
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_NEXT_SLOT:
|
||||
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
SaveState::NextSlot();
|
||||
System_PostUIMessage(UIMessage::SAVESTATE_DISPLAY_SLOT);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1134,82 +1171,24 @@ void EmuScreen::OnVKeyAnalog(VirtKey virtualKeyCode, float value) {
|
||||
limitMode = PSP_CoreParameter().analogFpsLimit == 60 ? FPSLimit::NORMAL : FPSLimit::ANALOG;
|
||||
}
|
||||
|
||||
bool EmuScreen::AllowKeyboardNavigation() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmuScreen::UnsyncKey(const KeyInput &key) {
|
||||
System_Notify(SystemNotification::ACTIVITY);
|
||||
|
||||
// Update imgui modifier flags
|
||||
if (key.flags & (KeyInputFlags::DOWN | KeyInputFlags::UP)) {
|
||||
bool down = (key.flags & KeyInputFlags::DOWN) != 0;
|
||||
switch (key.keyCode) {
|
||||
case NKCODE_CTRL_LEFT: keyCtrlLeft_ = down; break;
|
||||
case NKCODE_CTRL_RIGHT: keyCtrlRight_ = down; break;
|
||||
case NKCODE_SHIFT_LEFT: keyShiftLeft_ = down; break;
|
||||
case NKCODE_SHIFT_RIGHT: keyShiftRight_ = down; break;
|
||||
case NKCODE_ALT_LEFT: keyAltLeft_ = down; break;
|
||||
case NKCODE_ALT_RIGHT: keyAltRight_ = down; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
InputMode EmuScreen::PassInputToMapper() const {
|
||||
const bool chatMenuOpen = chatMenu_ && chatMenu_->GetVisibility() == UI::V_VISIBLE;
|
||||
if (chatMenuOpen) {
|
||||
return InputMode::None;
|
||||
}
|
||||
|
||||
if (chatMenuOpen || (g_Config.bShowImDebugger && imguiInited_)) {
|
||||
// Note: Allow some Vkeys through, so we can toggle the imgui for example (since we actually block the control mapper otherwise in imgui mode).
|
||||
// We need to manually implement it here :/
|
||||
if (g_Config.bShowImDebugger && imguiInited_) {
|
||||
if (key.flags & (KeyInputFlags::UP | KeyInputFlags::DOWN)) {
|
||||
InputMapping mapping(key.deviceId, key.keyCode);
|
||||
std::vector<int> pspButtons;
|
||||
bool mappingFound = KeyMap::InputMappingToPspButton(mapping, &pspButtons);
|
||||
if (mappingFound) {
|
||||
for (auto b : pspButtons) {
|
||||
if (b == VIRTKEY_TOGGLE_DEBUGGER || b == VIRTKEY_PAUSE) {
|
||||
return g_controlMapper.Key(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UI::EnableFocusMovement(false);
|
||||
// Enable gamepad controls while running imgui (but ignore mouse/keyboard).
|
||||
switch (key.deviceId) {
|
||||
case DEVICE_ID_KEYBOARD:
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard) {
|
||||
g_controlMapper.Key(key);
|
||||
}
|
||||
break;
|
||||
case DEVICE_ID_MOUSE:
|
||||
if (!ImGui::GetIO().WantCaptureMouse) {
|
||||
g_controlMapper.Key(key);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_controlMapper.Key(key);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Let up-events through to the controlMapper_ so input doesn't get stuck.
|
||||
if (key.flags & KeyInputFlags::UP) {
|
||||
g_controlMapper.Key(key);
|
||||
}
|
||||
InputMode modes = InputMode::Keyboard | InputMode::Mouse | InputMode::Other | InputMode::ImDebuggerToggle;
|
||||
if (g_Config.bShowImDebugger && imguiInited_) {
|
||||
if (ImGui::GetIO().WantCaptureKeyboard) {
|
||||
modes &= ~InputMode::Keyboard;
|
||||
}
|
||||
|
||||
return UIScreen::UnsyncKey(key);
|
||||
}
|
||||
return g_controlMapper.Key(key);
|
||||
}
|
||||
|
||||
void EmuScreen::UnsyncAxis(const AxisInput *axes, size_t count) {
|
||||
System_Notify(SystemNotification::ACTIVITY);
|
||||
|
||||
if (UI::IsFocusMovementEnabled()) {
|
||||
return UIScreen::UnsyncAxis(axes, count);
|
||||
if (ImGui::GetIO().WantCaptureMouse) {
|
||||
modes &= ~InputMode::Mouse;
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
return g_controlMapper.Axis(axes, count);
|
||||
return modes;
|
||||
}
|
||||
|
||||
bool EmuScreen::key(const KeyInput &key) {
|
||||
@@ -1231,7 +1210,7 @@ bool EmuScreen::key(const KeyInput &key) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
void EmuScreen::touch(const TouchInput &touch) {
|
||||
bool EmuScreen::touch(const TouchInput &touch) {
|
||||
System_Notify(SystemNotification::ACTIVITY);
|
||||
|
||||
bool ignoreGamepad = false;
|
||||
@@ -1253,7 +1232,7 @@ void EmuScreen::touch(const TouchInput &touch) {
|
||||
if (g_Config.bShowImDebugger && imguiInited_) {
|
||||
ImGui_ImplPlatform_TouchEvent(touch);
|
||||
if (!ImGui::GetIO().WantCaptureMouse) {
|
||||
UIScreen::touch(touch);
|
||||
return UIScreen::touch(touch);
|
||||
}
|
||||
} else if (g_Config.bMouseControl && !(touch.flags & TouchInputFlags::UP) && (touch.flags & TouchInputFlags::MOUSE)) {
|
||||
// don't do anything as the mouse pointer is hidden in this case.
|
||||
@@ -1271,8 +1250,9 @@ void EmuScreen::touch(const TouchInput &touch) {
|
||||
}
|
||||
}
|
||||
}
|
||||
UIScreen::touch(touch);
|
||||
return UIScreen::touch(touch);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Shouldn't actually need bounds for this, Anchor can center too.
|
||||
@@ -1943,9 +1923,15 @@ void EmuScreen::runImDebugger() {
|
||||
|
||||
// Update keyboard modifiers.
|
||||
auto &io = ImGui::GetIO();
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, keyCtrlLeft_ || keyCtrlRight_);
|
||||
io.AddKeyEvent(ImGuiMod_Shift, keyShiftLeft_ || keyShiftRight_);
|
||||
io.AddKeyEvent(ImGuiMod_Alt, keyAltLeft_ || keyAltRight_);
|
||||
|
||||
KeyModifier modifiers = NativeGetKeyModifiers();
|
||||
|
||||
const bool keyCtrl = (modifiers & KeyModifier::LCTRL) || (modifiers & KeyModifier::RCTRL);
|
||||
const bool keyShift = (modifiers & KeyModifier::LSHIFT) || (modifiers & KeyModifier::RSHIFT);
|
||||
const bool keyAlt = (modifiers & KeyModifier::LALT) || (modifiers & KeyModifier::RALT);
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, keyCtrl);
|
||||
io.AddKeyEvent(ImGuiMod_Shift, keyShift);
|
||||
io.AddKeyEvent(ImGuiMod_Alt, keyAlt);
|
||||
// io.AddKeyEvent(ImGuiMod_Super, e.key.super);
|
||||
|
||||
ImGuiID dockID = ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoDockingOverCentralNode);
|
||||
|
||||
+6
-10
@@ -52,14 +52,11 @@ public:
|
||||
void resized() override;
|
||||
ScreenRenderRole renderRole(bool isTop) const 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.
|
||||
bool UnsyncKey(const KeyInput &key) override;
|
||||
void UnsyncAxis(const AxisInput *axes, size_t count) override;
|
||||
InputMode PassInputToMapper() const override;
|
||||
|
||||
// We also need to do some special handling of queued UI events to handle closing the chat window.
|
||||
bool key(const KeyInput &key) override;
|
||||
void touch(const TouchInput &key) override;
|
||||
bool touch(const TouchInput &key) override;
|
||||
|
||||
void deviceLost() override;
|
||||
void deviceRestored(Draw::DrawContext *draw) override;
|
||||
@@ -69,8 +66,6 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
bool AllowKeyboardNavigation() const override;
|
||||
|
||||
void darken();
|
||||
void focusChanged(ScreenFocusChange focusChange) override;
|
||||
ScreenRenderFlags PreRender(ScreenRenderMode mode) override;
|
||||
@@ -102,7 +97,7 @@ private:
|
||||
bool checkPowerDown();
|
||||
|
||||
void ProcessQueuedVKeys();
|
||||
void ProcessVKey(VirtKey vkey);
|
||||
void ProcessVKey(VirtKey vkey, bool down);
|
||||
|
||||
bool ShouldRunEmulation(ScreenRenderMode mode) const;
|
||||
|
||||
@@ -148,7 +143,8 @@ private:
|
||||
ImCommand imCmd_{}; // needed to buffer commands in case imgui wasn't created yet.
|
||||
|
||||
bool imguiInited_ = false;
|
||||
// For ImGui modifier tracking
|
||||
// For ImGui modifier tracking. It works a bit weirdly and it's easier to track it here
|
||||
// than to use our global modifier tracking that attaches modifiers to each key event.
|
||||
bool keyCtrlLeft_ = false;
|
||||
bool keyCtrlRight_ = false;
|
||||
bool keyShiftLeft_ = false;
|
||||
@@ -158,7 +154,7 @@ private:
|
||||
|
||||
bool lastImguiEnabled_ = false;
|
||||
|
||||
std::vector<VirtKey> queuedVirtKeys_;
|
||||
std::vector<std::pair<VirtKey, bool>> queuedVirtKeys_;
|
||||
|
||||
ImGuiContext *ctx_ = nullptr;
|
||||
|
||||
|
||||
+3
-1
@@ -419,10 +419,12 @@ bool LogoScreen::key(const KeyInput &key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void LogoScreen::touch(const TouchInput &touch) {
|
||||
bool LogoScreen::touch(const TouchInput &touch) {
|
||||
if (touch.flags & TouchInputFlags::DOWN) {
|
||||
Next();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LogoScreen::DrawForeground(UIContext &dc) {
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ public:
|
||||
LogoScreen(AfterLogoScreen afterLogoScreen = AfterLogoScreen::DEFAULT);
|
||||
|
||||
bool key(const KeyInput &key) override;
|
||||
void touch(const TouchInput &touch) override;
|
||||
bool touch(const TouchInput &touch) override;
|
||||
void update() override;
|
||||
void DrawForeground(UIContext &ui) override;
|
||||
void sendMessage(UIMessage message, const char *value) override;
|
||||
|
||||
+107
-39
@@ -1369,23 +1369,17 @@ static void ProcessWheelRelease(InputKeyCode keyCode, double now, bool keyPress)
|
||||
}
|
||||
}
|
||||
|
||||
enum class Modifier {
|
||||
NONE = 0,
|
||||
LCTRL = 1,
|
||||
RCTRL = 2,
|
||||
LSHIFT = 4,
|
||||
RSHIFT = 8,
|
||||
LALT = 16,
|
||||
RALT = 32,
|
||||
LMETA = 64,
|
||||
RMETA = 128,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(Modifier);
|
||||
static Modifier g_modifiersPressed{};
|
||||
KeyModifier g_modifiersPressed{};
|
||||
|
||||
KeyModifier NativeGetKeyModifiers() {
|
||||
return g_modifiersPressed;
|
||||
}
|
||||
|
||||
bool NativeKey(const KeyInput &key) {
|
||||
double now = time_now_d();
|
||||
|
||||
System_Notify(SystemNotification::ACTIVITY);
|
||||
|
||||
// VR actions
|
||||
if ((IsVREnabled() || g_Config.bForceVR) && !UpdateVRKeys(key)) {
|
||||
return false;
|
||||
@@ -1433,44 +1427,92 @@ bool NativeKey(const KeyInput &key) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!g_screenManager) {
|
||||
return false;
|
||||
}
|
||||
HLEPlugins::SetKey(key.keyCode, (key.flags & KeyInputFlags::DOWN) ? 1 : 0);
|
||||
|
||||
// Handle releases of mousewheel keys.
|
||||
if ((key.flags & KeyInputFlags::DOWN) && key.deviceId == DEVICE_ID_MOUSE && (key.keyCode == NKCODE_EXT_MOUSEWHEEL_UP || key.keyCode == NKCODE_EXT_MOUSEWHEEL_DOWN)) {
|
||||
ProcessWheelRelease(key.keyCode, now, true);
|
||||
}
|
||||
|
||||
HLEPlugins::SetKey(key.keyCode, (key.flags & KeyInputFlags::DOWN) ? 1 : 0);
|
||||
if (!g_screenManager) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Track and update modifiers.
|
||||
// Filtering, detailed rules needed for good imgui behavior without having to ask the screen about what to do.
|
||||
InputMode inputMode = g_screenManager->PassInputToMapper();
|
||||
bool passKeyThrough = false;
|
||||
if (inputMode != InputMode::None) {
|
||||
if ((inputMode & InputMode::ImDebuggerToggle) && (key.flags & (KeyInputFlags::UP | KeyInputFlags::DOWN))) {
|
||||
InputMapping mapping(key.deviceId, key.keyCode);
|
||||
std::vector<int> pspButtons;
|
||||
bool mappingFound = KeyMap::InputMappingToPspButton(mapping, &pspButtons);
|
||||
if (mappingFound) {
|
||||
for (auto b : pspButtons) {
|
||||
if (b == VIRTKEY_TOGGLE_DEBUGGER || b == VIRTKEY_PAUSE) {
|
||||
// TRUE
|
||||
passKeyThrough = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key.deviceId == DEVICE_ID_MOUSE) {
|
||||
if (inputMode & InputMode::Mouse) {
|
||||
passKeyThrough = true;
|
||||
}
|
||||
} else if (key.deviceId == DEVICE_ID_KEYBOARD) {
|
||||
if (inputMode & InputMode::Keyboard) {
|
||||
passKeyThrough = true;
|
||||
}
|
||||
} else if (inputMode & InputMode::Other) {
|
||||
// yes this is different
|
||||
passKeyThrough = true;
|
||||
}
|
||||
} else {
|
||||
// Pass through only up events.
|
||||
if (key.flags & KeyInputFlags::UP) {
|
||||
passKeyThrough = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (passKeyThrough) {
|
||||
g_controlMapper.Key(key);
|
||||
}
|
||||
|
||||
// Ignore volume keys and stuff here - though we do send them through to the control mapper, so they can be mapped to PSP buttons.
|
||||
switch (key.keyCode) {
|
||||
case NKCODE_VOLUME_DOWN:
|
||||
case NKCODE_VOLUME_UP:
|
||||
case NKCODE_VOLUME_MUTE:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Track modifier keys.
|
||||
if (key.flags & KeyInputFlags::DOWN) {
|
||||
switch (key.keyCode) {
|
||||
case NKCODE_CTRL_LEFT: g_modifiersPressed |= Modifier::LCTRL; break;
|
||||
case NKCODE_CTRL_RIGHT: g_modifiersPressed |= Modifier::RCTRL; break;
|
||||
case NKCODE_SHIFT_LEFT: g_modifiersPressed |= Modifier::LSHIFT; break;
|
||||
case NKCODE_SHIFT_RIGHT: g_modifiersPressed |= Modifier::RSHIFT; break;
|
||||
case NKCODE_ALT_LEFT: g_modifiersPressed |= Modifier::LALT; break;
|
||||
case NKCODE_ALT_RIGHT: g_modifiersPressed |= Modifier::RALT; break;
|
||||
case NKCODE_META_LEFT: g_modifiersPressed |= Modifier::LMETA; break;
|
||||
case NKCODE_META_RIGHT: g_modifiersPressed |= Modifier::RMETA; break;
|
||||
case NKCODE_CTRL_LEFT: g_modifiersPressed |= KeyModifier::LCTRL; break;
|
||||
case NKCODE_CTRL_RIGHT: g_modifiersPressed |= KeyModifier::RCTRL; break;
|
||||
case NKCODE_SHIFT_LEFT: g_modifiersPressed |= KeyModifier::LSHIFT; break;
|
||||
case NKCODE_SHIFT_RIGHT: g_modifiersPressed |= KeyModifier::RSHIFT; break;
|
||||
case NKCODE_ALT_LEFT: g_modifiersPressed |= KeyModifier::LALT; break;
|
||||
case NKCODE_ALT_RIGHT: g_modifiersPressed |= KeyModifier::RALT; break;
|
||||
case NKCODE_META_LEFT: g_modifiersPressed |= KeyModifier::LMETA; break;
|
||||
case NKCODE_META_RIGHT: g_modifiersPressed |= KeyModifier::RMETA; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (key.flags & KeyInputFlags::UP) {
|
||||
switch (key.keyCode) {
|
||||
case NKCODE_CTRL_LEFT: g_modifiersPressed &= ~Modifier::LCTRL; break;
|
||||
case NKCODE_CTRL_RIGHT: g_modifiersPressed &= ~Modifier::RCTRL; break;
|
||||
case NKCODE_SHIFT_LEFT: g_modifiersPressed &= ~Modifier::LSHIFT; break;
|
||||
case NKCODE_SHIFT_RIGHT: g_modifiersPressed &= ~Modifier::RSHIFT; break;
|
||||
case NKCODE_ALT_LEFT: g_modifiersPressed &= ~Modifier::LALT; break;
|
||||
case NKCODE_ALT_RIGHT: g_modifiersPressed &= ~Modifier::RALT; break;
|
||||
case NKCODE_META_LEFT: g_modifiersPressed &= ~Modifier::LMETA; break;
|
||||
case NKCODE_META_RIGHT: g_modifiersPressed &= ~Modifier::RMETA; break;
|
||||
case NKCODE_CTRL_LEFT: g_modifiersPressed &= ~KeyModifier::LCTRL; break;
|
||||
case NKCODE_CTRL_RIGHT: g_modifiersPressed &= ~KeyModifier::RCTRL; break;
|
||||
case NKCODE_SHIFT_LEFT: g_modifiersPressed &= ~KeyModifier::LSHIFT; break;
|
||||
case NKCODE_SHIFT_RIGHT: g_modifiersPressed &= ~KeyModifier::RSHIFT; break;
|
||||
case NKCODE_ALT_LEFT: g_modifiersPressed &= ~KeyModifier::LALT; break;
|
||||
case NKCODE_ALT_RIGHT: g_modifiersPressed &= ~KeyModifier::RALT; break;
|
||||
case NKCODE_META_LEFT: g_modifiersPressed &= ~KeyModifier::LMETA; break;
|
||||
case NKCODE_META_RIGHT: g_modifiersPressed &= ~KeyModifier::RMETA; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1478,24 +1520,44 @@ bool NativeKey(const KeyInput &key) {
|
||||
|
||||
KeyInputFlags modifierFlags{};
|
||||
|
||||
if (g_modifiersPressed & (Modifier::LCTRL | Modifier::RCTRL)) {
|
||||
if (g_modifiersPressed & (KeyModifier::LCTRL | KeyModifier::RCTRL)) {
|
||||
modifierFlags |= KeyInputFlags::MOD_CTRL;
|
||||
}
|
||||
if (g_modifiersPressed & (Modifier::LSHIFT | Modifier::RSHIFT)) {
|
||||
if (g_modifiersPressed & (KeyModifier::LSHIFT | KeyModifier::RSHIFT)) {
|
||||
modifierFlags |= KeyInputFlags::MOD_SHIFT;
|
||||
}
|
||||
if (g_modifiersPressed & (Modifier::LALT | Modifier::RALT)) {
|
||||
if (g_modifiersPressed & (KeyModifier::LALT | KeyModifier::RALT)) {
|
||||
modifierFlags |= KeyInputFlags::MOD_ALT;
|
||||
}
|
||||
if (g_modifiersPressed & (Modifier::LMETA | Modifier::RMETA)) {
|
||||
if (g_modifiersPressed & (KeyModifier::LMETA | KeyModifier::RMETA)) {
|
||||
modifierFlags |= KeyInputFlags::MOD_META;
|
||||
}
|
||||
|
||||
KeyInput modKey = key;
|
||||
modKey.flags |= modifierFlags;
|
||||
|
||||
bool retval = false;
|
||||
|
||||
UI::KeyEventResult kev = UI::KeyEventToFocusMoves(key);
|
||||
if (!(key.flags & KeyInputFlags::IS_REPEAT)) {
|
||||
// If a repeat, we follow what KeyEventToFocusMoves set it to.
|
||||
// Otherwise we signal that we used the key, always.
|
||||
kev = UI::KeyEventResult::ACCEPT;
|
||||
}
|
||||
|
||||
switch (kev) {
|
||||
case UI::KeyEventResult::ACCEPT:
|
||||
retval = true;
|
||||
break;
|
||||
case UI::KeyEventResult::PASS_THROUGH:
|
||||
retval = false;
|
||||
break;
|
||||
case UI::KeyEventResult::IGNORE_KEY:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dispatch the key event.
|
||||
bool retval = g_screenManager->key(modKey);
|
||||
g_screenManager->key(modKey);
|
||||
|
||||
// The Mode key can have weird consequences on some devices, see #17245.
|
||||
if (key.keyCode == NKCODE_BUTTON_MODE) {
|
||||
@@ -1512,11 +1574,17 @@ void NativeAxis(const AxisInput *axes, size_t count) {
|
||||
return;
|
||||
}
|
||||
|
||||
System_Notify(SystemNotification::ACTIVITY);
|
||||
|
||||
if (!g_screenManager) {
|
||||
// Too early.
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_screenManager->PassInputToMapper() & (InputMode::Other | InputMode::ImDebuggerToggle)) {
|
||||
g_controlMapper.Axis(axes, count);
|
||||
}
|
||||
|
||||
g_screenManager->axis(axes, count);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
|
||||
@@ -389,7 +389,7 @@ bool OnScreenMessagesView::Dismiss(float x, float y) {
|
||||
return dismissed;
|
||||
}
|
||||
|
||||
bool OSDOverlayScreen::UnsyncTouch(const TouchInput &touch) {
|
||||
bool OSDOverlayScreen::touch(const TouchInput &touch) {
|
||||
// Don't really need to forward.
|
||||
// UIScreen::UnsyncTouch(touch);
|
||||
if ((touch.flags & TouchInputFlags::DOWN) && osmView_) {
|
||||
|
||||
@@ -38,7 +38,7 @@ class OSDOverlayScreen : public UIScreen {
|
||||
public:
|
||||
const char *tag() const override { return "OSDOverlayScreen"; }
|
||||
|
||||
bool UnsyncTouch(const TouchInput &touch) override;
|
||||
bool touch(const TouchInput &touch) override;
|
||||
|
||||
void CreateViews() override;
|
||||
void DrawForeground(UIContext &ui) override;
|
||||
|
||||
+4
-14
@@ -335,6 +335,10 @@ void GamePauseScreen::update() {
|
||||
UpdateUIState(UISTATE_PAUSEMENU);
|
||||
UIScreen::update();
|
||||
|
||||
if (g_controlMapper.PollPauseTrigger()) {
|
||||
TriggerFinish(DR_BACK);
|
||||
}
|
||||
|
||||
if (finishNextFrame_) {
|
||||
TriggerFinish(finishNextFrameResult_);
|
||||
finishNextFrame_ = false;
|
||||
@@ -376,20 +380,6 @@ GamePauseScreen::~GamePauseScreen() {
|
||||
__DisplaySetWasPaused();
|
||||
}
|
||||
|
||||
bool GamePauseScreen::UnsyncKey(const KeyInput &key) {
|
||||
bool retval = UIScreen::UnsyncKey(key);
|
||||
retval = g_controlMapper.Key(key) || retval;
|
||||
if (g_controlMapper.PollPauseTrigger()) {
|
||||
TriggerFinish(DR_BACK);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void GamePauseScreen::UnsyncAxis(const AxisInput *axes, size_t count) {
|
||||
UIScreen::UnsyncAxis(axes, count);
|
||||
g_controlMapper.Axis(axes, count);
|
||||
}
|
||||
|
||||
void GamePauseScreen::OnVKey(VirtKey virtualKeyCode, bool down) {
|
||||
// Simple de-bounce using createdTime_, just to be safe.
|
||||
if (down && virtualKeyCode == VIRTKEY_PAUSE && time_now_d() > createdTime_ + 0.1) {
|
||||
|
||||
+4
-4
@@ -38,6 +38,10 @@ public:
|
||||
|
||||
const char *tag() const override { return "GamePause"; }
|
||||
|
||||
InputMode PassInputToMapper() const override {
|
||||
return InputMode::ImDebuggerToggle;
|
||||
}
|
||||
|
||||
protected:
|
||||
void CreateViews() override;
|
||||
void update() override;
|
||||
@@ -45,10 +49,6 @@ protected:
|
||||
ViewLayoutMode LayoutMode() const override {
|
||||
return ViewLayoutMode::ApplyInsets;
|
||||
}
|
||||
// For processing of certain mapped keys.
|
||||
bool UnsyncKey(const KeyInput &key) override;
|
||||
void UnsyncAxis(const AxisInput *axes, size_t count) override;
|
||||
|
||||
void OnVKey(VirtKey virtualKeyCode, bool down) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -36,7 +36,7 @@ echo '<?xml version="1.0" encoding="UTF-8"?>
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchains/ios.cmake -GXcode ..
|
||||
# Build PPSSPP using xcode
|
||||
#xcodebuild clean build -project PPSSPP.xcodeproj CODE_SIGNING_ALLOWED=NO -sdk iphoneos -configuration Release
|
||||
xcodebuild -project PPSSPP.xcodeproj -scheme PPSSPP -sdk iphoneos -configuration Release clean build archive -archivePath ./build/PPSSPP.xcarchive CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO #CODE_SIGN_IDENTITY="iPhone Distribution: Your NAME / Company (TeamID)" #PROVISIONING_PROFILE="xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
xcodebuild -project PPSSPP.xcodeproj -scheme PPSSPP -sdk iphoneos -configuration Release -quiet clean build archive -archivePath ./build/PPSSPP.xcarchive CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO #CODE_SIGN_IDENTITY="iPhone Distribution: Your NAME / Company (TeamID)" #PROVISIONING_PROFILE="xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
# Export IPA file from xcarchive (probably only works with signed build)
|
||||
#xcodebuild -exportArchive -archivePath ./build/PPSSPP.xcarchive -exportPath ./build -exportOptionsPlist exportOptions.plist
|
||||
# This folder only exist when building with xcodebuild
|
||||
|
||||
Reference in New Issue
Block a user