mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-31 19:47:06 +01:00
The ScreenManager keeps a list of secondary outputs, each with its own swapchain, recreated after a lost device. Platform code adds, resizes and removes them from any thread through Native_AddOutput() and friends, which take effect at the start of the next frame. Each frame, every output is drawn by the topmost screen that claims it (Screen::renderOutput). EmuScreen claims Game outputs: it presents the game there, laid out for that output, and leaves it out of the backbuffer, where the touch controls and the menus stay. Not in non-buffered mode, which draws straight into the backbuffer. --game-window opens such an output on Mac, with Metal. It's a smoke test for multiple swapchains, and a stand-in for an AirPlay display. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
170 lines
4.9 KiB
C++
170 lines
4.9 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/NativeApp.h"
|
|
#include "Common/System/System.h"
|
|
|
|
namespace UI {
|
|
class View;
|
|
}
|
|
|
|
namespace Draw {
|
|
class DrawContext;
|
|
class Swapchain;
|
|
}
|
|
|
|
// A display other than the primary one, like a second window or a TV. See docs/multi-swapchain.md.
|
|
struct OutputDisplay {
|
|
int id;
|
|
OutputRole role;
|
|
Draw::Swapchain *swapchain; // nullptr while there's no draw context.
|
|
};
|
|
|
|
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.
|
|
AllKeys = 16, // The screen gets keys that are normally left to the OS, like volume. For key mapping.
|
|
};
|
|
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;
|
|
// Draws this screen's content for a secondary output, whose swapchain is bound. Returns false if the
|
|
// screen has nothing for it. The topmost screen that returns true is the one shown.
|
|
virtual bool renderOutput(const OutputDisplay &output) { return false; }
|
|
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; }
|
|
bool IsCreatingViews() const { return creatingViews_; }
|
|
// 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);
|
|
};
|