summaryrefslogtreecommitdiffstats
path: root/src/citra_qt
diff options
context:
space:
mode:
Diffstat (limited to 'src/citra_qt')
-rw-r--r--src/citra_qt/CMakeLists.txt29
-rw-r--r--src/citra_qt/bootmanager.cpp5
-rw-r--r--src/citra_qt/debugger/wait_tree.cpp3
-rw-r--r--src/citra_qt/game_list.cpp28
-rw-r--r--src/citra_qt/game_list.h3
-rw-r--r--src/citra_qt/game_list_p.h5
-rw-r--r--src/citra_qt/main.cpp159
-rw-r--r--src/citra_qt/main.h29
8 files changed, 161 insertions, 100 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index a9dacd5f1..e1b3566bf 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -1,5 +1,6 @@
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
set(SRCS
config.cpp
@@ -107,27 +108,9 @@ if(UNIX AND NOT APPLE)
install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif()
-if (Qt5_FOUND AND MSVC)
- include(WindowsCopyFiles)
-
- set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
- set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
- set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
- set(PLATFORMS ${DLL_DEST}platforms/)
-
- windows_copy_files(citra-qt ${Qt5_DLL_DIR} ${DLL_DEST}
- icudt*.dll
- icuin*.dll
- icuuc*.dll
- Qt5Core$<$<CONFIG:Debug>:d>.*
- Qt5Gui$<$<CONFIG:Debug>:d>.*
- Qt5OpenGL$<$<CONFIG:Debug>:d>.*
- Qt5Widgets$<$<CONFIG:Debug>:d>.*
- )
- windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
-
- unset(Qt5_DLL_DIR)
- unset(Qt5_PLATFORMS_DIR)
- unset(DLL_DEST)
- unset(PLATFORMS)
+if (MSVC)
+ include(CopyCitraQt5Deps)
+ include(CopyCitraSDLDeps)
+ copy_citra_Qt5_deps(citra-qt)
+ copy_citra_SDL_deps(citra-qt)
endif()
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 7699ca8d0..c7eb2aafc 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -19,11 +19,6 @@
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/video_core.h"
-#define APP_NAME "citra"
-#define APP_VERSION "0.1-" VERSION
-#define APP_TITLE APP_NAME " " APP_VERSION
-#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
-
EmuThread::EmuThread(GRenderWindow* render_window)
: exec_step(false), running(false), stop_run(false), render_window(render_window) {}
diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp
index 51e70fae3..5a308bf7f 100644
--- a/src/citra_qt/debugger/wait_tree.cpp
+++ b/src/citra_qt/debugger/wait_tree.cpp
@@ -229,7 +229,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes));
}
if (thread.status == THREADSTATUS_WAIT_SYNCH) {
- list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, thread.wait_all));
+ list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
+ thread.IsSleepingOnWaitAll()));
}
return list;
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index e536628dd..09469f3c5 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <QHeaderView>
+#include <QMenu>
#include <QThreadPool>
#include <QVBoxLayout>
#include "common/common_paths.h"
@@ -28,6 +29,7 @@ GameList::GameList(QWidget* parent) : QWidget{parent} {
tree_view->setSortingEnabled(true);
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
tree_view->setUniformRowHeights(true);
+ tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
item_model->insertColumns(0, COLUMN_COUNT);
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
@@ -35,10 +37,10 @@ GameList::GameList(QWidget* parent) : QWidget{parent} {
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
+ connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
// We must register all custom types with the Qt Automoc system so that we are able to use it
- // with
- // signals/slots. In this case, QList falls under the umbrells of custom types.
+ // with signals/slots. In this case, QList falls under the umbrells of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
layout->addWidget(tree_view);
@@ -71,6 +73,23 @@ void GameList::DonePopulating() {
tree_view->setEnabled(true);
}
+void GameList::PopupContextMenu(const QPoint& menu_location) {
+ QModelIndex item = tree_view->indexAt(menu_location);
+ if (!item.isValid())
+ return;
+
+ int row = item_model->itemFromIndex(item)->row();
+ QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
+ u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong();
+
+ QMenu context_menu;
+ QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
+ open_save_location->setEnabled(program_id != 0);
+ connect(open_save_location, &QAction::triggered,
+ [&]() { emit OpenSaveFolderRequested(program_id); });
+ context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
+}
+
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
if (!FileUtil::Exists(dir_path.toStdString()) ||
!FileUtil::IsDirectory(dir_path.toStdString())) {
@@ -128,8 +147,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
std::vector<u8> smdh;
loader->ReadIcon(smdh);
+ u64 program_id = 0;
+ loader->ReadProgramId(program_id);
+
emit EntryReady({
- new GameListItemPath(QString::fromStdString(physical_name), smdh),
+ new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id),
new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)),
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 30b2c79a8..1abf10051 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -36,12 +36,15 @@ public:
signals:
void GameChosen(QString game_path);
void ShouldCancelWorker();
+ void OpenSaveFolderRequested(u64 program_id);
private:
void AddEntry(const QList<QStandardItem*>& entry_items);
void ValidateEntry(const QModelIndex& item);
void DonePopulating();
+ void PopupContextMenu(const QPoint& menu_location);
+
QTreeView* tree_view = nullptr;
QStandardItemModel* item_model = nullptr;
GameListWorker* current_worker = nullptr;
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h
index 5ca3fe991..a15f06c5f 100644
--- a/src/citra_qt/game_list_p.h
+++ b/src/citra_qt/game_list_p.h
@@ -71,10 +71,13 @@ class GameListItemPath : public GameListItem {
public:
static const int FullPathRole = Qt::UserRole + 1;
static const int TitleRole = Qt::UserRole + 2;
+ static const int ProgramIdRole = Qt::UserRole + 3;
GameListItemPath() : GameListItem() {}
- GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() {
+ GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id)
+ : GameListItem() {
setData(game_path, FullPathRole);
+ setData(qulonglong(program_id), ProgramIdRole);
if (!Loader::IsValidSMDH(smdh_data)) {
// SMDH is not valid, set a default icon
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index a3887f9ab..4486f0870 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cinttypes>
#include <clocale>
#include <memory>
#include <thread>
@@ -41,6 +42,7 @@
#include "common/string_util.h"
#include "core/arm/disassembler/load_symbol_map.h"
#include "core/core.h"
+#include "core/file_sys/archive_source_sd_savedata.h"
#include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h"
#include "core/settings.h"
@@ -58,6 +60,36 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
ui.setupUi(this);
statusBar()->hide();
+ InitializeWidgets();
+ InitializeDebugMenuActions();
+ InitializeRecentFileMenuActions();
+ InitializeHotkeys();
+
+ SetDefaultUIGeometry();
+ RestoreUIState();
+
+ ConnectWidgetEvents();
+
+ setWindowTitle(QString("Citra | %1-%2").arg(Common::g_scm_branch, Common::g_scm_desc));
+ show();
+
+ game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
+
+ QStringList args = QApplication::arguments();
+ if (args.length() >= 2) {
+ BootGame(args[1].toStdString());
+ }
+}
+
+GMainWindow::~GMainWindow() {
+ // will get automatically deleted otherwise
+ if (render_window->parent() == nullptr)
+ delete render_window;
+
+ Pica::g_debug_context.reset();
+}
+
+void GMainWindow::InitializeWidgets() {
render_window = new GRenderWindow(this, emu_thread.get());
render_window->hide();
@@ -93,25 +125,27 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
graphicsCommandsWidget->hide();
- auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
+ graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
graphicsBreakpointsWidget->hide();
- auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
+ graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
graphicsVertexShaderWidget->hide();
- auto graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this);
+ graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
graphicsTracingWidget->hide();
- auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica Surface Viewer"), this);
- connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
- SLOT(OnCreateGraphicsSurfaceViewer()));
-
waitTreeWidget = new WaitTreeWidget(this);
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
waitTreeWidget->hide();
+}
+
+void GMainWindow::InitializeDebugMenuActions() {
+ auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica Surface Viewer"), this);
+ connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
+ SLOT(OnCreateGraphicsSurfaceViewer()));
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(graphicsSurfaceViewerAction);
@@ -129,19 +163,47 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
debug_menu->addAction(waitTreeWidget->toggleViewAction());
+}
+
+void GMainWindow::InitializeRecentFileMenuActions() {
+ for (int i = 0; i < max_recent_files_item; ++i) {
+ actions_recent_files[i] = new QAction(this);
+ actions_recent_files[i]->setVisible(false);
+ connect(actions_recent_files[i], SIGNAL(triggered()), this, SLOT(OnMenuRecentFile()));
+
+ ui.menu_recent_files->addAction(actions_recent_files[i]);
+ }
+
+ UpdateRecentFiles();
+}
+
+void GMainWindow::InitializeHotkeys() {
+ RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
+ RegisterHotkey("Main Window", "Swap Screens", QKeySequence::NextChild);
+ RegisterHotkey("Main Window", "Start Emulation");
+ LoadHotkeys();
+
+ connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
+ SLOT(OnMenuLoadFile()));
+ connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this,
+ SLOT(OnStartGame()));
+ connect(GetHotkey("Main Window", "Swap Screens", render_window), SIGNAL(activated()), this,
+ SLOT(OnSwapScreens()));
+}
- // Set default UI state
+void GMainWindow::SetDefaultUIGeometry() {
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
- QDesktopWidget* desktop = ((QApplication*)QApplication::instance())->desktop();
- QRect screenRect = desktop->screenGeometry(this);
- int x, y, w, h;
- w = screenRect.width() * 2 / 3;
- h = screenRect.height() / 2;
- x = (screenRect.x() + screenRect.width()) / 2 - w / 2;
- y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100;
+ const QRect screenRect = QApplication::desktop()->screenGeometry(this);
+
+ const int w = screenRect.width() * 2 / 3;
+ const int h = screenRect.height() / 2;
+ const int x = (screenRect.x() + screenRect.width()) / 2 - w / 2;
+ const int y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100;
+
setGeometry(x, y, w, h);
+}
- // Restore UI state
+void GMainWindow::RestoreUIState() {
restoreGeometry(UISettings::values.geometry);
restoreState(UISettings::values.state);
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
@@ -157,20 +219,13 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar);
OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked());
+}
- // Prepare actions for recent files
- for (int i = 0; i < max_recent_files_item; ++i) {
- actions_recent_files[i] = new QAction(this);
- actions_recent_files[i]->setVisible(false);
- connect(actions_recent_files[i], SIGNAL(triggered()), this, SLOT(OnMenuRecentFile()));
-
- ui.menu_recent_files->addAction(actions_recent_files[i]);
- }
- UpdateRecentFiles();
-
- // Setup connections
+void GMainWindow::ConnectWidgetEvents() {
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)),
Qt::DirectConnection);
+ connect(game_list, SIGNAL(OpenSaveFolderRequested(u64)), this,
+ SLOT(OnGameListOpenSaveFolder(u64)), Qt::DirectConnection);
connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),
Qt::DirectConnection);
@@ -197,40 +252,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
connect(this, SIGNAL(EmulationStarting(EmuThread*)), waitTreeWidget,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), waitTreeWidget, SLOT(OnEmulationStopping()));
-
- // Setup hotkeys
- RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
- RegisterHotkey("Main Window", "Swap Screens", QKeySequence::NextChild);
- RegisterHotkey("Main Window", "Start Emulation");
- LoadHotkeys();
-
- connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
- SLOT(OnMenuLoadFile()));
- connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this,
- SLOT(OnStartGame()));
- connect(GetHotkey("Main Window", "Swap Screens", this), SIGNAL(activated()), this,
- SLOT(OnSwapScreens()));
-
- std::string window_title =
- Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
- setWindowTitle(window_title.c_str());
-
- show();
-
- game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
-
- QStringList args = QApplication::arguments();
- if (args.length() >= 2) {
- BootGame(args[1].toStdString());
- }
-}
-
-GMainWindow::~GMainWindow() {
- // will get automatically deleted otherwise
- if (render_window->parent() == nullptr)
- delete render_window;
-
- Pica::g_debug_context.reset();
}
void GMainWindow::OnDisplayTitleBars(bool show) {
@@ -386,6 +407,7 @@ void GMainWindow::BootGame(const std::string& filename) {
game_list->hide();
}
render_window->show();
+ render_window->setFocus();
emulation_running = true;
OnStartGame();
@@ -460,6 +482,21 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
BootGame(game_path.toStdString());
}
+void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) {
+ std::string sdmc_dir = FileUtil::GetUserPath(D_SDMC_IDX);
+ std::string path = FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(sdmc_dir, program_id);
+ QString qpath = QString::fromStdString(path);
+
+ QDir dir(qpath);
+ if (!dir.exists()) {
+ QMessageBox::critical(this, tr("Error Opening Save Folder"), tr("Folder does not exist!"));
+ return;
+ }
+
+ LOG_INFO(Frontend, "Opening save data path for program_id=%" PRIu64, program_id);
+ QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
+}
+
void GMainWindow::OnMenuLoadFile() {
QString filename =
QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path,
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index f87178227..a2fd45c47 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -9,18 +9,21 @@
#include <QMainWindow>
#include "ui_main.h"
+class CallstackWidget;
class Config;
+class DisassemblerWidget;
+class EmuThread;
class GameList;
class GImageInfo;
+class GPUCommandStreamWidget;
+class GPUCommandListWidget;
+class GraphicsBreakPointsWidget;
+class GraphicsTracingWidget;
+class GraphicsVertexShaderWidget;
class GRenderWindow;
-class EmuThread;
-class ProfilerWidget;
class MicroProfileDialog;
-class DisassemblerWidget;
+class ProfilerWidget;
class RegistersWidget;
-class CallstackWidget;
-class GPUCommandStreamWidget;
-class GPUCommandListWidget;
class WaitTreeWidget;
class GMainWindow : public QMainWindow {
@@ -60,6 +63,16 @@ signals:
void EmulationStopping();
private:
+ void InitializeWidgets();
+ void InitializeDebugMenuActions();
+ void InitializeRecentFileMenuActions();
+ void InitializeHotkeys();
+
+ void SetDefaultUIGeometry();
+ void RestoreUIState();
+
+ void ConnectWidgetEvents();
+
/**
* Initializes the emulation system.
* @param system_mode The system mode with which to intialize the kernel.
@@ -105,6 +118,7 @@ private slots:
void OnStopGame();
/// Called whenever a user selects a game in the game list widget.
void OnGameListLoadFile(QString game_path);
+ void OnGameListOpenSaveFolder(u64 program_id);
void OnMenuLoadFile();
void OnMenuLoadSymbolMap();
/// Called whenever a user selects the "File->Select Game List Root" menu item
@@ -135,6 +149,9 @@ private:
CallstackWidget* callstackWidget;
GPUCommandStreamWidget* graphicsWidget;
GPUCommandListWidget* graphicsCommandsWidget;
+ GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
+ GraphicsVertexShaderWidget* graphicsVertexShaderWidget;
+ GraphicsTracingWidget* graphicsTracingWidget;
WaitTreeWidget* waitTreeWidget;
QAction* actions_recent_files[max_recent_files_item];