summaryrefslogtreecommitdiffstats
path: root/src/citra_qt
diff options
context:
space:
mode:
Diffstat (limited to 'src/citra_qt')
-rw-r--r--src/citra_qt/CMakeLists.txt3
-rw-r--r--src/citra_qt/bootmanager.cpp31
-rw-r--r--src/citra_qt/bootmanager.h6
-rw-r--r--src/citra_qt/config.cpp96
-rw-r--r--src/citra_qt/config.h5
-rw-r--r--src/citra_qt/configure_general.ui2
-rw-r--r--src/citra_qt/configure_input.cpp179
-rw-r--r--src/citra_qt/configure_input.h34
-rw-r--r--src/citra_qt/configure_system.cpp1
-rw-r--r--src/citra_qt/debugger/graphics/graphics_cmdlists.cpp36
-rw-r--r--src/citra_qt/debugger/graphics/graphics_surface.cpp54
-rw-r--r--src/citra_qt/debugger/graphics/graphics_tracing.cpp5
-rw-r--r--src/citra_qt/debugger/graphics/graphics_tracing.h6
-rw-r--r--src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp9
-rw-r--r--src/citra_qt/debugger/graphics/graphics_vertex_shader.h2
-rw-r--r--src/citra_qt/debugger/profiler.cpp111
-rw-r--r--src/citra_qt/debugger/profiler.h40
-rw-r--r--src/citra_qt/debugger/profiler.ui33
-rw-r--r--src/citra_qt/game_list.cpp60
-rw-r--r--src/citra_qt/game_list.h6
-rw-r--r--src/citra_qt/game_list_p.h8
-rw-r--r--src/citra_qt/hotkeys.h2
-rw-r--r--src/citra_qt/main.cpp243
-rw-r--r--src/citra_qt/main.h30
-rw-r--r--src/citra_qt/main.ui65
-rw-r--r--src/citra_qt/ui_settings.h1
26 files changed, 590 insertions, 478 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index d4460bf01..2b1c59a92 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -69,7 +69,6 @@ set(HEADERS
set(UIS
debugger/callstack.ui
debugger/disassembler.ui
- debugger/profiler.ui
debugger/registers.ui
configure.ui
configure_audio.ui
@@ -98,7 +97,7 @@ if (APPLE)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
endif()
-target_link_libraries(citra-qt core video_core audio_core common)
+target_link_libraries(citra-qt core video_core audio_core common input_common)
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads)
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 948db384d..bae576d6a 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -13,7 +13,9 @@
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "core/core.h"
-#include "core/frontend/key_map.h"
+#include "core/settings.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/video_core.h"
@@ -99,14 +101,17 @@ private:
};
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
- : QWidget(parent), child(nullptr), keyboard_id(0), emu_thread(emu_thread) {
+ : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
- std::string window_title =
- Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
+ std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name,
+ Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(QString::fromStdString(window_title));
- keyboard_id = KeyMap::NewDeviceId();
- ReloadSetKeymaps();
+ InputCommon::Init();
+}
+
+GRenderWindow::~GRenderWindow() {
+ InputCommon::Shutdown();
}
void GRenderWindow::moveContext() {
@@ -197,11 +202,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
}
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
- KeyMap::PressKey(*this, {event->key(), keyboard_id});
+ InputCommon::GetKeyboard()->PressKey(event->key());
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
- KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
+ InputCommon::GetKeyboard()->ReleaseKey(event->key());
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -230,13 +235,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
motion_emu->EndTilt();
}
-void GRenderWindow::ReloadSetKeymaps() {
- KeyMap::ClearKeyMapping(keyboard_id);
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- KeyMap::SetKeyMapping(
- {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
- KeyMap::mapping_targets[i]);
- }
+void GRenderWindow::focusOutEvent(QFocusEvent* event) {
+ QWidget::focusOutEvent(event);
+ InputCommon::GetKeyboard()->ReleaseAllKeys();
}
void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 7dac1c480..9d39f1af8 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -104,6 +104,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
+ ~GRenderWindow();
// EmuWindow implementation
void SwapBuffers() override;
@@ -127,7 +128,7 @@ public:
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
- void ReloadSetKeymaps() override;
+ void focusOutEvent(QFocusEvent* event) override;
void OnClientAreaResized(unsigned width, unsigned height);
@@ -152,9 +153,6 @@ private:
QByteArray geometry;
- /// Device id of keyboard for use with KeyMap
- int keyboard_id;
-
EmuThread* emu_thread;
/// Motion sensors emulation
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 2c03d3e93..bf0ac7c66 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -6,6 +6,7 @@
#include "citra_qt/config.h"
#include "citra_qt/ui_settings.h"
#include "common/file_util.h"
+#include "input_common/main.h"
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -16,25 +17,46 @@ Config::Config() {
Reload();
}
-const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
- // directly mapped keys
- Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
- Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I,
- Qt::Key_K, Qt::Key_J, Qt::Key_L,
-
- // indirectly mapped keys
- Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
+const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
+ Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
+ Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
};
+const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
+ {
+ Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
+ },
+ {
+ Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, Qt::Key_D,
+ },
+}};
+
void Config::ReadValues() {
qt_config->beginGroup("Controls");
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- Settings::values.input_mappings[Settings::NativeInput::All[i]] =
- qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i])
- .toInt();
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ Settings::values.buttons[i] =
+ qt_config
+ ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (Settings::values.buttons[i].empty())
+ Settings::values.buttons[i] = default_param;
}
- Settings::values.pad_circle_modifier_scale =
- qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
+
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_analogs[i][4], 0.5f);
+ Settings::values.analogs[i] =
+ qt_config
+ ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (Settings::values.analogs[i].empty())
+ Settings::values.analogs[i] = default_param;
+ }
+
qt_config->endGroup();
qt_config->beginGroup("Core");
@@ -76,6 +98,22 @@ void Config::ReadValues() {
qt_config->value("output_device", "auto").toString().toStdString();
qt_config->endGroup();
+ using namespace Service::CAM;
+ qt_config->beginGroup("Camera");
+ Settings::values.camera_name[OuterRightCamera] =
+ qt_config->value("camera_outer_right_name", "blank").toString().toStdString();
+ Settings::values.camera_config[OuterRightCamera] =
+ qt_config->value("camera_outer_right_config", "").toString().toStdString();
+ Settings::values.camera_name[InnerCamera] =
+ qt_config->value("camera_inner_name", "blank").toString().toStdString();
+ Settings::values.camera_config[InnerCamera] =
+ qt_config->value("camera_inner_config", "").toString().toStdString();
+ Settings::values.camera_name[OuterLeftCamera] =
+ qt_config->value("camera_outer_left_name", "blank").toString().toStdString();
+ Settings::values.camera_config[OuterLeftCamera] =
+ qt_config->value("camera_outer_left_config", "").toString().toStdString();
+ qt_config->endGroup();
+
qt_config->beginGroup("Data Storage");
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
qt_config->endGroup();
@@ -139,6 +177,7 @@ void Config::ReadValues() {
UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
+ UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
@@ -147,12 +186,14 @@ void Config::ReadValues() {
void Config::SaveValues() {
qt_config->beginGroup("Controls");
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
- Settings::values.input_mappings[Settings::NativeInput::All[i]]);
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(Settings::values.buttons[i]));
+ }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(Settings::values.analogs[i]));
}
- qt_config->setValue("pad_circle_modifier_scale",
- (double)Settings::values.pad_circle_modifier_scale);
qt_config->endGroup();
qt_config->beginGroup("Core");
@@ -192,6 +233,22 @@ void Config::SaveValues() {
qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id));
qt_config->endGroup();
+ using namespace Service::CAM;
+ qt_config->beginGroup("Camera");
+ qt_config->setValue("camera_outer_right_name",
+ QString::fromStdString(Settings::values.camera_name[OuterRightCamera]));
+ qt_config->setValue("camera_outer_right_config",
+ QString::fromStdString(Settings::values.camera_config[OuterRightCamera]));
+ qt_config->setValue("camera_inner_name",
+ QString::fromStdString(Settings::values.camera_name[InnerCamera]));
+ qt_config->setValue("camera_inner_config",
+ QString::fromStdString(Settings::values.camera_config[InnerCamera]));
+ qt_config->setValue("camera_outer_left_name",
+ QString::fromStdString(Settings::values.camera_name[OuterLeftCamera]));
+ qt_config->setValue("camera_outer_left_config",
+ QString::fromStdString(Settings::values.camera_config[OuterLeftCamera]));
+ qt_config->endGroup();
+
qt_config->beginGroup("Data Storage");
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
qt_config->endGroup();
@@ -238,6 +295,7 @@ void Config::SaveValues() {
qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
+ qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);
qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
qt_config->setValue("firstStart", UISettings::values.first_start);
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h
index 79c901804..cbf745ea2 100644
--- a/src/citra_qt/config.h
+++ b/src/citra_qt/config.h
@@ -4,6 +4,7 @@
#pragma once
+#include <array>
#include <string>
#include <QVariant>
#include "core/settings.h"
@@ -23,5 +24,7 @@ public:
void Reload();
void Save();
- static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults;
+
+ static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
+ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
};
diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configure_general.ui
index 0f3352a1d..c739605a4 100644
--- a/src/citra_qt/configure_general.ui
+++ b/src/citra_qt/configure_general.ui
@@ -27,7 +27,7 @@
<item>
<widget class="QCheckBox" name="toggle_deepscan">
<property name="text">
- <string>Recursive scan for game folder</string>
+ <string>Search sub-directories for games</string>
</property>
</widget>
</item>
diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp
index 3e6803b8a..b59713e2c 100644
--- a/src/citra_qt/configure_input.cpp
+++ b/src/citra_qt/configure_input.cpp
@@ -2,13 +2,21 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <memory>
#include <utility>
#include <QTimer>
#include "citra_qt/config.h"
#include "citra_qt/configure_input.h"
+#include "common/param_package.h"
+#include "input_common/main.h"
-static QString getKeyName(Qt::Key key_code) {
+const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
+ ConfigureInput::analog_sub_buttons{{
+ "up", "down", "left", "right", "modifier",
+ }};
+
+static QString getKeyName(int key_code) {
switch (key_code) {
case Qt::Key_Shift:
return QObject::tr("Shift");
@@ -17,13 +25,26 @@ static QString getKeyName(Qt::Key key_code) {
case Qt::Key_Alt:
return QObject::tr("Alt");
case Qt::Key_Meta:
- case -1:
return "";
default:
return QKeySequence(key_code).toString();
}
}
+static void SetButtonKey(int key, Common::ParamPackage& button_param) {
+ button_param = Common::ParamPackage{InputCommon::GenerateKeyboardParam(key)};
+}
+
+static void SetAnalogKey(int key, Common::ParamPackage& analog_param,
+ const std::string& button_name) {
+ if (analog_param.Get("engine", "") != "analog_from_button") {
+ analog_param = {
+ {"engine", "analog_from_button"}, {"modifier_scale", "0.5"},
+ };
+ }
+ analog_param.Set(button_name, InputCommon::GenerateKeyboardParam(key));
+}
+
ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
timer(std::make_unique<QTimer>()) {
@@ -32,36 +53,41 @@ ConfigureInput::ConfigureInput(QWidget* parent)
setFocusPolicy(Qt::ClickFocus);
button_map = {
- {Settings::NativeInput::Values::A, ui->buttonA},
- {Settings::NativeInput::Values::B, ui->buttonB},
- {Settings::NativeInput::Values::X, ui->buttonX},
- {Settings::NativeInput::Values::Y, ui->buttonY},
- {Settings::NativeInput::Values::L, ui->buttonL},
- {Settings::NativeInput::Values::R, ui->buttonR},
- {Settings::NativeInput::Values::ZL, ui->buttonZL},
- {Settings::NativeInput::Values::ZR, ui->buttonZR},
- {Settings::NativeInput::Values::START, ui->buttonStart},
- {Settings::NativeInput::Values::SELECT, ui->buttonSelect},
- {Settings::NativeInput::Values::HOME, ui->buttonHome},
- {Settings::NativeInput::Values::DUP, ui->buttonDpadUp},
- {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown},
- {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft},
- {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight},
- {Settings::NativeInput::Values::CUP, ui->buttonCStickUp},
- {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown},
- {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft},
- {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight},
- {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp},
- {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown},
- {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft},
- {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight},
- {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod},
+ ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonDpadUp,
+ ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR,
+ ui->buttonStart, ui->buttonSelect, ui->buttonZL, ui->buttonZR, ui->buttonHome,
};
- for (const auto& entry : button_map) {
- const Settings::NativeInput::Values input_id = entry.first;
- connect(entry.second, &QPushButton::released,
- [this, input_id]() { handleClick(input_id); });
+ analog_map = {{
+ {
+ ui->buttonCircleUp, ui->buttonCircleDown, ui->buttonCircleLeft, ui->buttonCircleRight,
+ ui->buttonCircleMod,
+ },
+ {
+ ui->buttonCStickUp, ui->buttonCStickDown, ui->buttonCStickLeft, ui->buttonCStickRight,
+ nullptr,
+ },
+ }};
+
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ if (button_map[button_id])
+ connect(button_map[button_id], &QPushButton::released, [=]() {
+ handleClick(button_map[button_id],
+ [=](int key) { SetButtonKey(key, buttons_param[button_id]); });
+ });
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ if (analog_map[analog_id][sub_button_id] != nullptr) {
+ connect(analog_map[analog_id][sub_button_id], &QPushButton::released, [=]() {
+ handleClick(analog_map[analog_id][sub_button_id], [=](int key) {
+ SetAnalogKey(key, analogs_param[analog_id],
+ analog_sub_buttons[sub_button_id]);
+ });
+ });
+ }
+ }
}
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
@@ -70,50 +96,93 @@ ConfigureInput::ConfigureInput(QWidget* parent)
connect(timer.get(), &QTimer::timeout, [this]() {
releaseKeyboard();
releaseMouse();
- current_input_id = boost::none;
+ key_setter = boost::none;
updateButtonLabels();
});
this->loadConfiguration();
+
+ // TODO(wwylele): enable these when the input emulation for them is implemented
+ ui->buttonZL->setEnabled(false);
+ ui->buttonZR->setEnabled(false);
+ ui->buttonHome->setEnabled(false);
+ ui->buttonCStickUp->setEnabled(false);
+ ui->buttonCStickDown->setEnabled(false);
+ ui->buttonCStickLeft->setEnabled(false);
+ ui->buttonCStickRight->setEnabled(false);
}
void ConfigureInput::applyConfiguration() {
- for (const auto& input_id : Settings::NativeInput::All) {
- const size_t index = static_cast<size_t>(input_id);
- Settings::values.input_mappings[index] = static_cast<int>(key_map[input_id]);
- }
+ std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+ std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+
Settings::Apply();
}
void ConfigureInput::loadConfiguration() {
- for (const auto& input_id : Settings::NativeInput::All) {
- const size_t index = static_cast<size_t>(input_id);
- key_map[input_id] = static_cast<Qt::Key>(Settings::values.input_mappings[index]);
- }
+ std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(),
+ buttons_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
+ analogs_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
updateButtonLabels();
}
void ConfigureInput::restoreDefaults() {
- for (const auto& input_id : Settings::NativeInput::All) {
- const size_t index = static_cast<size_t>(input_id);
- key_map[input_id] = static_cast<Qt::Key>(Config::defaults[index].toInt());
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ SetButtonKey(Config::default_buttons[button_id], buttons_param[button_id]);
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ SetAnalogKey(Config::default_analogs[analog_id][sub_button_id],
+ analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
+ }
}
updateButtonLabels();
applyConfiguration();
}
void ConfigureInput::updateButtonLabels() {
- for (const auto& input_id : Settings::NativeInput::All) {
- button_map[input_id]->setText(getKeyName(key_map[input_id]));
+ QString non_keyboard(tr("[non-keyboard]"));
+
+ auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) {
+ if (param.Get("engine", "") != "keyboard") {
+ return non_keyboard;
+ } else {
+ return getKeyName(param.Get("code", 0));
+ }
+ };
+
+ for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
+ button_map[button]->setText(KeyToText(buttons_param[button]));
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") {
+ for (QPushButton* button : analog_map[analog_id]) {
+ if (button)
+ button->setText(non_keyboard);
+ }
+ } else {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ Common::ParamPackage param(
+ analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], ""));
+ if (analog_map[analog_id][sub_button_id])
+ analog_map[analog_id][sub_button_id]->setText(KeyToText(param));
+ }
+ }
}
}
-void ConfigureInput::handleClick(Settings::NativeInput::Values input_id) {
- QPushButton* button = button_map[input_id];
+void ConfigureInput::handleClick(QPushButton* button, std::function<void(int)> new_key_setter) {
button->setText(tr("[press key]"));
button->setFocus();
- current_input_id = input_id;
+ key_setter = new_key_setter;
grabKeyboard();
grabMouse();
@@ -124,23 +193,13 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) {
releaseKeyboard();
releaseMouse();
- if (!current_input_id || !event)
+ if (!key_setter || !event)
return;
if (event->key() != Qt::Key_Escape)
- setInput(*current_input_id, static_cast<Qt::Key>(event->key()));
+ (*key_setter)(event->key());
updateButtonLabels();
- current_input_id = boost::none;
+ key_setter = boost::none;
timer->stop();
}
-
-void ConfigureInput::setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed) {
- // Remove duplicates
- for (auto& pair : key_map) {
- if (pair.second == key_pressed)
- pair.second = Qt::Key_unknown;
- }
-
- key_map[input_id] = key_pressed;
-}
diff --git a/src/citra_qt/configure_input.h b/src/citra_qt/configure_input.h
index bc343db83..c950fbcb4 100644
--- a/src/citra_qt/configure_input.h
+++ b/src/citra_qt/configure_input.h
@@ -4,10 +4,14 @@
#pragma once
+#include <array>
+#include <functional>
#include <memory>
+#include <string>
#include <QKeyEvent>
#include <QWidget>
#include <boost/optional.hpp>
+#include "common/param_package.h"
#include "core/settings.h"
#include "ui_configure_input.h"
@@ -31,15 +35,25 @@ public:
private:
std::unique_ptr<Ui::ConfigureInput> ui;
- /// This input is currently awaiting configuration.
- /// (i.e.: its corresponding QPushButton has been pressed.)
- boost::optional<Settings::NativeInput::Values> current_input_id;
std::unique_ptr<QTimer> timer;
- /// Each input is represented by a QPushButton.
- std::map<Settings::NativeInput::Values, QPushButton*> button_map;
- /// Each input is configured to respond to the press of a Qt::Key.
- std::map<Settings::NativeInput::Values, Qt::Key> key_map;
+ /// This will be the the setting function when an input is awaiting configuration.
+ boost::optional<std::function<void(int)>> key_setter;
+
+ std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
+ std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
+
+ static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
+
+ /// Each button input is represented by a QPushButton.
+ std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
+
+ /// Each analog input is represented by five QPushButtons which represents up, down, left, right
+ /// and modifier
+ std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
+ analog_map;
+
+ static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
/// Load configuration settings.
void loadConfiguration();
@@ -48,10 +62,8 @@ private:
/// Update UI to reflect current configuration.
void updateButtonLabels();
- /// Called when the button corresponding to input_id was pressed.
- void handleClick(Settings::NativeInput::Values input_id);
+ /// Called when the button was pressed.
+ void handleClick(QPushButton* button, std::function<void(int)> new_key_setter);
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
- /// Configure input input_id to respond to key key_pressed.
- void setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed);
};
diff --git a/src/citra_qt/configure_system.cpp b/src/citra_qt/configure_system.cpp
index eb1276ef3..040185e82 100644
--- a/src/citra_qt/configure_system.cpp
+++ b/src/citra_qt/configure_system.cpp
@@ -4,6 +4,7 @@
#include "citra_qt/configure_system.h"
#include "citra_qt/ui_settings.h"
+#include "core/core.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "ui_configure_system.h"
diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
index f5a2ec761..c68fe753b 100644
--- a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
@@ -18,15 +18,16 @@
#include "citra_qt/util/util.h"
#include "common/vector_math.h"
#include "video_core/debug_utils/debug_utils.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
+#include "video_core/regs.h"
+#include "video_core/texture/texture_decode.h"
namespace {
-QImage LoadTexture(const u8* src, const Pica::DebugUtils::TextureInfo& info) {
+QImage LoadTexture(const u8* src, const Pica::Texture::TextureInfo& info) {
QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
for (int y = 0; y < info.height; ++y) {
for (int x = 0; x < info.width; ++x) {
- Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info, true);
+ Math::Vec4<u8> color = Pica::Texture::LookupTexture(src, x, y, info, true);
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
}
}
@@ -36,9 +37,10 @@ QImage LoadTexture(const u8* src, const Pica::DebugUtils::TextureInfo& info) {
class TextureInfoWidget : public QWidget {
public:
- TextureInfoWidget(const u8* src, const Pica::DebugUtils::TextureInfo& info,
+ TextureInfoWidget(const u8* src, const Pica::Texture::TextureInfo& info,
QWidget* parent = nullptr)
: QWidget(parent) {
+
QLabel* image_widget = new QLabel;
QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
@@ -70,7 +72,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0:
- return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str());
+ return QString::fromLatin1(Pica::Regs::GetRegisterName(write.cmd_id));
case 1:
return QString("%1").arg(write.cmd_id, 3, 16, QLatin1Char('0'));
case 2:
@@ -121,15 +123,16 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
const unsigned int command_id =
list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
- if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
- COMMAND_IN_RANGE(command_id, texture2)) {
+ if (COMMAND_IN_RANGE(command_id, texturing.texture0) ||
+ COMMAND_IN_RANGE(command_id, texturing.texture1) ||
+ COMMAND_IN_RANGE(command_id, texturing.texture2)) {
unsigned texture_index;
- if (COMMAND_IN_RANGE(command_id, texture0)) {
+ if (COMMAND_IN_RANGE(command_id, texturing.texture0)) {
texture_index = 0;
- } else if (COMMAND_IN_RANGE(command_id, texture1)) {
+ } else if (COMMAND_IN_RANGE(command_id, texturing.texture1)) {
texture_index = 1;
- } else if (COMMAND_IN_RANGE(command_id, texture2)) {
+ } else if (COMMAND_IN_RANGE(command_id, texturing.texture2)) {
texture_index = 2;
} else {
UNREACHABLE_MSG("Unknown texture command");
@@ -144,23 +147,24 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
const unsigned int command_id =
list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
- if (COMMAND_IN_RANGE(command_id, texture0) || COMMAND_IN_RANGE(command_id, texture1) ||
- COMMAND_IN_RANGE(command_id, texture2)) {
+ if (COMMAND_IN_RANGE(command_id, texturing.texture0) ||
+ COMMAND_IN_RANGE(command_id, texturing.texture1) ||
+ COMMAND_IN_RANGE(command_id, texturing.texture2)) {
unsigned texture_index;
- if (COMMAND_IN_RANGE(command_id, texture0)) {
+ if (COMMAND_IN_RANGE(command_id, texturing.texture0)) {
texture_index = 0;
- } else if (COMMAND_IN_RANGE(command_id, texture1)) {
+ } else if (COMMAND_IN_RANGE(command_id, texturing.texture1)) {
texture_index = 1;
} else {
texture_index = 2;
}
- const auto texture = Pica::g_state.regs.GetTextures()[texture_index];
+ const auto texture = Pica::g_state.regs.texturing.GetTextures()[texture_index];
const auto config = texture.config;
const auto format = texture.format;
- const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
+ const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format);
const u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress());
new_info_widget = new TextureInfoWidget(src, info);
}
diff --git a/src/citra_qt/debugger/graphics/graphics_surface.cpp b/src/citra_qt/debugger/graphics/graphics_surface.cpp
index 4efd95d3c..47d9924e1 100644
--- a/src/citra_qt/debugger/graphics/graphics_surface.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_surface.cpp
@@ -16,8 +16,10 @@
#include "common/color.h"
#include "core/hw/gpu.h"
#include "core/memory.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_texturing.h"
+#include "video_core/texture/texture_decode.h"
#include "video_core/utils.h"
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
@@ -413,30 +415,30 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO: Store a reference to the registers in the debug context instead of accessing them
// directly...
- const auto& framebuffer = Pica::g_state.regs.framebuffer;
+ const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
surface_address = framebuffer.GetColorBufferPhysicalAddress();
surface_width = framebuffer.GetWidth();
surface_height = framebuffer.GetHeight();
switch (framebuffer.color_format) {
- case Pica::Regs::ColorFormat::RGBA8:
+ case Pica::FramebufferRegs::ColorFormat::RGBA8:
surface_format = Format::RGBA8;
break;
- case Pica::Regs::ColorFormat::RGB8:
+ case Pica::FramebufferRegs::ColorFormat::RGB8:
surface_format = Format::RGB8;
break;
- case Pica::Regs::ColorFormat::RGB5A1:
+ case Pica::FramebufferRegs::ColorFormat::RGB5A1:
surface_format = Format::RGB5A1;
break;
- case Pica::Regs::ColorFormat::RGB565:
+ case Pica::FramebufferRegs::ColorFormat::RGB565:
surface_format = Format::RGB565;
break;
- case Pica::Regs::ColorFormat::RGBA4:
+ case Pica::FramebufferRegs::ColorFormat::RGBA4:
surface_format = Format::RGBA4;
break;
@@ -449,22 +451,22 @@ void GraphicsSurfaceWidget::OnUpdate() {
}
case Source::DepthBuffer: {
- const auto& framebuffer = Pica::g_state.regs.framebuffer;
+ const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress();
surface_width = framebuffer.GetWidth();
surface_height = framebuffer.GetHeight();
switch (framebuffer.depth_format) {
- case Pica::Regs::DepthFormat::D16:
+ case Pica::FramebufferRegs::DepthFormat::D16:
surface_format = Format::D16;
break;
- case Pica::Regs::DepthFormat::D24:
+ case Pica::FramebufferRegs::DepthFormat::D24:
surface_format = Format::D24;
break;
- case Pica::Regs::DepthFormat::D24S8:
+ case Pica::FramebufferRegs::DepthFormat::D24S8:
surface_format = Format::D24X8;
break;
@@ -477,14 +479,14 @@ void GraphicsSurfaceWidget::OnUpdate() {
}
case Source::StencilBuffer: {
- const auto& framebuffer = Pica::g_state.regs.framebuffer;
+ const auto& framebuffer = Pica::g_state.regs.framebuffer.framebuffer;
surface_address = framebuffer.GetDepthBufferPhysicalAddress();
surface_width = framebuffer.GetWidth();
surface_height = framebuffer.GetHeight();
switch (framebuffer.depth_format) {
- case Pica::Regs::DepthFormat::D24S8:
+ case Pica::FramebufferRegs::DepthFormat::D24S8:
surface_format = Format::X24S8;
break;
@@ -511,8 +513,8 @@ void GraphicsSurfaceWidget::OnUpdate() {
break;
}
- const auto texture = Pica::g_state.regs.GetTextures()[texture_index];
- auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format);
+ const auto texture = Pica::g_state.regs.texturing.GetTextures()[texture_index];
+ auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
surface_address = info.physical_address;
surface_width = info.width;
@@ -567,28 +569,27 @@ void GraphicsSurfaceWidget::OnUpdate() {
surface_picture_label->show();
- unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
- unsigned stride = nibbles_per_pixel * surface_width / 2;
-
- // We handle depth formats here because DebugUtils only supports TextureFormats
if (surface_format <= Format::MaxTextureFormat) {
-
// Generate a virtual texture
- Pica::DebugUtils::TextureInfo info;
+ Pica::Texture::TextureInfo info;
info.physical_address = surface_address;
info.width = surface_width;
info.height = surface_height;
- info.format = static_cast<Pica::Regs::TextureFormat>(surface_format);
- info.stride = stride;
+ info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface_format);
+ info.SetDefaultStride();
for (unsigned int y = 0; y < surface_height; ++y) {
for (unsigned int x = 0; x < surface_width; ++x) {
- Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(buffer, x, y, info, true);
+ Math::Vec4<u8> color = Pica::Texture::LookupTexture(buffer, x, y, info, true);
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
}
}
-
} else {
+ // We handle depth formats here because DebugUtils only supports TextureFormats
+
+ // TODO(yuriks): Convert to newer tile-based addressing
+ unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
+ unsigned stride = nibbles_per_pixel * surface_width / 2;
ASSERT_MSG(nibbles_per_pixel >= 2,
"Depth decoder only supports formats with at least one byte per pixel");
@@ -689,7 +690,8 @@ void GraphicsSurfaceWidget::SaveSurface() {
unsigned int GraphicsSurfaceWidget::NibblesPerPixel(GraphicsSurfaceWidget::Format format) {
if (format <= Format::MaxTextureFormat) {
- return Pica::Regs::NibblesPerPixel(static_cast<Pica::Regs::TextureFormat>(format));
+ return Pica::TexturingRegs::NibblesPerPixel(
+ static_cast<Pica::TexturingRegs::TextureFormat>(format));
}
switch (format) {
diff --git a/src/citra_qt/debugger/graphics/graphics_tracing.cpp b/src/citra_qt/debugger/graphics/graphics_tracing.cpp
index 716ed50b8..40d5bed51 100644
--- a/src/citra_qt/debugger/graphics/graphics_tracing.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_tracing.cpp
@@ -18,7 +18,6 @@
#include "core/hw/lcd.h"
#include "core/tracer/recorder.h"
#include "nihstro/float24.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
@@ -71,8 +70,8 @@ void GraphicsTracingWidget::StartRecording() {
std::array<u32, 4 * 16> default_attributes;
for (unsigned i = 0; i < 16; ++i) {
for (unsigned comp = 0; comp < 3; ++comp) {
- default_attributes[4 * i + comp] =
- nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32());
+ default_attributes[4 * i + comp] = nihstro::to_float24(
+ Pica::g_state.input_default_attributes.attr[i][comp].ToFloat32());
}
}
diff --git a/src/citra_qt/debugger/graphics/graphics_tracing.h b/src/citra_qt/debugger/graphics/graphics_tracing.h
index 3f73bcd2e..eb1292c29 100644
--- a/src/citra_qt/debugger/graphics/graphics_tracing.h
+++ b/src/citra_qt/debugger/graphics/graphics_tracing.h
@@ -15,6 +15,9 @@ public:
explicit GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
+ void OnEmulationStarting(EmuThread* emu_thread);
+ void OnEmulationStopping();
+
private slots:
void StartRecording();
void StopRecording();
@@ -23,9 +26,6 @@ private slots:
void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
void OnResumed() override;
- void OnEmulationStarting(EmuThread* emu_thread);
- void OnEmulationStopping();
-
signals:
void SetStartTracingButtonEnabled(bool enable);
void SetStopTracingButtonEnabled(bool enable);
diff --git a/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp
index f37524190..e3f3194db 100644
--- a/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp
@@ -16,7 +16,6 @@
#include <QTreeView>
#include "citra_qt/debugger/graphics/graphics_vertex_shader.h"
#include "citra_qt/util/util.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/shader/debug_data.h"
#include "video_core/shader/shader.h"
@@ -359,7 +358,7 @@ void GraphicsVertexShaderWidget::DumpShader() {
auto& config = Pica::g_state.regs.vs;
Pica::DebugUtils::DumpShader(filename.toStdString(), config, setup,
- Pica::g_state.regs.vs_output_attributes);
+ Pica::g_state.regs.rasterizer.vs_output_attributes);
}
GraphicsVertexShaderWidget::GraphicsVertexShaderWidget(
@@ -511,7 +510,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
auto& shader_config = Pica::g_state.regs.vs;
for (auto instr : shader_setup.program_code)
info.code.push_back({instr});
- int num_attributes = Pica::g_state.regs.vertex_attributes.GetNumTotalAttributes();
+ int num_attributes = shader_config.max_input_attribute_index + 1;
for (auto pattern : shader_setup.swizzle_data)
info.swizzle_info.push_back({pattern});
@@ -522,11 +521,11 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
// Generate debug information
Pica::Shader::InterpreterEngine shader_engine;
shader_engine.SetupBatch(shader_setup, entry_point);
- debug_data = shader_engine.ProduceDebugInfo(shader_setup, input_vertex, num_attributes);
+ debug_data = shader_engine.ProduceDebugInfo(shader_setup, input_vertex, shader_config);
// Reload widget state
for (int attr = 0; attr < num_attributes; ++attr) {
- unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr);
+ unsigned source_attr = shader_config.GetRegisterForAttribute(attr);
input_data_mapping[attr]->setText(QString("-> v%1").arg(source_attr));
input_data_container[attr]->setVisible(true);
}
diff --git a/src/citra_qt/debugger/graphics/graphics_vertex_shader.h b/src/citra_qt/debugger/graphics/graphics_vertex_shader.h
index 3292573f3..c249a2ff8 100644
--- a/src/citra_qt/debugger/graphics/graphics_vertex_shader.h
+++ b/src/citra_qt/debugger/graphics/graphics_vertex_shader.h
@@ -82,7 +82,7 @@ private:
nihstro::ShaderInfo info;
Pica::Shader::DebugData<true> debug_data;
- Pica::Shader::InputVertex input_vertex;
+ Pica::Shader::AttributeBuffer input_vertex;
friend class GraphicsVertexShaderModel;
};
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp
index cee10403d..f060bbe08 100644
--- a/src/citra_qt/debugger/profiler.cpp
+++ b/src/citra_qt/debugger/profiler.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <QAction>
+#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QString>
@@ -9,121 +11,12 @@
#include "citra_qt/util/util.h"
#include "common/common_types.h"
#include "common/microprofile.h"
-#include "common/profiler_reporting.h"
// Include the implementation of the UI in this file. This isn't in microprofile.cpp because the
// non-Qt frontends don't need it (and don't implement the UI drawing hooks either).
#if MICROPROFILE_ENABLED
#define MICROPROFILEUI_IMPL 1
#include "common/microprofileui.h"
-#endif
-
-using namespace Common::Profiling;
-
-static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) {
- static auto duration_to_float = [](Duration dur) -> float {
- using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
- return std::chrono::duration_cast<FloatMs>(dur).count();
- };
-
- switch (col) {
- case 1:
- return duration_to_float(duration.avg);
- case 2:
- return duration_to_float(duration.min);
- case 3:
- return duration_to_float(duration.max);
- default:
- return QVariant();
- }
-}
-
-ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) {
- updateProfilingInfo();
-}
-
-QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
- switch (section) {
- case 0:
- return tr("Category");
- case 1:
- return tr("Avg");
- case 2:
- return tr("Min");
- case 3:
- return tr("Max");
- }
- }
-
- return QVariant();
-}
-
-QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const {
- return createIndex(row, column);
-}
-
-QModelIndex ProfilerModel::parent(const QModelIndex& child) const {
- return QModelIndex();
-}
-
-int ProfilerModel::columnCount(const QModelIndex& parent) const {
- return 4;
-}
-
-int ProfilerModel::rowCount(const QModelIndex& parent) const {
- if (parent.isValid()) {
- return 0;
- } else {
- return 2;
- }
-}
-
-QVariant ProfilerModel::data(const QModelIndex& index, int role) const {
- if (role == Qt::DisplayRole) {
- if (index.row() == 0) {
- if (index.column() == 0) {
- return tr("Frame");
- } else {
- return GetDataForColumn(index.column(), results.frame_time);
- }
- } else if (index.row() == 1) {
- if (index.column() == 0) {
- return tr("Frame (with swapping)");
- } else {
- return GetDataForColumn(index.column(), results.interframe_time);
- }
- }
- }
-
- return QVariant();
-}
-
-void ProfilerModel::updateProfilingInfo() {
- results = GetTimingResultsAggregator()->GetAggregatedResults();
- emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3));
-}
-
-ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent) {
- ui.setupUi(this);
-
- model = new ProfilerModel(this);
- ui.treeView->setModel(model);
-
- connect(this, SIGNAL(visibilityChanged(bool)), SLOT(setProfilingInfoUpdateEnabled(bool)));
- connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo()));
-}
-
-void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) {
- if (enable) {
- update_timer.start(100);
- model->updateProfilingInfo();
- } else {
- update_timer.stop();
- }
-}
-
-#if MICROPROFILE_ENABLED
class MicroProfileWidget : public QWidget {
public:
diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h
index c8912fd5a..eae1e9e3c 100644
--- a/src/citra_qt/debugger/profiler.h
+++ b/src/citra_qt/debugger/profiler.h
@@ -8,46 +8,6 @@
#include <QDockWidget>
#include <QTimer>
#include "common/microprofile.h"
-#include "common/profiler_reporting.h"
-#include "ui_profiler.h"
-
-class ProfilerModel : public QAbstractItemModel {
- Q_OBJECT
-
-public:
- explicit ProfilerModel(QObject* parent);
-
- QVariant headerData(int section, Qt::Orientation orientation,
- int role = Qt::DisplayRole) const override;
- QModelIndex index(int row, int column,
- const QModelIndex& parent = QModelIndex()) const override;
- QModelIndex parent(const QModelIndex& child) const override;
- int columnCount(const QModelIndex& parent = QModelIndex()) const override;
- int rowCount(const QModelIndex& parent = QModelIndex()) const override;
- QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
-
-public slots:
- void updateProfilingInfo();
-
-private:
- Common::Profiling::AggregatedFrameResult results;
-};
-
-class ProfilerWidget : public QDockWidget {
- Q_OBJECT
-
-public:
- explicit ProfilerWidget(QWidget* parent = nullptr);
-
-private slots:
- void setProfilingInfoUpdateEnabled(bool enable);
-
-private:
- Ui::Profiler ui;
- ProfilerModel* model;
-
- QTimer update_timer;
-};
class MicroProfileDialog : public QWidget {
Q_OBJECT
diff --git a/src/citra_qt/debugger/profiler.ui b/src/citra_qt/debugger/profiler.ui
deleted file mode 100644
index d3c9a9a1f..000000000
--- a/src/citra_qt/debugger/profiler.ui
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Profiler</class>
- <widget class="QDockWidget" name="Profiler">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>400</width>
- <height>300</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Profiler</string>
- </property>
- <widget class="QWidget" name="dockWidgetContents">
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QTreeView" name="treeView">
- <property name="alternatingRowColors">
- <bool>true</bool>
- </property>
- <property name="uniformRowHeights">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 09469f3c5..a9ec9e830 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <QFileInfo>
#include <QHeaderView>
#include <QMenu>
#include <QThreadPool>
@@ -38,11 +39,13 @@ GameList::GameList(QWidget* parent) : QWidget{parent} {
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
+ connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
// 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.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
+ layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(tree_view);
setLayout(layout);
}
@@ -102,6 +105,12 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
item_model->removeRows(0, item_model->rowCount());
emit ShouldCancelWorker();
+
+ auto watch_dirs = watcher.directories();
+ if (!watch_dirs.isEmpty()) {
+ watcher.removePaths(watch_dirs);
+ }
+ UpdateWatcherList(dir_path.toStdString(), deep_scan ? 256 : 0);
GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
@@ -131,6 +140,53 @@ void GameList::LoadInterfaceLayout() {
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
}
+const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf",
+ "cci", "cxi", "app"};
+
+static bool HasSupportedFileExtension(const std::string& file_name) {
+ QFileInfo file = QFileInfo(file_name.c_str());
+ return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
+}
+
+void GameList::RefreshGameDirectory() {
+ if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
+ LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
+ PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
+ }
+}
+
+/**
+ * Adds the game list folder to the QFileSystemWatcher to check for updates.
+ *
+ * The file watcher will fire off an update to the game list when a change is detected in the game
+ * list folder.
+ *
+ * Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and
+ * this function is fast enough to not stall the UI thread. If performance is an issue, it should
+ * be moved to another thread and properly locked to prevent concurrency issues.
+ *
+ * @param dir folder to check for changes in
+ * @param recursion 0 if recursion is disabled. Any positive number passed to this will add each
+ * directory recursively to the watcher and will update the file list if any of the folders
+ * change. The number determines how deep the recursion should traverse.
+ */
+void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) {
+ const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
+ const std::string& virtual_name) -> bool {
+ std::string physical_name = directory + DIR_SEP + virtual_name;
+
+ if (FileUtil::IsDirectory(physical_name)) {
+ UpdateWatcherList(physical_name, recursion - 1);
+ }
+ return true;
+ };
+
+ watcher.addPath(QString::fromStdString(dir));
+ if (recursion > 0) {
+ FileUtil::ForeachDirectoryEntry(nullptr, dir, callback);
+ }
+}
+
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
const std::string& virtual_name) -> bool {
@@ -139,7 +195,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
if (stop_processing)
return false; // Breaks the callback loop.
- if (!FileUtil::IsDirectory(physical_name)) {
+ if (!FileUtil::IsDirectory(physical_name) && HasSupportedFileExtension(physical_name)) {
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
if (!loader)
return true;
@@ -173,6 +229,6 @@ void GameListWorker::run() {
}
void GameListWorker::Cancel() {
- disconnect(this, nullptr, nullptr, nullptr);
+ this->disconnect();
stop_processing = true;
}
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 1abf10051..b141fa3a5 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -4,6 +4,7 @@
#pragma once
+#include <QFileSystemWatcher>
#include <QModelIndex>
#include <QSettings>
#include <QStandardItem>
@@ -33,6 +34,8 @@ public:
void SaveInterfaceLayout();
void LoadInterfaceLayout();
+ static const QStringList supported_file_extensions;
+
signals:
void GameChosen(QString game_path);
void ShouldCancelWorker();
@@ -44,8 +47,11 @@ private:
void DonePopulating();
void PopupContextMenu(const QPoint& menu_location);
+ void UpdateWatcherList(const std::string& path, unsigned int recursion);
+ void RefreshGameDirectory();
QTreeView* tree_view = nullptr;
QStandardItemModel* item_model = nullptr;
GameListWorker* current_worker = nullptr;
+ QFileSystemWatcher watcher;
};
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h
index a15f06c5f..3c11b6dd1 100644
--- a/src/citra_qt/game_list_p.h
+++ b/src/citra_qt/game_list_p.h
@@ -16,8 +16,8 @@
#include "video_core/utils.h"
/**
- * Gets game icon from SMDH
- * @param sdmh SMDH data
+ * Gets the game icon from SMDH data.
+ * @param smdh SMDH data
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
* @return QPixmap game icon
*/
@@ -42,8 +42,8 @@ static QPixmap GetDefaultIcon(bool large) {
}
/**
- * Gets the short game title fromn SMDH
- * @param sdmh SMDH data
+ * Gets the short game title from SMDH data.
+ * @param smdh SMDH data
* @param language title language
* @return QString short title
*/
diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h
index 46f48c2d8..a4ccc193b 100644
--- a/src/citra_qt/hotkeys.h
+++ b/src/citra_qt/hotkeys.h
@@ -29,6 +29,8 @@ void RegisterHotkey(const QString& group, const QString& action,
/**
* Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots.
*
+ * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
+ * @param action Name of the action (e.g. "Start Emulation", "Load Image").
* @param widget Parent widget of the returned QShortcut.
* @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
* will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent.
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index f765c0147..2723a0217 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -54,28 +54,30 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
Pica::g_debug_context = Pica::DebugContext::Construct();
-
+ setAcceptDrops(true);
ui.setupUi(this);
statusBar()->hide();
InitializeWidgets();
- InitializeDebugMenuActions();
+ InitializeDebugWidgets();
InitializeRecentFileMenuActions();
InitializeHotkeys();
SetDefaultUIGeometry();
RestoreUIState();
+ ConnectMenuEvents();
ConnectWidgetEvents();
- setWindowTitle(QString("Citra | %1-%2").arg(Common::g_scm_branch, Common::g_scm_desc));
+ setWindowTitle(QString("Citra %1| %2-%3")
+ .arg(Common::g_build_name, 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());
+ BootGame(args[1]);
}
}
@@ -94,73 +96,99 @@ void GMainWindow::InitializeWidgets() {
game_list = new GameList();
ui.horizontalLayout->addWidget(game_list);
- profilerWidget = new ProfilerWidget(this);
- addDockWidget(Qt::BottomDockWidgetArea, profilerWidget);
- profilerWidget->hide();
+ // Create status bar
+ emu_speed_label = new QLabel();
+ emu_speed_label->setToolTip(tr("Current emulation speed. Values higher or lower than 100% "
+ "indicate emulation is running faster or slower than a 3DS."));
+ game_fps_label = new QLabel();
+ game_fps_label->setToolTip(tr("How many frames per second the game is currently displaying. "
+ "This will vary from game to game and scene to scene."));
+ emu_frametime_label = new QLabel();
+ emu_frametime_label->setToolTip(
+ tr("Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For "
+ "full-speed emulation this should be at most 16.67 ms."));
+
+ for (auto& label : {emu_speed_label, game_fps_label, emu_frametime_label}) {
+ label->setVisible(false);
+ label->setFrameStyle(QFrame::NoFrame);
+ label->setContentsMargins(4, 0, 4, 0);
+ statusBar()->addPermanentWidget(label);
+ }
+ statusBar()->setVisible(true);
+}
+
+void GMainWindow::InitializeDebugWidgets() {
+ connect(ui.action_Create_Pica_Surface_Viewer, &QAction::triggered, this,
+ &GMainWindow::OnCreateGraphicsSurfaceViewer);
+
+ QMenu* debug_menu = ui.menu_View_Debugging;
#if MICROPROFILE_ENABLED
microProfileDialog = new MicroProfileDialog(this);
microProfileDialog->hide();
+ debug_menu->addAction(microProfileDialog->toggleViewAction());
#endif
disasmWidget = new DisassemblerWidget(this, emu_thread.get());
addDockWidget(Qt::BottomDockWidgetArea, disasmWidget);
disasmWidget->hide();
+ debug_menu->addAction(disasmWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, disasmWidget,
+ &DisassemblerWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, disasmWidget,
+ &DisassemblerWidget::OnEmulationStopping);
registersWidget = new RegistersWidget(this);
addDockWidget(Qt::RightDockWidgetArea, registersWidget);
registersWidget->hide();
+ debug_menu->addAction(registersWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, registersWidget,
+ &RegistersWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, registersWidget,
+ &RegistersWidget::OnEmulationStopping);
callstackWidget = new CallstackWidget(this);
addDockWidget(Qt::RightDockWidgetArea, callstackWidget);
callstackWidget->hide();
+ debug_menu->addAction(callstackWidget->toggleViewAction());
graphicsWidget = new GPUCommandStreamWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
graphicsWidget->hide();
+ debug_menu->addAction(graphicsWidget->toggleViewAction());
graphicsCommandsWidget = new GPUCommandListWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
graphicsCommandsWidget->hide();
+ debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
graphicsBreakpointsWidget->hide();
+ debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
graphicsVertexShaderWidget->hide();
+ debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
graphicsTracingWidget->hide();
+ debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, graphicsTracingWidget,
+ &GraphicsTracingWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget,
+ &GraphicsTracingWidget::OnEmulationStopping);
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);
- debug_menu->addSeparator();
- debug_menu->addAction(profilerWidget->toggleViewAction());
-#if MICROPROFILE_ENABLED
- debug_menu->addAction(microProfileDialog->toggleViewAction());
-#endif
- debug_menu->addAction(disasmWidget->toggleViewAction());
- debug_menu->addAction(registersWidget->toggleViewAction());
- debug_menu->addAction(callstackWidget->toggleViewAction());
- debug_menu->addAction(graphicsWidget->toggleViewAction());
- debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
- debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
- debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
- debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
debug_menu->addAction(waitTreeWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, waitTreeWidget,
+ &WaitTreeWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
+ &WaitTreeWidget::OnEmulationStopping);
}
void GMainWindow::InitializeRecentFileMenuActions() {
@@ -215,41 +243,46 @@ void GMainWindow::RestoreUIState() {
ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
ToggleWindowMode();
- ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar);
- OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked());
+ ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar);
+ OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
+
+ ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
+ statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
}
void GMainWindow::ConnectWidgetEvents() {
- connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)),
- Qt::DirectConnection);
+ connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)));
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);
- connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
- connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this,
- SLOT(OnMenuSelectGameListRoot()));
- connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
- connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
- connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
- connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
-
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
+ SLOT(OnGameListOpenSaveFolder(u64)));
+
connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), waitTreeWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), waitTreeWidget, SLOT(OnEmulationStopping()));
+
+ connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);
+}
+
+void GMainWindow::ConnectMenuEvents() {
+ // File
+ connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
+ connect(ui.action_Load_Symbol_Map, &QAction::triggered, this,
+ &GMainWindow::OnMenuLoadSymbolMap);
+ connect(ui.action_Select_Game_List_Root, &QAction::triggered, this,
+ &GMainWindow::OnMenuSelectGameListRoot);
+ connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
+
+ // Emulation
+ connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
+ connect(ui.action_Pause, &QAction::triggered, this, &GMainWindow::OnPauseGame);
+ connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame);
+ connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
+
+ // View
+ connect(ui.action_Single_Window_Mode, &QAction::triggered, this,
+ &GMainWindow::ToggleWindowMode);
+ connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this,
+ &GMainWindow::OnDisplayTitleBars);
+ connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
}
void GMainWindow::OnDisplayTitleBars(bool show) {
@@ -272,7 +305,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
}
}
-bool GMainWindow::LoadROM(const std::string& filename) {
+bool GMainWindow::LoadROM(const QString& filename) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
@@ -290,12 +323,13 @@ bool GMainWindow::LoadROM(const std::string& filename) {
Core::System& system{Core::System::GetInstance()};
- const Core::System::ResultStatus result{system.Load(render_window, filename)};
+ const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
if (result != Core::System::ResultStatus::Success) {
switch (result) {
case Core::System::ResultStatus::ErrorGetLoader:
- LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str());
+ LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!",
+ filename.toStdString().c_str());
QMessageBox::critical(this, tr("Error while loading ROM!"),
tr("The ROM format is not supported."));
break;
@@ -335,7 +369,7 @@ bool GMainWindow::LoadROM(const std::string& filename) {
return true;
}
-void GMainWindow::BootGame(const std::string& filename) {
+void GMainWindow::BootGame(const QString& filename) {
LOG_INFO(Frontend, "Citra starting...");
StoreRecentFile(filename); // Put the filename on top of the list
@@ -374,6 +408,8 @@ void GMainWindow::BootGame(const std::string& filename) {
if (ui.action_Single_Window_Mode->isChecked()) {
game_list->hide();
}
+ status_bar_update_timer.start(2000);
+
render_window->show();
render_window->setFocus();
@@ -408,11 +444,17 @@ void GMainWindow::ShutdownGame() {
render_window->hide();
game_list->show();
+ // Disable status bar updates
+ status_bar_update_timer.stop();
+ emu_speed_label->setVisible(false);
+ game_fps_label->setVisible(false);
+ emu_frametime_label->setVisible(false);
+
emulation_running = false;
}
-void GMainWindow::StoreRecentFile(const std::string& filename) {
- UISettings::values.recent_files.prepend(QString::fromStdString(filename));
+void GMainWindow::StoreRecentFile(const QString& filename) {
+ UISettings::values.recent_files.prepend(filename);
UISettings::values.recent_files.removeDuplicates();
while (UISettings::values.recent_files.size() > max_recent_files_item) {
UISettings::values.recent_files.removeLast();
@@ -447,7 +489,7 @@ void GMainWindow::UpdateRecentFiles() {
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
- BootGame(game_path.toStdString());
+ BootGame(game_path);
}
void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) {
@@ -466,19 +508,25 @@ void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) {
}
void GMainWindow::OnMenuLoadFile() {
- QString filename =
- QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path,
- tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
+ QString extensions;
+ for (const auto& piece : game_list->supported_file_extensions)
+ extensions += "*." + piece + " ";
+
+ QString file_filter = tr("3DS Executable") + " (" + extensions + ")";
+ file_filter += ";;" + tr("All Files (*.*)");
+
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
+ UISettings::values.roms_path, file_filter);
if (!filename.isEmpty()) {
UISettings::values.roms_path = QFileInfo(filename).path();
- BootGame(filename.toStdString());
+ BootGame(filename);
}
}
void GMainWindow::OnMenuLoadSymbolMap() {
QString filename = QFileDialog::getOpenFileName(
- this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
+ this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol Map (*.*)"));
if (!filename.isEmpty()) {
UISettings::values.symbols_path = QFileInfo(filename).path();
@@ -501,7 +549,7 @@ void GMainWindow::OnMenuRecentFile() {
QString filename = action->data().toString();
QFileInfo file_info(filename);
if (file_info.exists()) {
- BootGame(filename.toStdString());
+ BootGame(filename);
} else {
// Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"),
@@ -564,7 +612,6 @@ void GMainWindow::OnConfigure() {
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.applyConfiguration();
- render_window->ReloadSetKeymaps();
config->Save();
}
}
@@ -581,6 +628,23 @@ void GMainWindow::OnCreateGraphicsSurfaceViewer() {
graphicsSurfaceViewerWidget->show();
}
+void GMainWindow::UpdateStatusBar() {
+ if (emu_thread == nullptr) {
+ status_bar_update_timer.stop();
+ return;
+ }
+
+ auto results = Core::System::GetInstance().GetAndResetPerfStats();
+
+ emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
+ game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0));
+ emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
+
+ emu_speed_label->setVisible(true);
+ game_fps_label->setVisible(true);
+ emu_frametime_label->setVisible(true);
+}
+
bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true;
@@ -605,7 +669,8 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
UISettings::values.microprofile_visible = microProfileDialog->isVisible();
#endif
UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
- UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked();
+ UISettings::values.display_titlebar = ui.action_Display_Dock_Widget_Headers->isChecked();
+ UISettings::values.show_status_bar = ui.action_Show_Status_Bar->isChecked();
UISettings::values.first_start = false;
game_list->SaveInterfaceLayout();
@@ -620,6 +685,40 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event);
}
+static bool IsSingleFileDropEvent(QDropEvent* event) {
+ const QMimeData* mimeData = event->mimeData();
+ return mimeData->hasUrls() && mimeData->urls().length() == 1;
+}
+
+void GMainWindow::dropEvent(QDropEvent* event) {
+ if (IsSingleFileDropEvent(event) && ConfirmChangeGame()) {
+ const QMimeData* mimeData = event->mimeData();
+ QString filename = mimeData->urls().at(0).toLocalFile();
+ BootGame(filename);
+ }
+}
+
+void GMainWindow::dragEnterEvent(QDragEnterEvent* event) {
+ if (IsSingleFileDropEvent(event)) {
+ event->acceptProposedAction();
+ }
+}
+
+void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
+ event->acceptProposedAction();
+}
+
+bool GMainWindow::ConfirmChangeGame() {
+ if (emu_thread == nullptr)
+ return true;
+
+ auto answer = QMessageBox::question(
+ this, tr("Citra"),
+ tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ return answer != QMessageBox::No;
+}
+
#ifdef main
#undef main
#endif
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index a2fd45c47..ec841eaa5 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -64,7 +64,7 @@ signals:
private:
void InitializeWidgets();
- void InitializeDebugMenuActions();
+ void InitializeDebugWidgets();
void InitializeRecentFileMenuActions();
void InitializeHotkeys();
@@ -72,15 +72,10 @@ private:
void RestoreUIState();
void ConnectWidgetEvents();
+ void ConnectMenuEvents();
- /**
- * Initializes the emulation system.
- * @param system_mode The system mode with which to intialize the kernel.
- * @returns Whether the system was properly initialized.
- */
- bool InitializeSystem(u32 system_mode);
- bool LoadROM(const std::string& filename);
- void BootGame(const std::string& filename);
+ bool LoadROM(const QString& filename);
+ void BootGame(const QString& filename);
void ShutdownGame();
/**
@@ -94,7 +89,7 @@ private:
*
* @param filename the filename to store
*/
- void StoreRecentFile(const std::string& filename);
+ void StoreRecentFile(const QString& filename);
/**
* Updates the recent files menu.
@@ -110,6 +105,7 @@ private:
* @return true if the user confirmed
*/
bool ConfirmClose();
+ bool ConfirmChangeGame();
void closeEvent(QCloseEvent* event) override;
private slots:
@@ -131,17 +127,26 @@ private slots:
void OnCreateGraphicsSurfaceViewer();
private:
+ void UpdateStatusBar();
+
Ui::MainWindow ui;
GRenderWindow* render_window;
GameList* game_list;
+ // Status bar elements
+ QLabel* emu_speed_label = nullptr;
+ QLabel* game_fps_label = nullptr;
+ QLabel* emu_frametime_label = nullptr;
+ QTimer status_bar_update_timer;
+
std::unique_ptr<Config> config;
// Whether emulation is currently running in Citra.
bool emulation_running = false;
std::unique_ptr<EmuThread> emu_thread;
+ // Debugger panes
ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog;
DisassemblerWidget* disasmWidget;
@@ -155,6 +160,11 @@ private:
WaitTreeWidget* waitTreeWidget;
QAction* actions_recent_files[max_recent_files_item];
+
+protected:
+ void dropEvent(QDropEvent* event) override;
+ void dragEnterEvent(QDragEnterEvent* event) override;
+ void dragMoveEvent(QDragMoveEvent* event) override;
};
#endif // _CITRA_QT_MAIN_HXX_
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index adfa3689e..47dbb6ef7 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -79,8 +79,17 @@
<property name="title">
<string>&amp;View</string>
</property>
+ <widget class="QMenu" name="menu_View_Debugging">
+ <property name="title">
+ <string>Debugging</string>
+ </property>
+ <addaction name="action_Create_Pica_Surface_Viewer"/>
+ <addaction name="separator"/>
+ </widget>
<addaction name="action_Single_Window_Mode"/>
- <addaction name="actionDisplay_widget_title_bars"/>
+ <addaction name="action_Display_Dock_Widget_Headers"/>
+ <addaction name="action_Show_Status_Bar"/>
+ <addaction name="menu_View_Debugging"/>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
@@ -93,7 +102,6 @@
<addaction name="menu_View"/>
<addaction name="menu_Help"/>
</widget>
- <widget class="QStatusBar" name="statusbar"/>
<action name="action_Load_File">
<property name="text">
<string>Load File...</string>
@@ -151,7 +159,7 @@
<string>Configure...</string>
</property>
</action>
- <action name="actionDisplay_widget_title_bars">
+ <action name="action_Display_Dock_Widget_Headers">
<property name="checkable">
<bool>true</bool>
</property>
@@ -159,6 +167,14 @@
<string>Display Dock Widget Headers</string>
</property>
</action>
+ <action name="action_Show_Status_Bar">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Show Status Bar</string>
+ </property>
+ </action>
<action name="action_Select_Game_List_Root">
<property name="text">
<string>Select Game Directory...</string>
@@ -167,44 +183,11 @@
<string>Selects a folder to display in the game list</string>
</property>
</action>
+ <action name="action_Create_Pica_Surface_Viewer">
+ <property name="text">
+ <string>Create Pica Surface Viewer</string>
+ </property>
+ </action>
</widget>
<resources/>
- <connections>
- <connection>
- <sender>action_Exit</sender>
- <signal>triggered()</signal>
- <receiver>MainWindow</receiver>
- <slot>close()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>-1</x>
- <y>-1</y>
- </hint>
- <hint type="destinationlabel">
- <x>367</x>
- <y>314</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>actionDisplay_widget_title_bars</sender>
- <signal>triggered(bool)</signal>
- <receiver>MainWindow</receiver>
- <slot>OnDisplayTitleBars(bool)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>-1</x>
- <y>-1</y>
- </hint>
- <hint type="destinationlabel">
- <x>540</x>
- <y>364</y>
- </hint>
- </hints>
- </connection>
- </connections>
- <slots>
- <slot>OnConfigure()</slot>
- <slot>OnDisplayTitleBars(bool)</slot>
- </slots>
</ui>
diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h
index ed7fdff7e..6408ece2b 100644
--- a/src/citra_qt/ui_settings.h
+++ b/src/citra_qt/ui_settings.h
@@ -27,6 +27,7 @@ struct Values {
bool single_window_mode;
bool display_titlebar;
+ bool show_status_bar;
bool confirm_before_closing;
bool first_start;