mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-31 19:47:06 +01:00
As on a PSP: pressing HOME in a game shows "Do you want to quit the game?" over it. The game keeps running but gets no input while it's up (sceCtrl samples are blanked). Left/right picks Yes or No, the confirm button (our Confirmation button setting, or what compat forces) chooses, the other face button or HOME closes it, and Yes/No can be tapped. The volume keys change the game volume a step at a time, shown on the volume bar. Yes runs the game's registered exit callback first, like the PSP does, showing "Please wait..." meanwhile. Then, if the XMB launched the game, the XMB is booted again; otherwise it's back to the game browser, like Exit to menu. If the callback hasn't ended the game in 10 s, it's quit anyway. Not in the XMB itself, the PS1 emulator (its own HOME menu) or GE dumps. It lives in UI/ImposeHome.cpp; EmuScreen only feeds it input and frames and draws it over the PSP display. With a firmware dump installed, it's drawn with the firmware's own art from flash0:/kd/resource/impose.rsc, which is what impose.prx draws it from: prerendered text for each system language (the question, Yes/No normal and highlighted, Enter/Back, Please wait...), the button icons, the lines, the volume bar, the battery and the clock glyphs. The format is documented in ImposeHome.cpp. Without one, the same screen is drawn with our font and simple shapes at the same positions. Where impose.rsc doesn't say where things go, the layout follows impose_02g.prx (6.61), decrypted and disassembled: - The screen is filled with 0x9CE7 (#393939); the images are opaque. - Lines at (80, 96) and (80, 175); impose.prx grows them from the middle when it opens, which isn't done here. - Volume bar: the speaker at (28, 223), 30 steps from x 62, 12 apart. It shows our game volume in the PSP's 30 steps. - Button guide: the cross icon always left, the circle right, "Enter" and "Back" next to whichever confirms and cancels. - Status bar: the battery at (450, 5) (60% and up full, 30% two bars, 12% one, then empty), the clock ending next to it at y 5: "M/D H:MM", or "D/M" for the DD/MM/YYYY format, no leading zero on the month, day or hour, AM/PM for the 12-hour clock. The clock is the emulated PSP's local time (__RtcGetCurrentLocalTime, new), in the system's date and time formats. The battery is this device's, since the emulated PSP's is always full; a device without one shows it full. For that, Windows now reports -1 for SYSPROP_BATTERY_PERCENTAGE without a battery (was 100), as SDL already did; SDL polls the battery even when the status overlay doesn't show it, and the overlay leaves it out when there's none. Core side: __CtrlSetGameInputBlocked; __KernelCanExitToVSH and __KernelRequestExitToVSH to quit to the XMB from outside a syscall; and __KernelInvokeRegisteredExitCallback(toVSH), so a callback that returns without exiting ends in the XMB rather than powering down. New strings: "Do you want to quit the game?", "Please wait..." (Dialog). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NB244iFpvm14JH2FbwHXeQ
185 lines
6.1 KiB
C++
185 lines
6.1 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 <memory>
|
|
#include <optional>
|
|
#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 "Core/CoreParameter.h"
|
|
#include "UI/ImDebugger/ImCommand.h"
|
|
|
|
struct AxisInput;
|
|
struct DisplayLayoutConfig;
|
|
|
|
class AsyncImageFileView;
|
|
class ImposeHomeScreen;
|
|
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).
|
|
// umdAutoboot overrides the VSH's UMD auto-start setting.
|
|
EmuScreen(const Path &filename, const Path &mountIso = Path(), std::optional<bool> umdAutoboot = std::nullopt);
|
|
~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();
|
|
DeviceOrientation GameLayoutOrientation() const;
|
|
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_;
|
|
std::optional<bool> umdAutoboot_;
|
|
// The rest of a BootRequest from the emulated system (see PSP_RequestBoot).
|
|
std::optional<VSHLaunch> launchedFromVSH_;
|
|
std::vector<u8> vshmainArgs_;
|
|
|
|
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;
|
|
|
|
// The HOME button's quit screen (as impose draws it on a PSP).
|
|
std::unique_ptr<ImposeHomeScreen> imposeHome_;
|
|
};
|
|
|
|
bool MustRunBehind();
|
|
bool ShouldRunBehind();
|
|
// The orientation of the output the game goes to (like a TV), if there's one. Its display layout is the one for that.
|
|
bool GetGameOutputOrientation(ScreenManager *screenManager, DeviceOrientation *orientation);
|