dispaly
This commit is contained in:
parent
15dc8d038f
commit
5e278032c2
|
@ -17,8 +17,8 @@
|
|||
#include "dma.h"
|
||||
#include "fmt/chrono.h"
|
||||
#include "fmt/format.h"
|
||||
#include "gpu.h"
|
||||
#include "game_database.h"
|
||||
#include "gpu.h"
|
||||
#include "gte.h"
|
||||
#include "host.h"
|
||||
#include "host_display.h"
|
||||
|
@ -226,7 +226,7 @@ bool System::IsShutdown()
|
|||
|
||||
bool System::IsValid()
|
||||
{
|
||||
return s_state != State::Shutdown && s_state != State::Starting;
|
||||
return s_state == State::Running || s_state == State::Paused;
|
||||
}
|
||||
|
||||
bool System::IsStartupCancelled()
|
||||
|
@ -900,18 +900,15 @@ bool System::BootSystem(std::shared_ptr<SystemBootParameters> parameters)
|
|||
{
|
||||
Host::OnSystemStarting();
|
||||
|
||||
if (!parameters->state_stream)
|
||||
{
|
||||
if (parameters->filename.empty())
|
||||
Log_InfoPrintf("Boot Filename: <BIOS/Shell>");
|
||||
else
|
||||
Log_InfoPrintf("Boot Filename: %s", parameters->filename.c_str());
|
||||
}
|
||||
if (parameters->filename.empty())
|
||||
Log_InfoPrintf("Boot Filename: <BIOS/Shell>");
|
||||
else
|
||||
Log_InfoPrintf("Boot Filename: %s", parameters->filename.c_str());
|
||||
|
||||
// In Challenge mode, do not allow loading a save state under any circumstances
|
||||
// If it's present, drop it
|
||||
if (Cheevos::IsChallengeModeActive())
|
||||
parameters->state_stream.reset();
|
||||
parameters->save_state.clear();
|
||||
|
||||
if (!Host::AcquireHostDisplay())
|
||||
{
|
||||
|
@ -946,6 +943,29 @@ bool System::BootSystem(std::shared_ptr<SystemBootParameters> parameters)
|
|||
UpdateSoftwareCursor();
|
||||
g_spu.GetOutputStream()->PauseOutput(false);
|
||||
Host::OnSystemStarted();
|
||||
|
||||
if (!parameters->save_state.empty())
|
||||
{
|
||||
// try to load the state, if it fails, bail out
|
||||
std::unique_ptr<ByteStream> stream =
|
||||
ByteStream::OpenFile(parameters->save_state.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||
if (!stream)
|
||||
{
|
||||
Host::ReportErrorAsync(
|
||||
Host::TranslateString("System", "Error"),
|
||||
fmt::format(Host::TranslateString("System", "Failed to load save state file '{}' for booting.").GetCharArray(),
|
||||
parameters->save_state));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DoLoadState(stream.get(), false, true))
|
||||
{
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -999,34 +1019,26 @@ void System::DestroySystem()
|
|||
|
||||
bool System::LoadState(const char* filename)
|
||||
{
|
||||
if (!IsValid())
|
||||
return false;
|
||||
|
||||
std::unique_ptr<ByteStream> stream = ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Loading state from '%s'..."), filename);
|
||||
|
||||
if (IsValid())
|
||||
SaveUndoLoadState();
|
||||
|
||||
if (!InternalLoadState(stream.get()))
|
||||
{
|
||||
SaveUndoLoadState();
|
||||
Host::ReportFormattedErrorAsync(
|
||||
"Load State Error", Host::TranslateString("OSDMessage", "Loading state from '%s' failed. Resetting."), filename);
|
||||
|
||||
if (!InternalLoadState(stream.get()))
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Load State Error",
|
||||
Host::TranslateString("OSDMessage", "Loading state from '%s' failed. Resetting."),
|
||||
filename);
|
||||
if (m_undo_load_state)
|
||||
UndoLoadState();
|
||||
|
||||
if (m_undo_load_state)
|
||||
UndoLoadState();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto boot_params = std::make_shared<SystemBootParameters>();
|
||||
boot_params->state_stream = std::move(stream);
|
||||
if (!BootSystem(std::move(boot_params)))
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
ResetPerformanceCounters();
|
||||
|
@ -1066,23 +1078,6 @@ bool System::InternalBoot(const SystemBootParameters& params)
|
|||
s_startup_cancelled.store(false);
|
||||
s_region = g_settings.region;
|
||||
|
||||
if (params.state_stream)
|
||||
{
|
||||
if (!DoLoadState(params.state_stream.get(), params.force_software_renderer, true))
|
||||
{
|
||||
InternalShutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_settings.start_paused || params.override_start_paused.value_or(false))
|
||||
{
|
||||
DebugAssert(s_state == State::Running);
|
||||
s_state = State::Paused;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load CD image up and detect region.
|
||||
Common::Error error;
|
||||
std::unique_ptr<CDImage> media;
|
||||
|
@ -1214,6 +1209,11 @@ bool System::InternalBoot(const SystemBootParameters& params)
|
|||
|
||||
// Good to go.
|
||||
s_state = (g_settings.start_paused || params.override_start_paused.value_or(false)) ? State::Paused : State::Running;
|
||||
if (s_state == State::Paused)
|
||||
Host::OnSystemPaused();
|
||||
else
|
||||
Host::OnSystemResumed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1406,13 +1406,18 @@ void System::RecreateSystem()
|
|||
DestroySystem();
|
||||
|
||||
auto boot_params = std::make_shared<SystemBootParameters>();
|
||||
boot_params->state_stream = std::move(stream);
|
||||
if (!BootSystem(std::move(boot_params)))
|
||||
{
|
||||
Host::ReportErrorAsync("Error", "Failed to boot system after recreation.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DoLoadState(stream.get(), false, false))
|
||||
{
|
||||
DestroySystem();
|
||||
return;
|
||||
}
|
||||
|
||||
ResetPerformanceCounters();
|
||||
ResetThrottler();
|
||||
Host::RenderDisplay();
|
||||
|
@ -2742,7 +2747,7 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool force)
|
|||
s_running_game_code = entry->serial;
|
||||
s_running_game_title = entry->title;
|
||||
}
|
||||
|
||||
|
||||
if (image && image->HasSubImages() && g_settings.memory_card_use_playlist_title)
|
||||
{
|
||||
std::string image_title(image->GetMetadata("title"));
|
||||
|
@ -3520,63 +3525,6 @@ bool System::SaveStateToSlot(bool global, s32 slot)
|
|||
return SaveState(save_path.c_str());
|
||||
}
|
||||
|
||||
bool System::CanResumeSystemFromFile(const char* filename)
|
||||
{
|
||||
if (Host::GetBaseBoolSettingValue("Main", "SaveStateOnExit", true) && !Cheevos::IsChallengeModeActive())
|
||||
{
|
||||
#if 0
|
||||
const GameListEntry* entry = m_game_list->GetEntryForPath(filename);
|
||||
if (entry)
|
||||
return !entry->code.empty();
|
||||
else
|
||||
return !System::GetGameCodeForPath(filename, true).empty();
|
||||
#else
|
||||
Panic("Fixme");
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool System::ResumeSystemFromState(const char* filename, bool boot_on_failure)
|
||||
{
|
||||
if (!BootSystem(std::make_shared<SystemBootParameters>(filename)))
|
||||
return false;
|
||||
|
||||
const bool global = System::GetRunningCode().empty();
|
||||
if (global)
|
||||
{
|
||||
Host::ReportErrorAsync("Error",
|
||||
fmt::format("Cannot resume system with undetectable game code from '{}'.", filename));
|
||||
if (!boot_on_failure)
|
||||
{
|
||||
DestroySystem();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string path = GetGameSaveStateFileName(System::GetRunningCode().c_str(), -1);
|
||||
if (FileSystem::FileExists(path.c_str()))
|
||||
{
|
||||
if (!LoadState(path.c_str()) && !boot_on_failure)
|
||||
{
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!boot_on_failure)
|
||||
{
|
||||
Host::ReportErrorAsync("Error", fmt::format("Resume save state not found for '{}' ('{}').",
|
||||
System::GetRunningCode(), System::GetRunningTitle()));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool System::ResumeSystemFromMostRecentState()
|
||||
{
|
||||
const std::string path = GetMostRecentResumeSaveStatePath();
|
||||
|
|
|
@ -24,10 +24,10 @@ struct SystemBootParameters
|
|||
~SystemBootParameters();
|
||||
|
||||
std::string filename;
|
||||
std::string save_state;
|
||||
std::optional<bool> override_fast_boot;
|
||||
std::optional<bool> override_fullscreen;
|
||||
std::optional<bool> override_start_paused;
|
||||
std::unique_ptr<ByteStream> state_stream;
|
||||
u32 media_playlist_index = 0;
|
||||
bool load_image_to_ram = false;
|
||||
bool force_software_renderer = false;
|
||||
|
@ -335,12 +335,6 @@ std::optional<ExtendedSaveStateInfo> GetUndoSaveStateInfo();
|
|||
/// Undoes a load state, i.e. restores the state prior to the load.
|
||||
bool UndoLoadState();
|
||||
|
||||
/// Returns true if the specified file/disc image is resumable.
|
||||
bool CanResumeSystemFromFile(const char* filename);
|
||||
|
||||
/// Loads the resume save state for the given game. Optionally boots the game anyway if loading fails.
|
||||
bool ResumeSystemFromState(const char* filename, bool boot_on_failure);
|
||||
|
||||
/// Loads the most recent resume save state. This may be global or per-game.
|
||||
bool ResumeSystemFromMostRecentState();
|
||||
|
||||
|
|
|
@ -22,22 +22,25 @@ DebuggerWindow::DebuggerWindow(QWidget* parent /* = nullptr */)
|
|||
|
||||
DebuggerWindow::~DebuggerWindow() = default;
|
||||
|
||||
void DebuggerWindow::onEmulationPaused(bool paused)
|
||||
void DebuggerWindow::onEmulationPaused()
|
||||
{
|
||||
if (paused)
|
||||
{
|
||||
setUIEnabled(true);
|
||||
refreshAll();
|
||||
refreshBreakpointList();
|
||||
}
|
||||
else
|
||||
{
|
||||
setUIEnabled(false);
|
||||
}
|
||||
setUIEnabled(true);
|
||||
refreshAll();
|
||||
refreshBreakpointList();
|
||||
|
||||
{
|
||||
QSignalBlocker sb(m_ui.actionPause);
|
||||
m_ui.actionPause->setChecked(paused);
|
||||
m_ui.actionPause->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerWindow::onEmulationResumed()
|
||||
{
|
||||
setUIEnabled(false);
|
||||
|
||||
{
|
||||
QSignalBlocker sb(m_ui.actionPause);
|
||||
m_ui.actionPause->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,7 +384,8 @@ void DebuggerWindow::setupAdditionalUi()
|
|||
void DebuggerWindow::connectSignals()
|
||||
{
|
||||
QtHostInterface* hi = g_emu_thread;
|
||||
connect(hi, &QtHostInterface::emulationPaused, this, &DebuggerWindow::onEmulationPaused);
|
||||
connect(hi, &QtHostInterface::systemPaused, this, &DebuggerWindow::onEmulationPaused);
|
||||
connect(hi, &QtHostInterface::systemResumed, this, &DebuggerWindow::onEmulationResumed);
|
||||
connect(hi, &QtHostInterface::debuggerMessageReported, this, &DebuggerWindow::onDebuggerMessageReported);
|
||||
|
||||
connect(m_ui.actionPause, &QAction::toggled, this, &DebuggerWindow::onPauseActionToggled);
|
||||
|
|
|
@ -25,7 +25,8 @@ Q_SIGNALS:
|
|||
void closed();
|
||||
|
||||
public Q_SLOTS:
|
||||
void onEmulationPaused(bool paused);
|
||||
void onEmulationPaused();
|
||||
void onEmulationResumed();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event);
|
||||
|
|
|
@ -0,0 +1,427 @@
|
|||
#include "displaywidget.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/bitutils.h"
|
||||
#include "common/log.h"
|
||||
#include "mainwindow.h"
|
||||
#include "qthost.h"
|
||||
#include "qtutils.h"
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QWindowStateChangeEvent>
|
||||
#include <cmath>
|
||||
|
||||
#if !defined(_WIN32) && !defined(APPLE)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
Log_SetChannel(DisplayWidget);
|
||||
|
||||
DisplayWidget::DisplayWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
// We want a native window for both D3D and OpenGL.
|
||||
setAutoFillBackground(false);
|
||||
setAttribute(Qt::WA_NativeWindow, true);
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setAttribute(Qt::WA_PaintOnScreen, true);
|
||||
setAttribute(Qt::WA_KeyCompression, false);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
DisplayWidget::~DisplayWidget()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_clip_mouse_enabled)
|
||||
ClipCursor(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
qreal DisplayWidget::devicePixelRatioFromScreen() const
|
||||
{
|
||||
const QScreen* screen_for_ratio = screen();
|
||||
if (!screen_for_ratio)
|
||||
screen_for_ratio = QGuiApplication::primaryScreen();
|
||||
|
||||
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
|
||||
}
|
||||
|
||||
int DisplayWidget::scaledWindowWidth() const
|
||||
{
|
||||
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * devicePixelRatioFromScreen())), 1);
|
||||
}
|
||||
|
||||
int DisplayWidget::scaledWindowHeight() const
|
||||
{
|
||||
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen())), 1);
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> DisplayWidget::getWindowInfo()
|
||||
{
|
||||
WindowInfo wi;
|
||||
|
||||
// Windows and Apple are easy here since there's no display connection.
|
||||
#if defined(_WIN32)
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
#elif defined(__APPLE__)
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("xcb"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
}
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||||
wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle());
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Unknown PNI platform " << platform_name;
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_last_window_width = wi.surface_width = static_cast<u32>(scaledWindowWidth());
|
||||
m_last_window_height = wi.surface_height = static_cast<u32>(scaledWindowHeight());
|
||||
m_last_window_scale = wi.surface_scale = static_cast<float>(devicePixelRatioFromScreen());
|
||||
return wi;
|
||||
}
|
||||
|
||||
void DisplayWidget::updateRelativeMode(bool master_enable)
|
||||
{
|
||||
bool relative_mode = master_enable && InputManager::HasPointerAxisBinds();
|
||||
|
||||
#ifdef _WIN32
|
||||
// prefer ClipCursor() over warping movement when we're using raw input
|
||||
bool clip_cursor = relative_mode && InputManager::IsUsingRawInput();
|
||||
if (m_relative_mouse_enabled == relative_mode && m_clip_mouse_enabled == clip_cursor)
|
||||
return;
|
||||
|
||||
Log_InfoPrintf("updateRelativeMode(): relative=%s, clip=%s", relative_mode ? "yes" : "no",
|
||||
clip_cursor ? "yes" : "no");
|
||||
|
||||
if (!clip_cursor && m_clip_mouse_enabled)
|
||||
{
|
||||
m_clip_mouse_enabled = false;
|
||||
ClipCursor(nullptr);
|
||||
}
|
||||
#else
|
||||
if (m_relative_mouse_enabled == relative_mode)
|
||||
return;
|
||||
|
||||
Log_InfoPrintf("updateRelativeMode(): relative=%s", relative_mode ? "yes" : "no");
|
||||
#endif
|
||||
|
||||
if (relative_mode)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_relative_mouse_enabled = !clip_cursor;
|
||||
m_clip_mouse_enabled = clip_cursor;
|
||||
#else
|
||||
m_relative_mouse_enabled = true;
|
||||
#endif
|
||||
m_relative_mouse_start_pos = QCursor::pos();
|
||||
updateCenterPos();
|
||||
grabMouse();
|
||||
}
|
||||
else if (m_relative_mouse_enabled)
|
||||
{
|
||||
m_relative_mouse_enabled = false;
|
||||
QCursor::setPos(m_relative_mouse_start_pos);
|
||||
releaseMouse();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayWidget::updateCursor(bool master_enable)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled || m_clip_mouse_enabled);
|
||||
#else
|
||||
const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled);
|
||||
#endif
|
||||
if (m_cursor_hidden == hide)
|
||||
return;
|
||||
|
||||
m_cursor_hidden = hide;
|
||||
if (hide)
|
||||
setCursor(Qt::BlankCursor);
|
||||
else
|
||||
unsetCursor();
|
||||
}
|
||||
|
||||
void DisplayWidget::updateCenterPos()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_clip_mouse_enabled)
|
||||
{
|
||||
RECT rc;
|
||||
if (GetWindowRect(reinterpret_cast<HWND>(winId()), &rc))
|
||||
ClipCursor(&rc);
|
||||
}
|
||||
else if (m_relative_mouse_enabled)
|
||||
{
|
||||
RECT rc;
|
||||
if (GetWindowRect(reinterpret_cast<HWND>(winId()), &rc))
|
||||
{
|
||||
m_relative_mouse_center_pos.setX(((rc.right - rc.left) / 2) + rc.left);
|
||||
m_relative_mouse_center_pos.setY(((rc.bottom - rc.top) / 2) + rc.top);
|
||||
SetCursorPos(m_relative_mouse_center_pos.x(), m_relative_mouse_center_pos.y());
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (m_relative_mouse_enabled)
|
||||
{
|
||||
// we do a round trip here because these coordinates are dpi-unscaled
|
||||
m_relative_mouse_center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
|
||||
QCursor::setPos(m_relative_mouse_center_pos);
|
||||
m_relative_mouse_center_pos = QCursor::pos();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QPaintEngine* DisplayWidget::paintEngine() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DisplayWidget::event(QEvent* event)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
{
|
||||
const QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
|
||||
if (key_event->isAutoRepeat())
|
||||
return true;
|
||||
|
||||
// For some reason, Windows sends "fake" key events.
|
||||
// Scenario: Press shift, press F1, release shift, release F1.
|
||||
// Events: Shift=Pressed, F1=Pressed, Shift=Released, **F1=Pressed**, F1=Released.
|
||||
// To work around this, we keep track of keys pressed with modifiers in a list, and
|
||||
// discard the press event when it's been previously activated. It's pretty gross,
|
||||
// but I can't think of a better way of handling it, and there doesn't appear to be
|
||||
// any window flag which changes this behavior that I can see.
|
||||
|
||||
const u32 key = QtUtils::KeyEventToCode(key_event);
|
||||
const Qt::KeyboardModifiers modifiers = key_event->modifiers();
|
||||
const bool pressed = (key_event->type() == QEvent::KeyPress);
|
||||
const auto it = std::find(m_keys_pressed_with_modifiers.begin(), m_keys_pressed_with_modifiers.end(), key);
|
||||
if (it != m_keys_pressed_with_modifiers.end())
|
||||
{
|
||||
if (pressed)
|
||||
return true;
|
||||
else
|
||||
m_keys_pressed_with_modifiers.erase(it);
|
||||
}
|
||||
else if (modifiers != Qt::NoModifier && modifiers != Qt::KeypadModifier && pressed)
|
||||
{
|
||||
m_keys_pressed_with_modifiers.push_back(key);
|
||||
}
|
||||
|
||||
emit windowKeyEvent(key, pressed);
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
const QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||||
|
||||
if (!m_relative_mouse_enabled)
|
||||
{
|
||||
const qreal dpr = devicePixelRatioFromScreen();
|
||||
const QPoint mouse_pos = mouse_event->pos();
|
||||
|
||||
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * dpr);
|
||||
const float scaled_y = static_cast<float>(static_cast<qreal>(mouse_pos.y()) * dpr);
|
||||
emit windowMouseMoveEvent(false, scaled_x, scaled_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// On windows, we use winapi here. The reason being that the coordinates in QCursor
|
||||
// are un-dpi-scaled, so we lose precision at higher desktop scalings.
|
||||
float dx = 0.0f, dy = 0.0f;
|
||||
|
||||
#ifndef _WIN32
|
||||
const QPoint mouse_pos = QCursor::pos();
|
||||
if (mouse_pos != m_relative_mouse_center_pos)
|
||||
{
|
||||
dx = static_cast<float>(mouse_pos.x() - m_relative_mouse_center_pos.x());
|
||||
dy = static_cast<float>(mouse_pos.y() - m_relative_mouse_center_pos.y());
|
||||
QCursor::setPos(m_relative_mouse_center_pos);
|
||||
}
|
||||
#else
|
||||
POINT mouse_pos;
|
||||
if (GetCursorPos(&mouse_pos))
|
||||
{
|
||||
dx = static_cast<float>(mouse_pos.x - m_relative_mouse_center_pos.x());
|
||||
dy = static_cast<float>(mouse_pos.y - m_relative_mouse_center_pos.y());
|
||||
SetCursorPos(m_relative_mouse_center_pos.x(), m_relative_mouse_center_pos.y());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dx != 0.0f || dy != 0.0f)
|
||||
emit windowMouseMoveEvent(true, dx, dy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::MouseButtonRelease:
|
||||
{
|
||||
const u32 button_index = CountTrailingZeros(static_cast<u32>(static_cast<const QMouseEvent*>(event)->button()));
|
||||
emit windowMouseButtonEvent(static_cast<int>(button_index + 1u), event->type() != QEvent::MouseButtonRelease);
|
||||
|
||||
// don't toggle fullscreen when we're bound.. that wouldn't end well.
|
||||
if (event->type() == QEvent::MouseButtonDblClick &&
|
||||
static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton &&
|
||||
!InputManager::HasAnyBindingsForKey(InputManager::MakePointerButtonKey(0, 0)) &&
|
||||
Host::GetBoolSettingValue("UI", "DoubleClickTogglesFullscreen", true))
|
||||
{
|
||||
g_emu_thread->toggleFullscreen();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Wheel:
|
||||
{
|
||||
const QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
|
||||
emit windowMouseWheelEvent(wheel_event->angleDelta());
|
||||
return true;
|
||||
}
|
||||
|
||||
// According to https://bugreports.qt.io/browse/QTBUG-95925 the recommended practice for handling DPI change is
|
||||
// responding to paint events
|
||||
case QEvent::Paint:
|
||||
case QEvent::Resize:
|
||||
{
|
||||
QWidget::event(event);
|
||||
|
||||
const float dpr = devicePixelRatioFromScreen();
|
||||
const u32 scaled_width =
|
||||
static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * dpr)), 1));
|
||||
const u32 scaled_height =
|
||||
static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * dpr)), 1));
|
||||
|
||||
// avoid spamming resize events for paint events (sent on move on windows)
|
||||
if (m_last_window_width != scaled_width || m_last_window_height != scaled_height || m_last_window_scale != dpr)
|
||||
{
|
||||
m_last_window_width = scaled_width;
|
||||
m_last_window_height = scaled_height;
|
||||
m_last_window_scale = dpr;
|
||||
emit windowResizedEvent(scaled_width, scaled_height, dpr);
|
||||
}
|
||||
|
||||
updateCenterPos();
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Move:
|
||||
{
|
||||
updateCenterPos();
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Close:
|
||||
{
|
||||
// Closing the separate widget will either cancel the close, or trigger shutdown.
|
||||
// In the latter case, it's going to destroy us, so don't let Qt do it first.
|
||||
QMetaObject::invokeMethod(g_main_window, "requestShutdown", Q_ARG(bool, true), Q_ARG(bool, true),
|
||||
Q_ARG(bool, false));
|
||||
event->ignore();
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::WindowStateChange:
|
||||
{
|
||||
QWidget::event(event);
|
||||
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit windowRestoredEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return QWidget::event(event);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayContainer::DisplayContainer() : QStackedWidget(nullptr) {}
|
||||
|
||||
DisplayContainer::~DisplayContainer() = default;
|
||||
|
||||
bool DisplayContainer::IsNeeded(bool fullscreen, bool render_to_main)
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
return false;
|
||||
#else
|
||||
if (!fullscreen && render_to_main)
|
||||
return false;
|
||||
|
||||
// We only need this on Wayland because of client-side decorations...
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
return (platform_name == QStringLiteral("wayland"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayContainer::setDisplayWidget(DisplayWidget* widget)
|
||||
{
|
||||
Assert(!m_display_widget);
|
||||
m_display_widget = widget;
|
||||
addWidget(widget);
|
||||
}
|
||||
|
||||
DisplayWidget* DisplayContainer::removeDisplayWidget()
|
||||
{
|
||||
DisplayWidget* widget = m_display_widget;
|
||||
Assert(widget);
|
||||
m_display_widget = nullptr;
|
||||
removeWidget(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
bool DisplayContainer::event(QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::Close)
|
||||
{
|
||||
// Closing the separate widget will either cancel the close, or trigger shutdown.
|
||||
// In the latter case, it's going to destroy us, so don't let Qt do it first.
|
||||
QMetaObject::invokeMethod(g_main_window, "requestShutdown", Q_ARG(bool, true), Q_ARG(bool, true),
|
||||
Q_ARG(bool, false));
|
||||
event->ignore();
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool res = QStackedWidget::event(event);
|
||||
if (!m_display_widget)
|
||||
return res;
|
||||
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::WindowStateChange:
|
||||
{
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit m_display_widget->windowRestoredEvent();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
#include "common/types.h"
|
||||
#include "common/window_info.h"
|
||||
#include <QtWidgets/QStackedWidget>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <optional>
|
||||
|
||||
class DisplayWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DisplayWidget(QWidget* parent);
|
||||
~DisplayWidget();
|
||||
|
||||
QPaintEngine* paintEngine() const override;
|
||||
|
||||
ALWAYS_INLINE void setShouldHideCursor(bool hide) { m_should_hide_cursor = hide; }
|
||||
|
||||
int scaledWindowWidth() const;
|
||||
int scaledWindowHeight() const;
|
||||
qreal devicePixelRatioFromScreen() const;
|
||||
|
||||
std::optional<WindowInfo> getWindowInfo();
|
||||
|
||||
void updateRelativeMode(bool master_enable);
|
||||
void updateCursor(bool master_enable);
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowResizedEvent(int width, int height, float scale);
|
||||
void windowRestoredEvent();
|
||||
void windowKeyEvent(int key_code, bool pressed);
|
||||
void windowMouseMoveEvent(bool relative, float x, float y);
|
||||
void windowMouseButtonEvent(int button, bool pressed);
|
||||
void windowMouseWheelEvent(const QPoint& angle_delta);
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
void updateCenterPos();
|
||||
|
||||
QPoint m_relative_mouse_start_pos{};
|
||||
QPoint m_relative_mouse_center_pos{};
|
||||
bool m_relative_mouse_enabled = false;
|
||||
#ifdef _WIN32
|
||||
bool m_clip_mouse_enabled = false;
|
||||
#endif
|
||||
bool m_should_hide_cursor = false;
|
||||
bool m_cursor_hidden = false;
|
||||
|
||||
std::vector<int> m_keys_pressed_with_modifiers;
|
||||
|
||||
u32 m_last_window_width = 0;
|
||||
u32 m_last_window_height = 0;
|
||||
float m_last_window_scale = 1.0f;
|
||||
};
|
||||
|
||||
class DisplayContainer final : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DisplayContainer();
|
||||
~DisplayContainer();
|
||||
|
||||
static bool IsNeeded(bool fullscreen, bool render_to_main);
|
||||
|
||||
void setDisplayWidget(DisplayWidget* widget);
|
||||
DisplayWidget* removeDisplayWidget();
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
DisplayWidget* m_display_widget = nullptr;
|
||||
};
|
|
@ -28,7 +28,7 @@
|
|||
<ClCompile Include="inputbindingdialog.cpp" />
|
||||
<ClCompile Include="inputbindingwidgets.cpp" />
|
||||
<ClCompile Include="memoryviewwidget.cpp" />
|
||||
<ClCompile Include="qtdisplaywidget.cpp" />
|
||||
<ClCompile Include="displaywidget.cpp" />
|
||||
<ClCompile Include="gamelistsettingswidget.cpp" />
|
||||
<ClCompile Include="gamelistrefreshthread.cpp" />
|
||||
<ClCompile Include="gamelistwidget.cpp" />
|
||||
|
@ -57,7 +57,7 @@
|
|||
<QtMoc Include="enhancementsettingswidget.h" />
|
||||
<QtMoc Include="memorycardsettingswidget.h" />
|
||||
<QtMoc Include="memorycardeditordialog.h" />
|
||||
<QtMoc Include="qtdisplaywidget.h" />
|
||||
<QtMoc Include="displaywidget.h" />
|
||||
<QtMoc Include="generalsettingswidget.h" />
|
||||
<QtMoc Include="displaysettingswidget.h" />
|
||||
<QtMoc Include="hotkeysettingswidget.h" />
|
||||
|
@ -208,6 +208,7 @@
|
|||
<ClCompile Include="$(IntDir)moc_controllerbindingwidgets.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_controllerglobalsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_controllersettingsdialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_displaywidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_emulationsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_enhancementsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_gamelistmodel.cpp" />
|
||||
|
@ -232,7 +233,6 @@
|
|||
<ClCompile Include="$(IntDir)moc_postprocessingchainconfigwidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_postprocessingshaderconfigwidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_postprocessingsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_qtdisplaywidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_qthost.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_settingsdialog.cpp" />
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<ClCompile Include="audiosettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_audiosettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_qtdisplaywidget.cpp" />
|
||||
<ClCompile Include="qtdisplaywidget.cpp" />
|
||||
<ClCompile Include="displaywidget.cpp" />
|
||||
<ClCompile Include="qtprogresscallback.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
|
||||
<ClCompile Include="generalsettingswidget.cpp" />
|
||||
|
@ -87,6 +87,8 @@
|
|||
<ClCompile Include="$(IntDir)moc_controllerbindingwidgets.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_controllerglobalsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_controllersettingsdialog.cpp" />
|
||||
<ClCompile Include="gamelistrefreshthread.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_gamelistrefreshthread.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="qtutils.h" />
|
||||
|
@ -112,7 +114,7 @@
|
|||
<QtMoc Include="hotkeysettingswidget.h" />
|
||||
<QtMoc Include="inputbindingwidgets.h" />
|
||||
<QtMoc Include="audiosettingswidget.h" />
|
||||
<QtMoc Include="qtdisplaywidget.h" />
|
||||
<QtMoc Include="displaywidget.h" />
|
||||
<QtMoc Include="generalsettingswidget.h" />
|
||||
<QtMoc Include="qtprogresscallback.h" />
|
||||
<QtMoc Include="advancedsettingswidget.h" />
|
||||
|
@ -144,6 +146,7 @@
|
|||
<QtMoc Include="controllerbindingwidgets.h" />
|
||||
<QtMoc Include="controllerglobalsettingswidget.h" />
|
||||
<QtMoc Include="controllersettingsdialog.h" />
|
||||
<QtMoc Include="gamelistrefreshthread.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtUi Include="consolesettingswidget.ui" />
|
||||
|
@ -174,6 +177,8 @@
|
|||
<QtUi Include="controllerglobalsettingswidget.ui" />
|
||||
<QtUi Include="controllersettingsdialog.ui" />
|
||||
<QtUi Include="controllerbindingwidget_digital_controller.ui" />
|
||||
<QtUi Include="emptygamelistwidget.ui" />
|
||||
<QtUi Include="gamelistwidget.ui" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="qt5.natvis" />
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
#include "gdbconnection.h"
|
||||
#include "qthost.h"
|
||||
#include "common/log.h"
|
||||
#include "core/gdb_protocol.h"
|
||||
#include "qthost.h"
|
||||
Log_SetChannel(GDBConnection);
|
||||
|
||||
GDBConnection::GDBConnection(QObject *parent, int descriptor)
|
||||
: QThread(parent), m_descriptor(descriptor)
|
||||
GDBConnection::GDBConnection(QObject* parent, int descriptor) : QThread(parent), m_descriptor(descriptor)
|
||||
{
|
||||
Log_InfoPrintf("(%u) Accepted new connection on GDB server", m_descriptor);
|
||||
|
||||
connect(&m_socket, &QTcpSocket::readyRead, this, &GDBConnection::receivedData);
|
||||
connect(&m_socket, &QTcpSocket::disconnected, this, &GDBConnection::gotDisconnected);
|
||||
|
||||
if (m_socket.setSocketDescriptor(m_descriptor)) {
|
||||
if (m_socket.setSocketDescriptor(m_descriptor))
|
||||
{
|
||||
g_emu_thread->pauseSystem(true, true);
|
||||
}
|
||||
else {
|
||||
Log_ErrorPrintf("(%u) Failed to set socket descriptor: %s", m_descriptor, m_socket.errorString().toUtf8().constData());
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("(%u) Failed to set socket descriptor: %s", m_descriptor,
|
||||
m_socket.errorString().toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,52 +33,60 @@ void GDBConnection::receivedData()
|
|||
qint64 bytesRead;
|
||||
char buffer[256];
|
||||
|
||||
while ((bytesRead = m_socket.read(buffer, sizeof(buffer))) > 0) {
|
||||
for (char c : std::string_view(buffer, bytesRead)) {
|
||||
while ((bytesRead = m_socket.read(buffer, sizeof(buffer))) > 0)
|
||||
{
|
||||
for (char c : std::string_view(buffer, bytesRead))
|
||||
{
|
||||
m_readBuffer.push_back(c);
|
||||
|
||||
if (GDBProtocol::IsPacketInterrupt(m_readBuffer)) {
|
||||
if (GDBProtocol::IsPacketInterrupt(m_readBuffer))
|
||||
{
|
||||
Log_DebugPrintf("(%u) > Interrupt request", m_descriptor);
|
||||
g_emu_thread->pauseSystem(true, true);
|
||||
m_readBuffer.erase();
|
||||
}
|
||||
else if (GDBProtocol::IsPacketContinue(m_readBuffer)) {
|
||||
else if (GDBProtocol::IsPacketContinue(m_readBuffer))
|
||||
{
|
||||
Log_DebugPrintf("(%u) > Continue request", m_descriptor);
|
||||
g_emu_thread->pauseSystem(false, false);
|
||||
m_readBuffer.erase();
|
||||
}
|
||||
else if (GDBProtocol::IsPacketComplete(m_readBuffer)) {
|
||||
else if (GDBProtocol::IsPacketComplete(m_readBuffer))
|
||||
{
|
||||
Log_DebugPrintf("(%u) > %s", m_descriptor, m_readBuffer.c_str());
|
||||
writePacket(GDBProtocol::ProcessPacket(m_readBuffer));
|
||||
m_readBuffer.erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bytesRead == -1) {
|
||||
if (bytesRead == -1)
|
||||
{
|
||||
Log_ErrorPrintf("(%u) Failed to read from socket: %s", m_descriptor, m_socket.errorString().toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
void GDBConnection::onEmulationPaused(bool paused)
|
||||
void GDBConnection::onEmulationPaused()
|
||||
{
|
||||
if (paused) {
|
||||
if (m_seen_resume) {
|
||||
m_seen_resume = false;
|
||||
// Generate a stop reply packet, insert '?' command to generate it.
|
||||
writePacket(GDBProtocol::ProcessPacket("$?#3f"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_seen_resume = true;
|
||||
// Send ack, in case GDB sent a continue request.
|
||||
writePacket("+");
|
||||
if (m_seen_resume)
|
||||
{
|
||||
m_seen_resume = false;
|
||||
// Generate a stop reply packet, insert '?' command to generate it.
|
||||
writePacket(GDBProtocol::ProcessPacket("$?#3f"));
|
||||
}
|
||||
}
|
||||
|
||||
void GDBConnection::onEmulationResumed()
|
||||
{
|
||||
m_seen_resume = true;
|
||||
// Send ack, in case GDB sent a continue request.
|
||||
writePacket("+");
|
||||
}
|
||||
|
||||
void GDBConnection::writePacket(std::string_view packet)
|
||||
{
|
||||
Log_DebugPrintf("(%u) < %*s", m_descriptor, packet.length(), packet.data());
|
||||
if (m_socket.write(packet.data(), packet.length()) == -1) {
|
||||
if (m_socket.write(packet.data(), packet.length()) == -1)
|
||||
{
|
||||
Log_ErrorPrintf("(%u) Failed to write to socket: %s", m_descriptor, m_socket.errorString().toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ public:
|
|||
public Q_SLOTS:
|
||||
void gotDisconnected();
|
||||
void receivedData();
|
||||
void onEmulationPaused(bool paused);
|
||||
void onEmulationPaused();
|
||||
void onEmulationResumed();
|
||||
|
||||
private:
|
||||
void writePacket(std::string_view data);
|
||||
|
|
|
@ -29,7 +29,8 @@ void GDBServer::incomingConnection(qintptr descriptor)
|
|||
{
|
||||
Log_InfoPrint("Accepted connection on GDB server");
|
||||
GDBConnection *thread = new GDBConnection(this, descriptor);
|
||||
connect(g_emu_thread, &QtHostInterface::emulationPaused, thread, &GDBConnection::onEmulationPaused);
|
||||
connect(g_emu_thread, &QtHostInterface::systemPaused, thread, &GDBConnection::onEmulationPaused);
|
||||
connect(g_emu_thread, &QtHostInterface::systemResumed, thread, &GDBConnection::onEmulationResumed);
|
||||
thread->start();
|
||||
m_connections.push_back(thread);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "controllersettingsdialog.h"
|
||||
#include "core/types.h"
|
||||
#include "qtdisplaywidget.h"
|
||||
#include "displaywidget.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
|
@ -59,8 +59,8 @@ public Q_SLOTS:
|
|||
private Q_SLOTS:
|
||||
void reportError(const QString& title, const QString& message);
|
||||
bool confirmMessage(const QString& title, const QString& message);
|
||||
QtDisplayWidget* createDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
QtDisplayWidget* updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
DisplayWidget* createDisplay(bool fullscreen, bool render_to_main);
|
||||
DisplayWidget* updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void displaySizeRequested(qint32 width, qint32 height);
|
||||
void destroyDisplay();
|
||||
void focusDisplayWidget();
|
||||
|
@ -68,10 +68,11 @@ private Q_SLOTS:
|
|||
void updateMouseMode(bool paused);
|
||||
|
||||
void onSettingsResetToDefault();
|
||||
void onEmulationStarting();
|
||||
void onEmulationStarted();
|
||||
void onEmulationStopped();
|
||||
void onEmulationPaused(bool paused);
|
||||
void onSystemStarting();
|
||||
void onSystemStarted();
|
||||
void onSystemDestroyed();
|
||||
void onSystemPaused();
|
||||
void onSystemResumed();
|
||||
void onSystemPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time,
|
||||
float worst_frame_time, GPURenderer renderer, quint32 render_width,
|
||||
quint32 render_height, bool render_interlaced);
|
||||
|
@ -124,26 +125,30 @@ protected:
|
|||
void dropEvent(QDropEvent* event) override;
|
||||
|
||||
private:
|
||||
ALWAYS_INLINE QWidget* getDisplayContainer() const
|
||||
{
|
||||
return (m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget));
|
||||
}
|
||||
|
||||
void setTheme(const QString& theme);
|
||||
void setStyleFromSettings();
|
||||
void setIconThemeFromSettings();
|
||||
void setupAdditionalUi();
|
||||
void connectSignals();
|
||||
void addThemeToMenu(const QString& name, const QString& key);
|
||||
|
||||
void updateEmulationActions(bool starting, bool running, bool cheevos_challenge_mode);
|
||||
void updateStatusBarWidgetVisibility();
|
||||
void updateWindowTitle();
|
||||
void updateWindowState(bool force_visible = false);
|
||||
|
||||
void setProgressBar(int current, int total);
|
||||
void clearProgressBar();
|
||||
|
||||
QWidget* getDisplayContainer() const;
|
||||
bool isShowingGameList() const;
|
||||
bool isRenderingFullscreen() const;
|
||||
bool isRenderingToMain() const;
|
||||
bool shouldHideMouseCursor() const;
|
||||
bool shouldHideMainWindow() const;
|
||||
|
||||
void switchToGameListView();
|
||||
void switchToEmulationView();
|
||||
void startGameOrChangeDiscs(const std::string& path);
|
||||
void saveStateToConfig();
|
||||
void restoreStateFromConfig();
|
||||
void saveDisplayWindowGeometryToConfig();
|
||||
|
@ -166,15 +171,17 @@ private:
|
|||
void setGameListEntryCoverImage(const GameList::Entry* entry);
|
||||
void recreate();
|
||||
|
||||
std::optional<bool> promptForResumeState(const QString& save_state_path);
|
||||
void startGameListEntry(const GameList::Entry* entry, std::optional<s32> save_slot, std::optional<bool> fast_boot);
|
||||
|
||||
Ui::MainWindow m_ui;
|
||||
|
||||
QString m_unthemed_style_name;
|
||||
|
||||
GameListWidget* m_game_list_widget = nullptr;
|
||||
|
||||
HostDisplay* m_host_display = nullptr;
|
||||
QtDisplayWidget* m_display_widget = nullptr;
|
||||
QtDisplayContainer* m_display_container = nullptr;
|
||||
DisplayWidget* m_display_widget = nullptr;
|
||||
DisplayContainer* m_display_container = nullptr;
|
||||
|
||||
QProgressBar* m_status_progress_widget = nullptr;
|
||||
QLabel* m_status_speed_widget = nullptr;
|
||||
|
@ -191,14 +198,20 @@ private:
|
|||
CheatManagerDialog* m_cheat_manager_dialog = nullptr;
|
||||
DebuggerWindow* m_debugger_window = nullptr;
|
||||
|
||||
std::string m_running_game_code;
|
||||
std::string m_current_game_title;
|
||||
std::string m_current_game_code;
|
||||
|
||||
bool m_emulation_running = false;
|
||||
bool m_was_paused_by_focus_loss = false;
|
||||
bool m_open_debugger_on_start = false;
|
||||
bool m_relative_mouse_mode = false;
|
||||
bool m_mouse_cursor_hidden = false;
|
||||
|
||||
bool m_display_created = false;
|
||||
bool m_save_states_invalidated = false;
|
||||
bool m_was_paused_on_surface_loss = false;
|
||||
bool m_was_disc_change_request = false;
|
||||
bool m_is_closing = false;
|
||||
|
||||
GDBServer* m_gdb_server = nullptr;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,13 +20,6 @@
|
|||
<iconset resource="resources/resources.qrc">
|
||||
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
|
||||
</property>
|
||||
<widget class="QStackedWidget" name="mainContainer">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page"/>
|
||||
<widget class="QWidget" name="page_2"/>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
|
@ -81,6 +74,7 @@
|
|||
<addaction name="actionStartFile"/>
|
||||
<addaction name="actionStartDisc"/>
|
||||
<addaction name="actionStartBios"/>
|
||||
<addaction name="actionStartFullscreenUI"/>
|
||||
<addaction name="actionResumeLastState"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPowerOff"/>
|
||||
|
@ -254,6 +248,7 @@
|
|||
</attribute>
|
||||
<addaction name="actionStartFile"/>
|
||||
<addaction name="actionStartBios"/>
|
||||
<addaction name="actionStartFullscreenUI2"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionResumeLastState"/>
|
||||
<addaction name="actionReset"/>
|
||||
|
@ -869,6 +864,24 @@
|
|||
<string>Power Off &Without Saving</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStartFullscreenUI">
|
||||
<property name="icon">
|
||||
<iconset theme="tv-2-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Big Picture Mode</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStartFullscreenUI2">
|
||||
<property name="icon">
|
||||
<iconset theme="tv-2-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Big Picture</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources/resources.qrc"/>
|
||||
|
|
|
@ -1,310 +0,0 @@
|
|||
#include "qtdisplaywidget.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/bitutils.h"
|
||||
#include "qthost.h"
|
||||
#include "qtutils.h"
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QWindowStateChangeEvent>
|
||||
#include <cmath>
|
||||
|
||||
#if !defined(_WIN32) && !defined(APPLE)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
QtDisplayWidget::QtDisplayWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
// We want a native window for both D3D and OpenGL.
|
||||
setAutoFillBackground(false);
|
||||
setAttribute(Qt::WA_NativeWindow, true);
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setAttribute(Qt::WA_PaintOnScreen, true);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
QtDisplayWidget::~QtDisplayWidget() = default;
|
||||
|
||||
qreal QtDisplayWidget::devicePixelRatioFromScreen() const
|
||||
{
|
||||
QScreen* screen_for_ratio;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
screen_for_ratio = windowHandle()->screen();
|
||||
#else
|
||||
screen_for_ratio = screen();
|
||||
#endif
|
||||
if (!screen_for_ratio)
|
||||
screen_for_ratio = QGuiApplication::primaryScreen();
|
||||
|
||||
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
|
||||
}
|
||||
|
||||
int QtDisplayWidget::scaledWindowWidth() const
|
||||
{
|
||||
return static_cast<int>(std::ceil(static_cast<qreal>(width()) * devicePixelRatioFromScreen()));
|
||||
}
|
||||
|
||||
int QtDisplayWidget::scaledWindowHeight() const
|
||||
{
|
||||
return static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen()));
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> QtDisplayWidget::getWindowInfo() const
|
||||
{
|
||||
WindowInfo wi;
|
||||
|
||||
// Windows and Apple are easy here since there's no display connection.
|
||||
#if defined(_WIN32)
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
#elif defined(__APPLE__)
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("xcb"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
}
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||||
wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle());
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Unknown PNI platform " << platform_name;
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
|
||||
wi.surface_width = scaledWindowWidth();
|
||||
wi.surface_height = scaledWindowHeight();
|
||||
wi.surface_scale = devicePixelRatioFromScreen();
|
||||
wi.surface_format = WindowInfo::SurfaceFormat::RGB8;
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
void QtDisplayWidget::setRelativeMode(bool enabled)
|
||||
{
|
||||
if (m_relative_mouse_enabled == enabled)
|
||||
return;
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
m_relative_mouse_start_position = QCursor::pos();
|
||||
|
||||
const QPoint center_pos = mapToGlobal(QPoint(width() / 2, height() / 2));
|
||||
QCursor::setPos(center_pos);
|
||||
m_relative_mouse_last_position = center_pos;
|
||||
grabMouse();
|
||||
}
|
||||
else
|
||||
{
|
||||
QCursor::setPos(m_relative_mouse_start_position);
|
||||
releaseMouse();
|
||||
}
|
||||
|
||||
m_relative_mouse_enabled = enabled;
|
||||
}
|
||||
|
||||
QPaintEngine* QtDisplayWidget::paintEngine() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool QtDisplayWidget::event(QEvent* event)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
{
|
||||
const QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
|
||||
if (!key_event->isAutoRepeat())
|
||||
{
|
||||
emit windowKeyEvent(key_event->key(), event->type() == QEvent::KeyPress);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
const QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||||
|
||||
if (!m_relative_mouse_enabled)
|
||||
{
|
||||
const qreal dpr = devicePixelRatioFromScreen();
|
||||
const QPoint mouse_pos = mouse_event->pos();
|
||||
const int scaled_x = static_cast<int>(static_cast<qreal>(mouse_pos.x()) * dpr);
|
||||
const int scaled_y = static_cast<int>(static_cast<qreal>(mouse_pos.y()) * dpr);
|
||||
|
||||
windowMouseMoveEvent(scaled_x, scaled_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
const QPoint center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
|
||||
const QPoint mouse_pos = mapToGlobal(mouse_event->pos());
|
||||
|
||||
const int dx = mouse_pos.x() - center_pos.x();
|
||||
const int dy = mouse_pos.y() - center_pos.y();
|
||||
m_relative_mouse_last_position.setX(m_relative_mouse_last_position.x() + dx);
|
||||
m_relative_mouse_last_position.setY(m_relative_mouse_last_position.y() + dy);
|
||||
windowMouseMoveEvent(m_relative_mouse_last_position.x(), m_relative_mouse_last_position.y());
|
||||
QCursor::setPos(center_pos);
|
||||
|
||||
#if 0
|
||||
qCritical() << "center" << center_pos.x() << "," << center_pos.y();
|
||||
qCritical() << "mouse" << mouse_pos.x() << "," << mouse_pos.y();
|
||||
qCritical() << "dxdy" << dx << "," << dy;
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::MouseButtonRelease:
|
||||
{
|
||||
const u32 button_index = CountTrailingZeros(static_cast<u32>(static_cast<const QMouseEvent*>(event)->button()));
|
||||
emit windowMouseButtonEvent(static_cast<int>(button_index + 1u), event->type() != QEvent::MouseButtonRelease);
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Wheel:
|
||||
{
|
||||
const QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
|
||||
emit windowMouseWheelEvent(wheel_event->angleDelta());
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Resize:
|
||||
{
|
||||
QWidget::event(event);
|
||||
|
||||
emit windowResizedEvent(scaledWindowWidth(), scaledWindowHeight());
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Close:
|
||||
{
|
||||
emit windowClosedEvent();
|
||||
QWidget::event(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::WindowStateChange:
|
||||
{
|
||||
QWidget::event(event);
|
||||
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit windowRestoredEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::FocusIn:
|
||||
{
|
||||
QWidget::event(event);
|
||||
emit windowFocusEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::ActivationChange:
|
||||
{
|
||||
QWidget::event(event);
|
||||
if (isActiveWindow())
|
||||
emit windowFocusEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return QWidget::event(event);
|
||||
}
|
||||
}
|
||||
|
||||
QtDisplayContainer::QtDisplayContainer() : QStackedWidget(nullptr) {}
|
||||
|
||||
QtDisplayContainer::~QtDisplayContainer() = default;
|
||||
|
||||
bool QtDisplayContainer::IsNeeded(bool fullscreen, bool render_to_main)
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
return false;
|
||||
#else
|
||||
if (fullscreen || render_to_main)
|
||||
return false;
|
||||
|
||||
// We only need this on Wayland because of client-side decorations...
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
return (platform_name == QStringLiteral("wayland"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void QtDisplayContainer::setDisplayWidget(QtDisplayWidget* widget)
|
||||
{
|
||||
Assert(!m_display_widget);
|
||||
m_display_widget = widget;
|
||||
addWidget(widget);
|
||||
}
|
||||
|
||||
QtDisplayWidget* QtDisplayContainer::removeDisplayWidget()
|
||||
{
|
||||
QtDisplayWidget* widget = m_display_widget;
|
||||
Assert(widget);
|
||||
m_display_widget = nullptr;
|
||||
removeWidget(widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
bool QtDisplayContainer::event(QEvent* event)
|
||||
{
|
||||
const bool res = QStackedWidget::event(event);
|
||||
if (!m_display_widget)
|
||||
return res;
|
||||
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::Close:
|
||||
{
|
||||
emit m_display_widget->windowClosedEvent();
|
||||
}
|
||||
break;
|
||||
|
||||
case QEvent::WindowStateChange:
|
||||
{
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit m_display_widget->windowRestoredEvent();
|
||||
}
|
||||
break;
|
||||
|
||||
case QEvent::FocusIn:
|
||||
{
|
||||
emit m_display_widget->windowFocusEvent();
|
||||
}
|
||||
break;
|
||||
|
||||
case QEvent::ActivationChange:
|
||||
{
|
||||
if (isActiveWindow())
|
||||
emit m_display_widget->windowFocusEvent();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
#include "common/types.h"
|
||||
#include "common/window_info.h"
|
||||
#include <QtWidgets/QStackedWidget>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <optional>
|
||||
|
||||
class QtDisplayWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtDisplayWidget(QWidget* parent);
|
||||
~QtDisplayWidget();
|
||||
|
||||
QPaintEngine* paintEngine() const override;
|
||||
|
||||
int scaledWindowWidth() const;
|
||||
int scaledWindowHeight() const;
|
||||
qreal devicePixelRatioFromScreen() const;
|
||||
|
||||
std::optional<WindowInfo> getWindowInfo() const;
|
||||
|
||||
void setRelativeMode(bool enabled);
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowFocusEvent();
|
||||
void windowResizedEvent(int width, int height);
|
||||
void windowRestoredEvent();
|
||||
void windowClosedEvent();
|
||||
void windowKeyEvent(int key_code, bool pressed);
|
||||
void windowMouseMoveEvent(float x, float y);
|
||||
void windowMouseButtonEvent(int button, bool pressed);
|
||||
void windowMouseWheelEvent(const QPoint& angle_delta);
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
QPoint m_relative_mouse_start_position{};
|
||||
QPoint m_relative_mouse_last_position{};
|
||||
bool m_relative_mouse_enabled = false;
|
||||
};
|
||||
|
||||
class QtDisplayContainer final : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtDisplayContainer();
|
||||
~QtDisplayContainer();
|
||||
|
||||
static bool IsNeeded(bool fullscreen, bool render_to_main);
|
||||
|
||||
void setDisplayWidget(QtDisplayWidget* widget);
|
||||
QtDisplayWidget* removeDisplayWidget();
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
QtDisplayWidget* m_display_widget = nullptr;
|
||||
};
|
|
@ -14,6 +14,7 @@
|
|||
#include "core/memory_card.h"
|
||||
#include "core/spu.h"
|
||||
#include "core/system.h"
|
||||
#include "displaywidget.h"
|
||||
#include "frontend-common/fullscreen_ui.h"
|
||||
#include "frontend-common/game_list.h"
|
||||
#include "frontend-common/imgui_manager.h"
|
||||
|
@ -23,9 +24,9 @@
|
|||
#include "frontend-common/vulkan_host_display.h"
|
||||
#include "imgui.h"
|
||||
#include "mainwindow.h"
|
||||
#include "qtdisplaywidget.h"
|
||||
#include "qtprogresscallback.h"
|
||||
#include "qtutils.h"
|
||||
#include "scmversion/scmversion.h"
|
||||
#include "util/audio_stream.h"
|
||||
#include "util/ini_settings_interface.h"
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
@ -146,6 +147,22 @@ bool QtHost::InNoGUIMode()
|
|||
return s_nogui_mode;
|
||||
}
|
||||
|
||||
QString QtHost::GetAppNameAndVersion()
|
||||
{
|
||||
return QStringLiteral("DuckStation %1 (%2)").arg(g_scm_tag_str).arg(g_scm_branch_str);
|
||||
}
|
||||
|
||||
QString QtHost::GetAppConfigSuffix()
|
||||
{
|
||||
#if defined(_DEBUG)
|
||||
return QStringLiteral(" [Debug]");
|
||||
#elif defined(_DEBUGFAST)
|
||||
return QStringLiteral(" [DebugFast]");
|
||||
#else
|
||||
return QString();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QtHost::InitializeConfig()
|
||||
{
|
||||
if (!SetCriticalFolders())
|
||||
|
@ -459,7 +476,6 @@ void QtHostInterface::bootSystem(std::shared_ptr<SystemBootParameters> params)
|
|||
return;
|
||||
}
|
||||
|
||||
emit emulationStarting();
|
||||
if (!System::BootSystem(std::move(params)))
|
||||
return;
|
||||
|
||||
|
@ -476,11 +492,14 @@ void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_o
|
|||
return;
|
||||
}
|
||||
|
||||
emit emulationStarting();
|
||||
Panic("fixme");
|
||||
|
||||
#if 0
|
||||
if (filename.isEmpty())
|
||||
System::ResumeSystemFromMostRecentState();
|
||||
else
|
||||
System::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure);
|
||||
#endif
|
||||
}
|
||||
|
||||
void QtHostInterface::resumeSystemFromMostRecentState()
|
||||
|
@ -503,14 +522,24 @@ void QtHostInterface::onDisplayWindowKeyEvent(int key, bool pressed)
|
|||
GenericInputBinding::Unknown);
|
||||
}
|
||||
|
||||
void QtHostInterface::onDisplayWindowMouseMoveEvent(float x, float y)
|
||||
void QtHostInterface::onDisplayWindowMouseMoveEvent(bool relative, float x, float y)
|
||||
{
|
||||
// display might be null here if the event happened after shutdown
|
||||
DebugAssert(isOnWorkerThread());
|
||||
if (s_host_display)
|
||||
s_host_display->SetMousePosition(static_cast<s32>(x), static_cast<s32>(y));
|
||||
if (!relative)
|
||||
{
|
||||
if (s_host_display)
|
||||
s_host_display->SetMousePosition(static_cast<s32>(x), static_cast<s32>(y));
|
||||
|
||||
InputManager::UpdatePointerAbsolutePosition(0, x, y);
|
||||
InputManager::UpdatePointerAbsolutePosition(0, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x != 0.0f)
|
||||
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::X, x);
|
||||
if (y != 0.0f)
|
||||
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
void QtHostInterface::onDisplayWindowMouseButtonEvent(int button, bool pressed)
|
||||
|
@ -563,18 +592,6 @@ void QtHostInterface::onDisplayWindowResized(int width, int height)
|
|||
}
|
||||
}
|
||||
|
||||
void QtHostInterface::onDisplayWindowFocused()
|
||||
{
|
||||
if (!s_host_display || !m_lost_exclusive_fullscreen)
|
||||
return;
|
||||
|
||||
// try to restore exclusive fullscreen
|
||||
m_lost_exclusive_fullscreen = false;
|
||||
m_is_exclusive_fullscreen = true;
|
||||
m_is_fullscreen = true;
|
||||
updateDisplayState();
|
||||
}
|
||||
|
||||
void QtHostInterface::redrawDisplayWindow()
|
||||
{
|
||||
if (!isOnWorkerThread())
|
||||
|
@ -597,42 +614,43 @@ void QtHostInterface::toggleFullscreen()
|
|||
return;
|
||||
}
|
||||
|
||||
SetFullscreen(!m_is_fullscreen);
|
||||
setFullscreen(!m_is_fullscreen);
|
||||
}
|
||||
|
||||
HostDisplay* QtHostInterface::acquireHostDisplay()
|
||||
void QtHostInterface::setFullscreen(bool fullscreen)
|
||||
{
|
||||
if (!isOnWorkerThread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_host_display || m_is_fullscreen == fullscreen)
|
||||
return;
|
||||
|
||||
m_is_fullscreen = fullscreen;
|
||||
updateDisplayRequested(fullscreen, m_is_rendering_to_main, m_is_surfaceless);
|
||||
}
|
||||
|
||||
void QtHostInterface::setSurfaceless(bool surfaceless)
|
||||
{
|
||||
if (!isOnWorkerThread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "setSurfaceless", Qt::QueuedConnection, Q_ARG(bool, surfaceless));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_host_display || m_is_surfaceless == surfaceless)
|
||||
return;
|
||||
|
||||
m_is_surfaceless = surfaceless;
|
||||
updateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main, m_is_surfaceless);
|
||||
}
|
||||
|
||||
static void createHostDisplay()
|
||||
{
|
||||
Assert(!s_host_display);
|
||||
|
||||
m_is_rendering_to_main = Host::GetBaseBoolSettingValue("Main", "RenderToMainWindow", true);
|
||||
|
||||
QtDisplayWidget* display_widget = createDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main);
|
||||
if (!display_widget || !s_host_display->HasRenderDevice())
|
||||
{
|
||||
emit destroyDisplayRequested();
|
||||
s_host_display.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!s_host_display->MakeRenderContextCurrent() ||
|
||||
!s_host_display->InitializeRenderDevice(EmuFolders::Cache, g_settings.gpu_use_debug_device,
|
||||
g_settings.gpu_threaded_presentation) ||
|
||||
!ImGuiManager::Initialize() || !CommonHost::CreateHostDisplayResources())
|
||||
{
|
||||
ImGuiManager::Shutdown();
|
||||
CommonHost::ReleaseHostDisplayResources();
|
||||
s_host_display->DestroyRenderDevice();
|
||||
emit destroyDisplayRequested();
|
||||
s_host_display.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_is_exclusive_fullscreen = s_host_display->IsFullscreen();
|
||||
return s_host_display.get();
|
||||
}
|
||||
|
||||
HostDisplay* QtHostInterface::createHostDisplay()
|
||||
{
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
case GPURenderer::HardwareVulkan:
|
||||
|
@ -657,23 +675,49 @@ HostDisplay* QtHostInterface::createHostDisplay()
|
|||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
HostDisplay* QtHostInterface::acquireHostDisplay()
|
||||
{
|
||||
createHostDisplay();
|
||||
|
||||
m_is_rendering_to_main = Host::GetBaseBoolSettingValue("Main", "RenderToMainWindow", true);
|
||||
|
||||
DisplayWidget* display_widget = createDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
|
||||
if (!display_widget || !s_host_display->HasRenderDevice())
|
||||
{
|
||||
emit destroyDisplayRequested();
|
||||
s_host_display.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!s_host_display->MakeRenderContextCurrent() ||
|
||||
!s_host_display->InitializeRenderDevice(EmuFolders::Cache, g_settings.gpu_use_debug_device,
|
||||
g_settings.gpu_threaded_presentation) ||
|
||||
!ImGuiManager::Initialize() || !CommonHost::CreateHostDisplayResources())
|
||||
{
|
||||
ImGuiManager::Shutdown();
|
||||
CommonHost::ReleaseHostDisplayResources();
|
||||
s_host_display->DestroyRenderDevice();
|
||||
emit destroyDisplayRequested();
|
||||
s_host_display.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_is_exclusive_fullscreen = s_host_display->IsFullscreen();
|
||||
return s_host_display.get();
|
||||
}
|
||||
|
||||
void QtHostInterface::connectDisplaySignals(QtDisplayWidget* widget)
|
||||
void QtHostInterface::connectDisplaySignals(DisplayWidget* widget)
|
||||
{
|
||||
widget->disconnect(this);
|
||||
|
||||
connect(widget, &QtDisplayWidget::windowFocusEvent, this, &QtHostInterface::onDisplayWindowFocused);
|
||||
connect(widget, &QtDisplayWidget::windowResizedEvent, this, &QtHostInterface::onDisplayWindowResized);
|
||||
connect(widget, &QtDisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow);
|
||||
connect(widget, &QtDisplayWidget::windowClosedEvent, this, &QtHostInterface::powerOffSystem,
|
||||
Qt::BlockingQueuedConnection);
|
||||
connect(widget, &QtDisplayWidget::windowKeyEvent, this, &QtHostInterface::onDisplayWindowKeyEvent);
|
||||
connect(widget, &QtDisplayWidget::windowMouseMoveEvent, this, &QtHostInterface::onDisplayWindowMouseMoveEvent);
|
||||
connect(widget, &QtDisplayWidget::windowMouseButtonEvent, this, &QtHostInterface::onDisplayWindowMouseButtonEvent);
|
||||
connect(widget, &QtDisplayWidget::windowMouseWheelEvent, this, &QtHostInterface::onDisplayWindowMouseWheelEvent);
|
||||
connect(widget, &DisplayWidget::windowResizedEvent, this, &QtHostInterface::onDisplayWindowResized);
|
||||
connect(widget, &DisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow);
|
||||
connect(widget, &DisplayWidget::windowKeyEvent, this, &QtHostInterface::onDisplayWindowKeyEvent);
|
||||
connect(widget, &DisplayWidget::windowMouseMoveEvent, this, &QtHostInterface::onDisplayWindowMouseMoveEvent);
|
||||
connect(widget, &DisplayWidget::windowMouseButtonEvent, this, &QtHostInterface::onDisplayWindowMouseButtonEvent);
|
||||
connect(widget, &DisplayWidget::windowMouseWheelEvent, this, &QtHostInterface::onDisplayWindowMouseWheelEvent);
|
||||
}
|
||||
|
||||
void QtHostInterface::updateDisplayState()
|
||||
|
@ -684,7 +728,7 @@ void QtHostInterface::updateDisplayState()
|
|||
// this expects the context to get moved back to us afterwards
|
||||
s_host_display->DoneRenderContextCurrent();
|
||||
|
||||
QtDisplayWidget* display_widget =
|
||||
DisplayWidget* display_widget =
|
||||
updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main && !m_is_fullscreen);
|
||||
if (!display_widget || !s_host_display->MakeRenderContextCurrent())
|
||||
Panic("Failed to make device context current after updating");
|
||||
|
@ -736,35 +780,48 @@ void QtHostInterface::RequestExit()
|
|||
emit exitRequested();
|
||||
}
|
||||
|
||||
void QtHostInterface::onSystemStarted()
|
||||
void Host::OnSystemStarting()
|
||||
{
|
||||
wakeThread();
|
||||
stopBackgroundControllerPollTimer();
|
||||
CommonHost::OnSystemStarting();
|
||||
|
||||
emit emulationStarted();
|
||||
emit emulationPaused(false);
|
||||
emit g_emu_thread->systemStarting();
|
||||
}
|
||||
|
||||
void QtHostInterface::onSystemPaused()
|
||||
void Host::OnSystemStarted()
|
||||
{
|
||||
emit emulationPaused(true);
|
||||
startBackgroundControllerPollTimer();
|
||||
renderDisplay();
|
||||
CommonHost::OnSystemStarted();
|
||||
|
||||
g_emu_thread->wakeThread();
|
||||
g_emu_thread->stopBackgroundControllerPollTimer();
|
||||
|
||||
emit g_emu_thread->systemStarted();
|
||||
}
|
||||
|
||||
void QtHostInterface::onSystemResumed()
|
||||
void Host::OnSystemPaused()
|
||||
{
|
||||
emit emulationPaused(false);
|
||||
CommonHost::OnSystemPaused();
|
||||
|
||||
wakeThread();
|
||||
stopBackgroundControllerPollTimer();
|
||||
emit focusDisplayWidgetRequested();
|
||||
emit g_emu_thread->systemPaused();
|
||||
g_emu_thread->startBackgroundControllerPollTimer();
|
||||
g_emu_thread->renderDisplay();
|
||||
}
|
||||
|
||||
void QtHostInterface::onSystemDestroyed()
|
||||
void Host::OnSystemResumed()
|
||||
{
|
||||
startBackgroundControllerPollTimer();
|
||||
emit emulationStopped();
|
||||
CommonHost::OnSystemResumed();
|
||||
|
||||
emit g_emu_thread->systemResumed();
|
||||
|
||||
g_emu_thread->wakeThread();
|
||||
g_emu_thread->stopBackgroundControllerPollTimer();
|
||||
}
|
||||
|
||||
void Host::OnSystemDestroyed()
|
||||
{
|
||||
CommonHost::OnSystemDestroyed();
|
||||
|
||||
g_emu_thread->startBackgroundControllerPollTimer();
|
||||
emit g_emu_thread->systemDestroyed();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -1323,9 +1380,6 @@ void QtHostInterface::loadState(const QString& filename)
|
|||
return;
|
||||
}
|
||||
|
||||
if (System::IsShutdown())
|
||||
emit emulationStarting();
|
||||
|
||||
System::LoadState(filename.toStdString().c_str());
|
||||
}
|
||||
|
||||
|
@ -1910,36 +1964,6 @@ void Host::CheckForSettingsChanges(const Settings& old_settings)
|
|||
// TODO, e.g. render to main
|
||||
}
|
||||
|
||||
void Host::OnSystemStarting()
|
||||
{
|
||||
CommonHost::OnSystemStarting();
|
||||
// g_emu_thread->onSystemStarting();
|
||||
}
|
||||
|
||||
void Host::OnSystemStarted()
|
||||
{
|
||||
CommonHost::OnSystemStarted();
|
||||
g_emu_thread->onSystemStarted();
|
||||
}
|
||||
|
||||
void Host::OnSystemPaused()
|
||||
{
|
||||
CommonHost::OnSystemPaused();
|
||||
g_emu_thread->onSystemPaused();
|
||||
}
|
||||
|
||||
void Host::OnSystemResumed()
|
||||
{
|
||||
CommonHost::OnSystemResumed();
|
||||
g_emu_thread->onSystemResumed();
|
||||
}
|
||||
|
||||
void Host::OnSystemDestroyed()
|
||||
{
|
||||
CommonHost::OnSystemDestroyed();
|
||||
g_emu_thread->onSystemDestroyed();
|
||||
}
|
||||
|
||||
void Host::OnPerformanceMetricsUpdated()
|
||||
{
|
||||
GPURenderer renderer = GPURenderer::Count;
|
||||
|
|
|
@ -34,7 +34,7 @@ class INISettingsInterface;
|
|||
class HostDisplay;
|
||||
|
||||
class MainWindow;
|
||||
class QtDisplayWidget;
|
||||
class DisplayWidget;
|
||||
|
||||
Q_DECLARE_METATYPE(std::shared_ptr<SystemBootParameters>);
|
||||
Q_DECLARE_METATYPE(GPURenderer);
|
||||
|
@ -60,6 +60,11 @@ public:
|
|||
|
||||
ALWAYS_INLINE QEventLoop* getEventLoop() const { return m_worker_thread_event_loop; }
|
||||
|
||||
ALWAYS_INLINE bool isFullscreen() const { return m_is_fullscreen; }
|
||||
ALWAYS_INLINE bool isRenderingToMain() const { return m_is_rendering_to_main; }
|
||||
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
|
||||
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
|
||||
|
||||
void reinstallTranslator();
|
||||
|
||||
void populateLoadStateMenu(const char* game_code, QMenu* menu);
|
||||
|
@ -80,17 +85,14 @@ public:
|
|||
static std::vector<std::pair<QString, QString>> getAvailableLanguageList();
|
||||
|
||||
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
|
||||
static HostDisplay* createHostDisplay();
|
||||
HostDisplay* acquireHostDisplay();
|
||||
void connectDisplaySignals(QtDisplayWidget* widget);
|
||||
void connectDisplaySignals(DisplayWidget* widget);
|
||||
void releaseHostDisplay();
|
||||
void updateDisplay();
|
||||
void renderDisplay();
|
||||
|
||||
void onSystemStarted();
|
||||
void onSystemPaused();
|
||||
void onSystemResumed();
|
||||
void onSystemDestroyed();
|
||||
void startBackgroundControllerPollTimer();
|
||||
void stopBackgroundControllerPollTimer();
|
||||
void wakeThread();
|
||||
|
||||
Q_SIGNALS:
|
||||
void errorReported(const QString& title, const QString& message);
|
||||
|
@ -101,13 +103,14 @@ Q_SIGNALS:
|
|||
void onInputDeviceConnected(const QString& identifier, const QString& device_name);
|
||||
void onInputDeviceDisconnected(const QString& identifier);
|
||||
void onVibrationMotorsEnumerated(const QList<InputBindingKey>& motors);
|
||||
void emulationStarting();
|
||||
void emulationStarted();
|
||||
void emulationStopped();
|
||||
void emulationPaused(bool paused);
|
||||
void systemStarting();
|
||||
void systemStarted();
|
||||
void systemDestroyed();
|
||||
void systemPaused();
|
||||
void systemResumed();
|
||||
void gameListRefreshed();
|
||||
QtDisplayWidget* createDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
QtDisplayWidget* updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
DisplayWidget* createDisplayRequested(bool fullscreen, bool render_to_main);
|
||||
DisplayWidget* updateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void displaySizeRequested(qint32 width, qint32 height);
|
||||
void focusDisplayWidgetRequested();
|
||||
void destroyDisplayRequested();
|
||||
|
@ -156,6 +159,8 @@ public Q_SLOTS:
|
|||
void saveScreenshot();
|
||||
void redrawDisplayWindow();
|
||||
void toggleFullscreen();
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setSurfaceless(bool surfaceless);
|
||||
void loadCheatList(const QString& filename);
|
||||
void setCheatEnabled(quint32 index, bool enabled);
|
||||
void applyCheat(quint32 index);
|
||||
|
@ -165,11 +170,10 @@ public Q_SLOTS:
|
|||
|
||||
private Q_SLOTS:
|
||||
void doStopThread();
|
||||
void onDisplayWindowMouseMoveEvent(float x, float y);
|
||||
void onDisplayWindowMouseMoveEvent(bool relative, float x, float y);
|
||||
void onDisplayWindowMouseButtonEvent(int button, bool pressed);
|
||||
void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle);
|
||||
void onDisplayWindowResized(int width, int height);
|
||||
void onDisplayWindowFocused();
|
||||
void onDisplayWindowKeyEvent(int key, bool pressed);
|
||||
void doBackgroundControllerPoll();
|
||||
|
||||
|
@ -209,8 +213,6 @@ private:
|
|||
|
||||
void createBackgroundControllerPollTimer();
|
||||
void destroyBackgroundControllerPollTimer();
|
||||
void startBackgroundControllerPollTimer();
|
||||
void stopBackgroundControllerPollTimer();
|
||||
|
||||
void setImGuiFont();
|
||||
|
||||
|
@ -223,7 +225,6 @@ private:
|
|||
void checkRenderToMainState();
|
||||
void updateDisplayState();
|
||||
void queueSettingsSave();
|
||||
void wakeThread();
|
||||
|
||||
QThread* m_original_thread = nullptr;
|
||||
Thread* m_worker_thread = nullptr;
|
||||
|
@ -235,10 +236,16 @@ private:
|
|||
QTimer* m_background_controller_polling_timer = nullptr;
|
||||
std::vector<QTranslator*> m_translators;
|
||||
|
||||
bool m_run_fullscreen_ui = false;
|
||||
bool m_is_rendering_to_main = false;
|
||||
bool m_is_fullscreen = false;
|
||||
bool m_is_exclusive_fullscreen = false;
|
||||
bool m_lost_exclusive_fullscreen = false;
|
||||
bool m_is_surfaceless = false;
|
||||
bool m_save_state_on_shutdown = false;
|
||||
bool m_pause_on_focus_loss = false;
|
||||
|
||||
bool m_was_paused_by_focus_loss = false;
|
||||
};
|
||||
|
||||
extern QtHostInterface* g_emu_thread;
|
||||
|
@ -254,14 +261,18 @@ bool InNoGUIMode();
|
|||
void RunOnUIThread(const std::function<void()>& func, bool block = false);
|
||||
|
||||
/// Returns the application name and version, optionally including debug/devel config indicator.
|
||||
// QString GetAppNameAndVersion();
|
||||
QString GetAppNameAndVersion();
|
||||
|
||||
/// Returns the debug/devel config indicator.
|
||||
// QString GetAppConfigSuffix();
|
||||
QString GetAppConfigSuffix();
|
||||
|
||||
/// Returns the base path for resources. This may be : prefixed, if we're using embedded resources.
|
||||
// QString GetResourcesBasePath();
|
||||
QString GetResourcesBasePath();
|
||||
|
||||
/// Thread-safe settings access.
|
||||
void QueueSettingsSave();
|
||||
|
||||
/// VM state, safe to access on UI thread.
|
||||
bool IsSystemValid();
|
||||
bool IsSystemPaused();
|
||||
} // namespace QtHost
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
#include <QtWidgets/QStatusBar>
|
||||
#include <QtWidgets/QStyle>
|
||||
#include <QtWidgets/QTableView>
|
||||
#include <QtWidgets/QTreeView>
|
||||
|
@ -772,4 +773,54 @@ std::optional<unsigned> PromptForAddress(QWidget* parent, const QString& title,
|
|||
return address;
|
||||
}
|
||||
|
||||
QString StringViewToQString(const std::string_view& str)
|
||||
{
|
||||
return str.empty() ? QString() : QString::fromUtf8(str.data(), str.size());
|
||||
}
|
||||
|
||||
void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited)
|
||||
{
|
||||
if (widget->font().italic() != inherited)
|
||||
{
|
||||
QFont new_font(widget->font());
|
||||
new_font.setItalic(inherited);
|
||||
widget->setFont(new_font);
|
||||
}
|
||||
}
|
||||
|
||||
void SetWindowResizeable(QWidget* widget, bool resizeable)
|
||||
{
|
||||
if (QMainWindow* window = qobject_cast<QMainWindow*>(widget); window)
|
||||
{
|
||||
// update status bar grip if present
|
||||
if (QStatusBar* sb = window->statusBar(); sb)
|
||||
sb->setSizeGripEnabled(resizeable);
|
||||
}
|
||||
|
||||
if ((widget->sizePolicy().horizontalPolicy() == QSizePolicy::Preferred) != resizeable)
|
||||
{
|
||||
if (resizeable)
|
||||
{
|
||||
// Min/max numbers come from uic.
|
||||
widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
widget->setMinimumSize(1, 1);
|
||||
widget->setMaximumSize(16777215, 16777215);
|
||||
}
|
||||
else
|
||||
{
|
||||
widget->setFixedSize(widget->size());
|
||||
widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height)
|
||||
{
|
||||
width = std::max(width, 1);
|
||||
height = std::max(height, 1);
|
||||
if (widget->sizePolicy().horizontalPolicy() == QSizePolicy::Fixed)
|
||||
widget->setFixedSize(width, height);
|
||||
|
||||
widget->resize(width, height);
|
||||
}
|
||||
} // namespace QtUtils
|
||||
|
|
|
@ -67,4 +67,16 @@ void FillComboBoxWithEmulationSpeeds(QComboBox* cb);
|
|||
/// Prompts for an address in hex.
|
||||
std::optional<unsigned> PromptForAddress(QWidget* parent, const QString& title, const QString& label, bool code);
|
||||
|
||||
/// Converts a std::string_view to a QString safely.
|
||||
QString StringViewToQString(const std::string_view& str);
|
||||
|
||||
/// Sets a widget to italics if the setting value is inherited.
|
||||
void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited);
|
||||
|
||||
/// Changes whether a window is resizable.
|
||||
void SetWindowResizeable(QWidget* widget, bool resizeable);
|
||||
|
||||
/// Adjusts the fixed size for a window if it's not resizeable.
|
||||
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height);
|
||||
|
||||
} // namespace QtUtils
|
|
@ -364,20 +364,7 @@ bool CommonHost::ParseCommandLineParameters(int argc, char* argv[],
|
|||
boot_params->filename = std::move(boot_filename);
|
||||
boot_params->override_fast_boot = std::move(force_fast_boot);
|
||||
boot_params->override_fullscreen = std::move(force_fullscreen);
|
||||
|
||||
if (!state_filename.empty())
|
||||
{
|
||||
std::unique_ptr<ByteStream> state_stream =
|
||||
ByteStream::OpenFile(state_filename.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||
if (!state_stream)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open save state file '%s'", state_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
boot_params->state_stream = std::move(state_stream);
|
||||
}
|
||||
|
||||
boot_params->save_state = std::move(state_filename);
|
||||
*out_boot_params = std::move(boot_params);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue