mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
WipeRequesterToken() only reset the screen's token, after CreateViews() had already handed it to the new views, so nothing was ever forgotten - not even in ~Screen(), which saw a token of -1. A text input dialog open across a RecreateViews() (on the RetroAchievements screen, a slow login failing) would then write through a deleted PopupTextInputChoice, possibly into the field that took its place: #21708. Requests made while creating views now get their own token, forgotten before the views are deleted. Screen-level requests keep the screen's token. RequestManager: ForgetRequestsWithToken() now locks, and also drops responses that arrived but weren't processed yet. ProcessRequests() calls the callbacks one at a time without holding the lock, so a callback that closes a screen drops the rest of that screen's responses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
156 lines
4.2 KiB
C++
156 lines
4.2 KiB
C++
// Super basic screen manager. Let's you, well, switch between screens. Can also be used
|
|
// to pop one screen in front for a bit while keeping another one running, it's basically
|
|
// a native "activity stack". Well actually that part is still a TODO.
|
|
//
|
|
// Semantics
|
|
//
|
|
// switchScreen: When you call this, on a newed screen, the ScreenManager takes ownership.
|
|
// On the next update, it switches to the new screen and deletes the previous screen.
|
|
//
|
|
// TODO: A way to do smooth transitions between screens. Will probably involve screenshotting
|
|
// the previous screen and then animating it on top of the current screen with transparency
|
|
// and/or other similar effects.
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
#include "Common/Common.h"
|
|
#include "Common/Input/InputState.h"
|
|
#include "Common/System/System.h"
|
|
|
|
namespace UI {
|
|
class View;
|
|
}
|
|
|
|
namespace Draw {
|
|
class DrawContext;
|
|
}
|
|
|
|
enum DialogResult {
|
|
DR_NONE,
|
|
DR_OK,
|
|
DR_CANCEL,
|
|
DR_YES,
|
|
DR_NO,
|
|
DR_BACK,
|
|
};
|
|
const char *DialogResultToString(DialogResult result);
|
|
|
|
class ScreenManager;
|
|
class UIContext;
|
|
|
|
enum class ScreenFocusChange {
|
|
FOCUS_LOST_TOP, // Another screen was pushed on top
|
|
FOCUS_BECAME_TOP, // Became the top screen again
|
|
};
|
|
|
|
enum class ScreenRenderMode {
|
|
DEFAULT = 0,
|
|
FIRST = 1,
|
|
BEHIND = 4,
|
|
TOP = 8,
|
|
};
|
|
ENUM_CLASS_BITOPS(ScreenRenderMode);
|
|
|
|
enum class ScreenRenderRole {
|
|
NONE = 0,
|
|
CAN_BE_BACKGROUND = 1,
|
|
MUST_BE_FIRST = 2,
|
|
};
|
|
ENUM_CLASS_BITOPS(ScreenRenderRole);
|
|
|
|
enum class ViewLayoutMode {
|
|
ApplyInsets = 0, // For gameplay, option #1
|
|
IgnoreInsets, // For gameplay, option #2
|
|
IgnoreBottomInset,
|
|
};
|
|
|
|
enum class QueuedEventType : u8 {
|
|
KEY,
|
|
AXIS,
|
|
TOUCH,
|
|
};
|
|
|
|
struct QueuedEvent {
|
|
QueuedEventType type;
|
|
union {
|
|
TouchInput touch;
|
|
KeyInput key;
|
|
AxisInput axis;
|
|
};
|
|
};
|
|
|
|
enum class ScreenRenderFlags {
|
|
NONE = 0,
|
|
HANDLED_THROTTLING = 1,
|
|
};
|
|
ENUM_CLASS_BITOPS(ScreenRenderFlags);
|
|
|
|
enum class InputMode {
|
|
None = 0,
|
|
Keyboard = 1,
|
|
Mouse = 2,
|
|
Other = 4,
|
|
ImDebuggerToggle = 8, // ugly hack. debugger and pause goes through.
|
|
};
|
|
ENUM_CLASS_BITOPS(InputMode);
|
|
|
|
class Screen {
|
|
public:
|
|
Screen() = default;
|
|
virtual ~Screen();
|
|
|
|
virtual void onFinish(DialogResult reason) {}
|
|
virtual void update() {}
|
|
virtual ScreenRenderFlags PreRender(ScreenRenderMode mode) { return ScreenRenderFlags::NONE; }
|
|
virtual ScreenRenderFlags render(ScreenRenderMode mode) = 0;
|
|
virtual void resized() {}
|
|
virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
|
|
virtual void sendMessage(UIMessage message, const char *value) {}
|
|
virtual void deviceLost() {}
|
|
virtual void deviceRestored(Draw::DrawContext *draw) {}
|
|
virtual ScreenRenderRole renderRole(bool isTop) const { return ScreenRenderRole::NONE; }
|
|
virtual bool wantBrightBackground() const { return false; } // special hack for DisplayLayoutScreen.
|
|
|
|
virtual void focusChanged(ScreenFocusChange focusChange);
|
|
|
|
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.
|
|
virtual InputMode PassInputToMapper() const { return InputMode::None; }
|
|
|
|
ScreenManager *screenManager() { return screenManager_; }
|
|
const ScreenManager *screenManager() const { return screenManager_; }
|
|
void setScreenManager(ScreenManager *sm) { screenManager_ = sm; }
|
|
|
|
virtual const char *tag() const = 0;
|
|
|
|
virtual bool isTransparent() const { return false; }
|
|
virtual bool isTopLevel() const { return false; }
|
|
|
|
virtual TouchInput transformTouch(const TouchInput &touch) { return touch; }
|
|
virtual bool WantsTextInput() const { return false; }
|
|
|
|
protected:
|
|
// Requests made while creating views (by the views themselves, or with callbacks that capture them) get a separate
|
|
// token, so they can be forgotten when the views are deleted, while the screen's own requests stay alive.
|
|
int GetRequesterToken();
|
|
void SetCreatingViews(bool creating) { creatingViews_ = creating; }
|
|
// Drops the callbacks of pending requests made by the views, which are about to be deleted.
|
|
void ForgetViewRequests();
|
|
|
|
private:
|
|
ScreenManager *screenManager_ = nullptr;
|
|
int token_ = -1;
|
|
int viewsToken_ = -1;
|
|
bool creatingViews_ = false;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Screen);
|
|
};
|