mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-01 20:18:15 +01:00
An external display (like an Apple TV over AirPlay) becomes a Game output, when the backend is Metal. The game is shown there full screen, and the phone shows the touch controls with "Playing on TV" in place of the game. The menus stay on the phone. With other backends, iOS keeps mirroring. Up to iOS 26 the external display scene comes from the Info.plist role. iOS 27 ignores that, and only offers the scene to apps that register a scene accessory for it, so we do that too. The registration is withdrawn when the view controller shuts down, so it follows a backend change, and a restart now goes to the app's scene rather than the first one. An Apple TV 4K only offers 3840x2160 over AirPlay, so the stream stays 4K, but we render at 1920x1080 and let the layer scale it up. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
170 lines
5.3 KiB
C++
170 lines
5.3 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#pragma once
|
|
|
|
#include <list>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "Common/File/Path.h"
|
|
#include "Common/Input/KeyCodes.h"
|
|
#include "Common/UI/Screen.h"
|
|
#include "Common/UI/UIScreen.h"
|
|
#include "Common/UI/Tween.h"
|
|
#include "Core/KeyMap.h"
|
|
#include "Core/ControlMapper.h"
|
|
#include "UI/ImDebugger/ImCommand.h"
|
|
|
|
struct AxisInput;
|
|
struct DisplayLayoutConfig;
|
|
|
|
class AsyncImageFileView;
|
|
class ChatMenu;
|
|
class ImDebugger;
|
|
struct ImCommand;
|
|
struct ImGuiContext;
|
|
|
|
class EmuScreen : public UIScreen, protected ControlListener {
|
|
public:
|
|
// mountIso, if set, is mounted on umd1: when filename is an ELF/PBP (like the VSH).
|
|
EmuScreen(const Path &filename, const Path &mountIso = Path());
|
|
~EmuScreen();
|
|
|
|
const char *tag() const override { return "Emu"; }
|
|
|
|
void update() override;
|
|
ScreenRenderFlags render(ScreenRenderMode mode) override;
|
|
void dialogFinished(const Screen *dialog, DialogResult result) override;
|
|
void sendMessage(UIMessage message, const char *value) override;
|
|
void resized() override;
|
|
ScreenRenderRole renderRole(bool isTop) const 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;
|
|
bool touch(const TouchInput &key) override;
|
|
|
|
void deviceLost() override;
|
|
void deviceRestored(Draw::DrawContext *draw) override;
|
|
|
|
protected:
|
|
void darken();
|
|
void drawGameOnOutputMessage();
|
|
void focusChanged(ScreenFocusChange focusChange) override;
|
|
ScreenRenderFlags PreRender(ScreenRenderMode mode) override;
|
|
bool renderOutput(const OutputDisplay &output) override;
|
|
|
|
// ControlListener implementations
|
|
void OnVKey(VirtKey virtualKeyCode, bool down) override;
|
|
void OnVKeyAnalog(VirtKey virtualKeyCode, float value) override;
|
|
void UpdatePSPButtons(uint32_t buttonMask, uint32_t changedMask) override;
|
|
void SetPSPAnalog(int rotation, int stick, float x, float y) override;
|
|
ViewLayoutMode LayoutMode() const override;
|
|
|
|
bool AllowFocusMovement() const override;
|
|
|
|
private:
|
|
void CreateViews() override;
|
|
ScreenRenderFlags RunEmulation(bool skipBufferEffects);
|
|
void UpdateGameOutput();
|
|
const DisplayLayoutConfig &GameLayoutConfig() const;
|
|
void OnDevTools(UI::EventParams ¶ms);
|
|
void OpenChat(bool focus);
|
|
|
|
void HandleFlip();
|
|
void ProcessGameBoot(const Path &filename);
|
|
bool bootAllowStorage(const Path &filename);
|
|
void bootComplete();
|
|
bool hasVisibleUI();
|
|
void renderUI();
|
|
|
|
void AutoLoadSaveState();
|
|
bool checkPowerDown();
|
|
void leaveGame();
|
|
|
|
void ProcessQueuedVKeys();
|
|
void ProcessVKey(VirtKey vkey, bool down);
|
|
|
|
bool ShouldRunEmulation(ScreenRenderMode mode) const;
|
|
|
|
UI::Event OnDevMenu;
|
|
bool bootPending_ = true;
|
|
bool bootIsReset_ = false;
|
|
Path gamePath_;
|
|
Path mountIso_;
|
|
|
|
bool quit_ = false;
|
|
std::string errorMessage_;
|
|
|
|
// If set, pauses at the end of the frame.
|
|
// We also poll the pause trigger from the ControlMapper. That needs refactoring.
|
|
bool pauseTrigger_ = false;
|
|
|
|
// The last read chat message count, and how many new ones there are.
|
|
int chatMessages_ = 0;
|
|
int newChatMessages_ = 0;
|
|
|
|
// In-memory save state used for freezeFrame, which is useful for debugging.
|
|
std::vector<u8> freezeState_;
|
|
|
|
std::string tag_;
|
|
|
|
double saveStatePreviewShownTime_ = 0.0;
|
|
AsyncImageFileView *saveStatePreview_ = nullptr;
|
|
int saveStateSlot_;
|
|
|
|
UI::CallbackColorTween *loadingViewColor_ = nullptr;
|
|
UI::VisibilityTween *loadingViewVisible_ = nullptr;
|
|
UI::Spinner *loadingSpinner_ = nullptr;
|
|
// Shown while a savestate is being saved or loaded, if that takes long enough to notice.
|
|
UI::Spinner *saveStateSpinner_ = nullptr;
|
|
double saveStateBusySince_ = -1.0;
|
|
UI::Button *resumeButton_ = nullptr;
|
|
UI::Button *resetButton_ = nullptr;
|
|
UI::Button *backButton_ = nullptr;
|
|
UI::Clickable *chatButton_ = nullptr;
|
|
ChatMenu *chatMenu_ = nullptr;
|
|
// Set when the chat opens. Stick movements don't reach the control mapper while it's open (key
|
|
// releases do), so what's held is let go up front.
|
|
bool releaseMapperInput_ = false;
|
|
|
|
UI::Button *cardboardDisableButton_ = nullptr;
|
|
|
|
std::string extraAssertInfoStr_;
|
|
|
|
std::mutex queuedVirtKeysLock_;
|
|
std::vector<std::pair<VirtKey, bool>> queuedVirtKeys_;
|
|
|
|
bool frameStep_ = false;
|
|
#ifndef MOBILE_DEVICE
|
|
bool startDumping_ = false;
|
|
#endif
|
|
bool autoLoadFailed_ = false; // to prevent repeat reloads
|
|
bool readyToFinishBoot_ = false;
|
|
bool skipBufferEffects_ = false; // cached state, fetched once per frame.
|
|
// The game goes to a secondary output (OutputRole::Game) instead of the backbuffer. Per frame too.
|
|
bool gameOnOutput_ = false;
|
|
DeviceOrientation gameOutputOrientation_ = DeviceOrientation::Landscape;
|
|
|
|
uint32_t clearColor_ = 0;
|
|
};
|
|
|
|
bool MustRunBehind();
|
|
bool ShouldRunBehind();
|