mirror of
https://github.com/obhq/obliteration.git
synced 2025-02-17 09:59:23 +00:00
Initializes GUI for VMM (#890)
This commit is contained in:
parent
98981ef1f9
commit
b93fde21d0
@ -36,6 +36,7 @@ add_executable(obliteration WIN32 MACOSX_BUNDLE
|
||||
game_settings.cpp
|
||||
game_settings_dialog.cpp
|
||||
initialize_wizard.cpp
|
||||
launch_settings.cpp
|
||||
log_formatter.cpp
|
||||
main.cpp
|
||||
main_window.cpp
|
||||
|
27
src/core.hpp
27
src/core.hpp
@ -9,6 +9,7 @@
|
||||
struct error;
|
||||
struct param;
|
||||
struct pkg;
|
||||
struct vmm;
|
||||
|
||||
typedef void (*pkg_extract_status_t) (const char *status, std::size_t bar, std::uint64_t current, std::uint64_t total, void *ud);
|
||||
|
||||
@ -39,6 +40,9 @@ extern "C" {
|
||||
bool explicit_decryption,
|
||||
void (*status) (const char *, std::uint64_t, std::uint64_t, void *),
|
||||
void *ud);
|
||||
|
||||
vmm *vmm_new();
|
||||
void vmm_free(vmm *vmm);
|
||||
}
|
||||
|
||||
class Error final {
|
||||
@ -209,3 +213,26 @@ public:
|
||||
private:
|
||||
pkg *m_obj;
|
||||
};
|
||||
|
||||
class Vmm final {
|
||||
public:
|
||||
Vmm() : m_obj(nullptr) {}
|
||||
Vmm(const Vmm &) = delete;
|
||||
~Vmm() { kill(); }
|
||||
|
||||
public:
|
||||
Vmm &operator=(const Vmm &) = delete;
|
||||
operator bool() const { return m_obj != nullptr; }
|
||||
|
||||
public:
|
||||
void kill()
|
||||
{
|
||||
if (m_obj) {
|
||||
vmm_free(m_obj);
|
||||
m_obj = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
vmm *m_obj;
|
||||
};
|
||||
|
@ -4,3 +4,4 @@ mod ffi;
|
||||
mod fwdl;
|
||||
mod param;
|
||||
mod pkg;
|
||||
mod vmm;
|
||||
|
14
src/core/src/vmm/mod.rs
Normal file
14
src/core/src/vmm/mod.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#[no_mangle]
|
||||
pub extern "C-unwind" fn vmm_new() -> *mut Vmm {
|
||||
let vmm = Vmm {};
|
||||
|
||||
Box::into_raw(vmm.into())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C-unwind" fn vmm_free(vmm: *mut Vmm) {
|
||||
drop(Box::from_raw(vmm));
|
||||
}
|
||||
|
||||
/// Manage a virtual machine that run the kernel.
|
||||
pub struct Vmm {}
|
@ -1,5 +1,6 @@
|
||||
#include "game_models.hpp"
|
||||
#include "path.hpp"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
Game::Game(const QString &id, const QString &name, const QString &directory) :
|
||||
@ -7,38 +8,21 @@ Game::Game(const QString &id, const QString &name, const QString &directory) :
|
||||
m_name(name),
|
||||
m_directory(directory)
|
||||
{
|
||||
// Load icon.
|
||||
auto dir = joinPath(directory, "sce_sys");
|
||||
auto path = joinPath(dir.c_str(), "icon0.png");
|
||||
QPixmap icon(QFile::exists(path.c_str()) ? path.c_str() : ":/resources/fallbackicon0.png");
|
||||
|
||||
icon.setDevicePixelRatio(2.0);
|
||||
|
||||
// Scale down.
|
||||
m_icon = icon.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
}
|
||||
|
||||
QPixmap Game::icon() const {
|
||||
if (!m_cachedIcon.isNull()) {
|
||||
return m_cachedIcon;
|
||||
}
|
||||
|
||||
// Get icon path.
|
||||
auto dir = joinPath(m_directory, "sce_sys");
|
||||
auto path = joinPath(dir.c_str(), "icon0.png");
|
||||
|
||||
if (QFile::exists(path.c_str())) {
|
||||
m_cachedIcon.load(path.c_str());
|
||||
} else {
|
||||
// Load fallback icon if icon0 doesn't exist.
|
||||
m_cachedIcon.load(":/resources/fallbackicon0.png");
|
||||
}
|
||||
|
||||
// For games with large icon sizes.
|
||||
if (m_cachedIcon.width() != 512 || m_cachedIcon.height() != 512) {
|
||||
m_cachedIcon = m_cachedIcon.scaled(512, 512, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
m_cachedIcon.setDevicePixelRatio(2.0);
|
||||
|
||||
return m_cachedIcon;
|
||||
}
|
||||
|
||||
GameListModel::GameListModel(QObject *parent) :
|
||||
QAbstractListModel(parent)
|
||||
{
|
||||
@ -72,38 +56,83 @@ void GameListModel::clear()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int GameListModel::columnCount(const QModelIndex &) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int GameListModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole) {
|
||||
return {};
|
||||
} else if (orientation == Qt::Vertical) {
|
||||
return section + 1;
|
||||
} else if (orientation != Qt::Horizontal) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (section) {
|
||||
case 0:
|
||||
return "Name";
|
||||
case 1:
|
||||
return "ID";
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariant GameListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return m_items[index.row()]->name();
|
||||
case Qt::DecorationRole:
|
||||
return m_items[index.row()]->icon();
|
||||
default:
|
||||
return QVariant();
|
||||
auto game = m_items[index.row()];
|
||||
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return game->name();
|
||||
case Qt::DecorationRole:
|
||||
return game->icon();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (role == Qt::DisplayRole) {
|
||||
return game->id();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void GameListModel::sort(int column, Qt::SortOrder order)
|
||||
{
|
||||
if (column != 0)
|
||||
return;
|
||||
|
||||
emit layoutAboutToBeChanged();
|
||||
|
||||
auto compare = [order](const Game* a, const Game* b) {
|
||||
if (order == Qt::AscendingOrder)
|
||||
return a->name().toLower() < b->name().toLower();
|
||||
else
|
||||
return a->name().toLower() > b->name().toLower();
|
||||
};
|
||||
|
||||
std::sort(m_items.begin(), m_items.end(), compare);
|
||||
switch (column) {
|
||||
case 0:
|
||||
std::sort(m_items.begin(), m_items.end(), [order](const Game *a, const Game *b) {
|
||||
if (order == Qt::AscendingOrder) {
|
||||
return a->name().toUpper() < b->name().toUpper();
|
||||
} else {
|
||||
return a->name().toUpper() > b->name().toUpper();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
std::sort(m_items.begin(), m_items.end(), [order](const Game *a, const Game *b) {
|
||||
if (order == Qt::AscendingOrder) {
|
||||
return a->id() < b->id();
|
||||
} else {
|
||||
return a->id() > b->id();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
@ -13,19 +13,19 @@ public:
|
||||
const QString &id() const { return m_id; }
|
||||
const QString &name() const { return m_name; }
|
||||
const QString &directory() const { return m_directory; }
|
||||
QPixmap icon() const;
|
||||
const QPixmap &icon() const { return m_icon; }
|
||||
|
||||
private:
|
||||
QString m_id;
|
||||
QString m_name;
|
||||
QString m_directory;
|
||||
mutable QPixmap m_cachedIcon;
|
||||
QPixmap m_icon;
|
||||
};
|
||||
|
||||
class GameListModel final : public QAbstractListModel {
|
||||
public:
|
||||
GameListModel(QObject *parent = nullptr);
|
||||
~GameListModel();
|
||||
~GameListModel() override;
|
||||
|
||||
public:
|
||||
void add(Game *game);
|
||||
@ -34,7 +34,9 @@ public:
|
||||
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
|
||||
|
||||
public:
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
private:
|
||||
|
23
src/launch_settings.cpp
Normal file
23
src/launch_settings.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "launch_settings.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
LaunchSettings::LaunchSettings(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
auto layout = new QVBoxLayout();
|
||||
|
||||
// Start button.
|
||||
auto start = new QPushButton("Start");
|
||||
|
||||
connect(start, &QAbstractButton::clicked, [this]() { emit startClicked(); });
|
||||
|
||||
layout->addWidget(start);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
LaunchSettings::~LaunchSettings()
|
||||
{
|
||||
}
|
13
src/launch_settings.hpp
Normal file
13
src/launch_settings.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class LaunchSettings final : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LaunchSettings(QWidget *parent = nullptr);
|
||||
~LaunchSettings() override;
|
||||
|
||||
signals:
|
||||
void startClicked();
|
||||
};
|
@ -1,14 +1,13 @@
|
||||
#include "main_window.hpp"
|
||||
#include "app_data.hpp"
|
||||
#include "core.hpp"
|
||||
#include "game_models.hpp"
|
||||
#include "game_settings.hpp"
|
||||
#include "game_settings_dialog.hpp"
|
||||
#include "launch_settings.hpp"
|
||||
#include "log_formatter.hpp"
|
||||
#include "path.hpp"
|
||||
#include "pkg_installer.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
@ -18,8 +17,8 @@
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QHeaderView>
|
||||
#include <QIcon>
|
||||
#include <QListView>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
@ -28,7 +27,9 @@
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QSettings>
|
||||
#include <QStackedWidget>
|
||||
#include <QStyleHints>
|
||||
#include <QTableView>
|
||||
#include <QTabWidget>
|
||||
#include <QToolBar>
|
||||
#include <QUrl>
|
||||
@ -37,9 +38,10 @@
|
||||
|
||||
MainWindow::MainWindow() :
|
||||
m_tab(nullptr),
|
||||
m_screen(nullptr),
|
||||
m_launch(nullptr),
|
||||
m_games(nullptr),
|
||||
m_log(nullptr),
|
||||
m_kernel(nullptr)
|
||||
m_log(nullptr)
|
||||
{
|
||||
setWindowTitle("Obliteration");
|
||||
|
||||
@ -87,41 +89,37 @@ MainWindow::MainWindow() :
|
||||
helpMenu->addAction(aboutQt);
|
||||
helpMenu->addAction(about);
|
||||
|
||||
#ifndef __APPLE__
|
||||
// File toolbar.
|
||||
auto fileBar = addToolBar("&File");
|
||||
|
||||
fileBar->setMovable(false);
|
||||
fileBar->addAction(installPkg);
|
||||
#endif
|
||||
|
||||
// Central widget.
|
||||
m_tab = new QTabWidget(this);
|
||||
m_tab->setDocumentMode(true);
|
||||
|
||||
#ifdef __APPLE__
|
||||
m_tab->tabBar()->setExpanding(true);
|
||||
#endif
|
||||
|
||||
setCentralWidget(m_tab);
|
||||
|
||||
// Setup screen tab.
|
||||
m_screen = new QStackedWidget();
|
||||
|
||||
m_tab->addTab(m_screen, QIcon(svgPath + "monitor.svg"), "Screen");
|
||||
|
||||
// Setup launch settings.
|
||||
m_launch = new LaunchSettings();
|
||||
|
||||
connect(m_launch, &LaunchSettings::startClicked, this, &MainWindow::startKernel);
|
||||
|
||||
m_screen->addWidget(m_launch);
|
||||
|
||||
// Setup game list.
|
||||
m_games = new QListView();
|
||||
m_games = new QTableView();
|
||||
m_games->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_games->setLayoutMode(QListView::SinglePass);
|
||||
m_games->setSortingEnabled(true);
|
||||
m_games->setWordWrap(false);
|
||||
m_games->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
m_games->setModel(new GameListModel(this));
|
||||
m_games->setViewMode(QListView::IconMode);
|
||||
m_games->setWordWrap(true);
|
||||
m_games->verticalScrollBar()->setSingleStep(20);
|
||||
|
||||
|
||||
connect(m_games, &QAbstractItemView::doubleClicked, this, &MainWindow::startGame);
|
||||
connect(m_games, &QWidget::customContextMenuRequested, this, &MainWindow::requestGamesContextMenu);
|
||||
|
||||
m_tab->addTab(m_games, QIcon(svgPath + "view-comfy.svg"), "Games");
|
||||
|
||||
connect(m_tab, &QTabWidget::currentChanged, this, &MainWindow::tabChanged);
|
||||
|
||||
// Setup log view.
|
||||
auto log = new QPlainTextEdit();
|
||||
|
||||
@ -169,7 +167,6 @@ bool MainWindow::loadGames()
|
||||
|
||||
// Load games
|
||||
progress.setLabelText("Loading games...");
|
||||
auto gameList = reinterpret_cast<GameListModel *>(m_games->model());
|
||||
|
||||
for (auto &gameId : games) {
|
||||
if (progress.wasCanceled() || !loadGame(gameId)) {
|
||||
@ -179,7 +176,10 @@ bool MainWindow::loadGames()
|
||||
progress.setValue(++step);
|
||||
}
|
||||
|
||||
gameList->sort(0, Qt::AscendingOrder); // TODO add ability to select descending order (button?)
|
||||
// Update widgets.
|
||||
m_games->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
|
||||
m_games->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
m_games->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -201,7 +201,7 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
killKernel();
|
||||
m_kernel.kill();
|
||||
}
|
||||
|
||||
// Save geometry.
|
||||
@ -220,26 +220,6 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
// Allows the games list to resort if window is resized.
|
||||
if (m_tab->currentIndex() == 0) {
|
||||
m_games->updateGeometry();
|
||||
m_games->doItemsLayout();
|
||||
}
|
||||
|
||||
QMainWindow::resizeEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::tabChanged()
|
||||
{
|
||||
// Update games list if window was resized on another tab.
|
||||
if (m_tab->currentIndex() == 0) {
|
||||
m_games->updateGeometry();
|
||||
m_games->doItemsLayout();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::installPkg()
|
||||
{
|
||||
// Browse a PKG.
|
||||
@ -341,19 +321,17 @@ void MainWindow::requestGamesContextMenu(const QPoint &pos)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::startGame(const QModelIndex &index)
|
||||
void MainWindow::startKernel()
|
||||
{
|
||||
if (requireEmulatorStopped()) {
|
||||
// Just switch to screen tab if currently running.
|
||||
if (m_kernel) {
|
||||
m_tab->setCurrentIndex(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target game.
|
||||
auto model = reinterpret_cast<GameListModel *>(m_games->model());
|
||||
auto game = model->get(index.row()); // Qt already guaranteed the index is valid.
|
||||
|
||||
// Clear previous log and switch to log view.
|
||||
// Clear previous log and switch to screen tab.
|
||||
m_log->reset();
|
||||
m_tab->setCurrentIndex(1);
|
||||
m_tab->setCurrentIndex(0);
|
||||
|
||||
// Get full path to kernel binary.
|
||||
QString path;
|
||||
@ -361,117 +339,32 @@ void MainWindow::startGame(const QModelIndex &index)
|
||||
if (QFile::exists(".obliteration-development")) {
|
||||
auto b = std::filesystem::current_path();
|
||||
|
||||
b /= STR("src");
|
||||
b /= STR("target");
|
||||
#ifdef NDEBUG
|
||||
b /= STR("release");
|
||||
#if defined(_WIN32) && defined(NDEBUG)
|
||||
path = QString::fromStdWString((b / L"src" / L"target" / L"x86_64-unknown-none" / L"release" / L"obkrnl").wstring());
|
||||
#elif defined(_WIN32) && !defined(NDEBUG)
|
||||
path = QString::fromStdWString((b / L"src" / L"target" / L"x86_64-unknown-none" / L"debug" / L"obkrnl").wstring());
|
||||
#elif defined(NDEBUG)
|
||||
path = QString::fromStdString((b / "src" / "target" / "x86_64-unknown-none" / "release" / "obkrnl").string());
|
||||
#else
|
||||
b /= STR("debug");
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
b /= L"obkrnl.exe";
|
||||
path = QString::fromStdWString(b.wstring());
|
||||
#else
|
||||
b /= "obkrnl";
|
||||
path = QString::fromStdString(b.string());
|
||||
path = QString::fromStdString((b / "src" / "target" / "x86_64-unknown-none" / "debug" / "obkrnl").string());
|
||||
#endif
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
std::filesystem::path b(QCoreApplication::applicationDirPath().toStdString(), std::filesystem::path::native_format);
|
||||
b /= L"bin";
|
||||
b /= L"obkrnl.exe";
|
||||
b /= L"share";
|
||||
b /= L"obkrnl";
|
||||
path = QString::fromStdWString(b.wstring());
|
||||
#else
|
||||
std::filesystem::path b(QCoreApplication::applicationDirPath().toStdString(), std::filesystem::path::native_format);
|
||||
auto b = std::filesystem::path(QCoreApplication::applicationDirPath().toStdString(), std::filesystem::path::native_format).parent_path();
|
||||
#ifdef __APPLE__
|
||||
b /= "Resources";
|
||||
#else
|
||||
b /= "share";
|
||||
#endif
|
||||
b /= "obkrnl";
|
||||
path = QString::fromStdString(b.string());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Setup kernel arguments.
|
||||
QStringList args;
|
||||
|
||||
args << "--debug-dump" << kernelDebugDump();
|
||||
args << "--clear-debug-dump";
|
||||
args << readSystemDirectorySetting();
|
||||
args << game->directory();
|
||||
|
||||
// Setup environment variable.
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
|
||||
env.insert("TERM", "xterm");
|
||||
|
||||
// Prepare kernel launching.
|
||||
m_kernel = new QProcess(this);
|
||||
m_kernel->setProgram(path);
|
||||
m_kernel->setArguments(args);
|
||||
m_kernel->setProcessEnvironment(env);
|
||||
m_kernel->setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
connect(m_kernel, &QProcess::errorOccurred, this, &MainWindow::kernelError);
|
||||
connect(m_kernel, &QIODevice::readyRead, this, &MainWindow::kernelOutput);
|
||||
connect(m_kernel, &QProcess::finished, this, &MainWindow::kernelTerminated);
|
||||
|
||||
// Launch kernel.
|
||||
m_kernel->start(QIODeviceBase::ReadOnly | QIODeviceBase::Text);
|
||||
}
|
||||
|
||||
void MainWindow::kernelError(QProcess::ProcessError error)
|
||||
{
|
||||
// Get error message.
|
||||
QString msg;
|
||||
|
||||
switch (error) {
|
||||
case QProcess::FailedToStart:
|
||||
msg = QString("Failed to launch %1.").arg(m_kernel->program());
|
||||
break;
|
||||
case QProcess::Crashed:
|
||||
msg = "The kernel crashed.";
|
||||
break;
|
||||
default:
|
||||
msg = "The kernel encountered an unknown error.";
|
||||
}
|
||||
|
||||
// Flush the kenel log before we destroy its object.
|
||||
kernelOutput();
|
||||
|
||||
// Destroy object.
|
||||
m_kernel->deleteLater();
|
||||
m_kernel = nullptr;
|
||||
|
||||
// Display error.
|
||||
QMessageBox::critical(this, "Error", msg);
|
||||
}
|
||||
|
||||
void MainWindow::kernelOutput()
|
||||
{
|
||||
// It is possible for Qt to signal this slot after QProcess::errorOccurred or QProcess::finished
|
||||
// so we need to check if the those signals has been received.
|
||||
if (!m_kernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (m_kernel->canReadLine()) {
|
||||
auto line = QString::fromUtf8(m_kernel->readLine());
|
||||
|
||||
m_log->appendMessage(line);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::kernelTerminated(int, QProcess::ExitStatus)
|
||||
{
|
||||
// Do nothing if we got QProcess::errorOccurred before this signal.
|
||||
if (!m_kernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
kernelOutput();
|
||||
|
||||
QMessageBox::critical(this, "Error", "The emulator kernel has stopped unexpectedly. Please take a look at the log and report this issue if possible.");
|
||||
|
||||
m_kernel->deleteLater();
|
||||
m_kernel = nullptr;
|
||||
}
|
||||
|
||||
bool MainWindow::loadGame(const QString &gameId)
|
||||
@ -507,24 +400,6 @@ bool MainWindow::loadGame(const QString &gameId)
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::killKernel()
|
||||
{
|
||||
// Do nothing if the kernel already terminated. This prevent a crash if this method is putting
|
||||
// behind the message box and the kernel itself was terminated while waiting for the user to confirm.
|
||||
if (!m_kernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to disconnect all slots first otherwise the application will be freeze.
|
||||
disconnect(m_kernel, nullptr, nullptr, nullptr);
|
||||
|
||||
m_kernel->kill();
|
||||
m_kernel->waitForFinished(-1);
|
||||
|
||||
delete m_kernel;
|
||||
m_kernel = nullptr;
|
||||
}
|
||||
|
||||
void MainWindow::restoreGeometry()
|
||||
{
|
||||
QSettings settings;
|
||||
@ -554,13 +429,13 @@ bool MainWindow::requireEmulatorStopped()
|
||||
killPrompt.setStandardButtons(QMessageBox::Cancel | QMessageBox::Yes);
|
||||
killPrompt.setDefaultButton(QMessageBox::Cancel);
|
||||
killPrompt.setIcon(QMessageBox::Warning);
|
||||
if (killPrompt.exec() == QMessageBox::Yes) {
|
||||
killKernel();
|
||||
return false; // Kernel was killed
|
||||
|
||||
if (killPrompt.exec() != QMessageBox::Yes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true; // Kernel left running
|
||||
m_kernel.kill();
|
||||
}
|
||||
|
||||
return false; // Kernel isn't running
|
||||
return false;
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QProcess>
|
||||
#include "core.hpp"
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
class LaunchSettings;
|
||||
class LogFormatter;
|
||||
class QListView;
|
||||
class QStackedWidget;
|
||||
class QTableView;
|
||||
|
||||
class MainWindow final : public QMainWindow {
|
||||
public:
|
||||
@ -16,29 +19,25 @@ public:
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void tabChanged();
|
||||
void installPkg();
|
||||
void openSystemFolder();
|
||||
void reportIssue();
|
||||
void aboutObliteration();
|
||||
void requestGamesContextMenu(const QPoint &pos);
|
||||
void startGame(const QModelIndex &index);
|
||||
void kernelError(QProcess::ProcessError error);
|
||||
void kernelOutput();
|
||||
void kernelTerminated(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void startKernel();
|
||||
|
||||
private:
|
||||
bool loadGame(const QString &gameId);
|
||||
void killKernel();
|
||||
void restoreGeometry();
|
||||
bool requireEmulatorStopped();
|
||||
|
||||
private:
|
||||
QTabWidget *m_tab;
|
||||
QListView *m_games;
|
||||
QStackedWidget *m_screen;
|
||||
LaunchSettings *m_launch;
|
||||
QTableView *m_games;
|
||||
LogFormatter *m_log;
|
||||
QProcess *m_kernel;
|
||||
Vmm m_kernel;
|
||||
};
|
||||
|
@ -10,6 +10,7 @@
|
||||
<file>resources/lightmode/card-text-outline.svg</file>
|
||||
<file>resources/lightmode/cog-outline.svg</file>
|
||||
<file>resources/lightmode/folder-open-outline.svg</file>
|
||||
<file>resources/lightmode/monitor.svg</file>
|
||||
<file>resources/lightmode/view-comfy.svg</file>
|
||||
<file>resources/fallbackicon0.png</file>
|
||||
<file>resources/obliteration-icon.png</file>
|
||||
|
1
src/resources/lightmode/monitor.svg
Normal file
1
src/resources/lightmode/monitor.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21,16H3V4H21M21,2H3C1.89,2 1,2.89 1,4V16A2,2 0 0,0 3,18H10V20H8V22H16V20H14V18H21A2,2 0 0,0 23,16V4C23,2.89 22.1,2 21,2Z" /></svg>
|
After Width: | Height: | Size: 200 B |
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#define STR(x) L##x
|
||||
#else
|
||||
#define STR(x) x
|
||||
#endif
|
@ -2,7 +2,6 @@
|
||||
#include "path.hpp"
|
||||
#include "progress_dialog.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "string.hpp"
|
||||
#include "system_downloader.hpp"
|
||||
|
||||
#include <QCoreApplication>
|
||||
@ -20,12 +19,11 @@ bool isSystemInitialized(const QString &path)
|
||||
auto libkernel = toPath(path);
|
||||
|
||||
try {
|
||||
libkernel /= STR("system");
|
||||
libkernel /= STR("common");
|
||||
libkernel /= STR("lib");
|
||||
libkernel /= STR("libkernel.sprx");
|
||||
|
||||
return std::filesystem::exists(libkernel);
|
||||
#ifdef _WIN32
|
||||
return std::filesystem::exists(libkernel / L"system" / L"common" / L"lib" / L"libkernel.sprx");
|
||||
#else
|
||||
return std::filesystem::exists(libkernel / "system" / "common" / "lib" / "libkernel.sprx");
|
||||
#endif
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user