diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 4704b340b2ed..1e4e6f403c95 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -541,6 +541,8 @@ add_library(core SysConf.h System.cpp System.h + TimePlayed.cpp + TimePlayed.h TitleDatabase.cpp TitleDatabase.h WC24PatchEngine.cpp diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 0442347fdd07..9dff1ab89fec 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -267,6 +267,7 @@ const Info MAIN_RENDER_WINDOW_AUTOSIZE{{System::Main, "Display", "RenderWi false}; const Info MAIN_KEEP_WINDOW_ON_TOP{{System::Main, "Display", "KeepWindowOnTop"}, false}; const Info MAIN_DISABLE_SCREENSAVER{{System::Main, "Display", "DisableScreenSaver"}, true}; +const Info MAIN_TIME_TRACKING{{System::Main, "Display", "TimeTracking"}, true}; // Main.DSP @@ -459,6 +460,8 @@ const Info MAIN_GAMELIST_COLUMN_BLOCK_SIZE{{System::Main, "GameList", "Col false}; const Info MAIN_GAMELIST_COLUMN_COMPRESSION{{System::Main, "GameList", "ColumnCompression"}, false}; +const Info MAIN_GAMELIST_COLUMN_TIME_PLAYED{{System::Main, "GameList", "ColumnTimePlayed"}, + true}; const Info MAIN_GAMELIST_COLUMN_TAGS{{System::Main, "GameList", "ColumnTags"}, false}; // Main.FifoPlayer diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index b6e8f966c793..8764a5d91c7b 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -186,6 +186,7 @@ extern const Info MAIN_RENDER_WINDOW_HEIGHT; extern const Info MAIN_RENDER_WINDOW_AUTOSIZE; extern const Info MAIN_KEEP_WINDOW_ON_TOP; extern const Info MAIN_DISABLE_SCREENSAVER; +extern const Info MAIN_TIME_TRACKING; // Main.General @@ -295,6 +296,7 @@ extern const Info MAIN_GAMELIST_COLUMN_FILE_SIZE; extern const Info MAIN_GAMELIST_COLUMN_FILE_FORMAT; extern const Info MAIN_GAMELIST_COLUMN_BLOCK_SIZE; extern const Info MAIN_GAMELIST_COLUMN_COMPRESSION; +extern const Info MAIN_GAMELIST_COLUMN_TIME_PLAYED; extern const Info MAIN_GAMELIST_COLUMN_TAGS; // Main.FifoPlayer diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 21d722290ee4..5a123c87cd53 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -98,14 +99,52 @@ void SConfig::LoadSettings() Config::Load(); } +const std::string SConfig::GetGameID() const +{ + std::lock_guard lock(m_metadata_lock); + return m_game_id; +} + +const std::string SConfig::GetGameTDBID() const +{ + std::lock_guard lock(m_metadata_lock); + return m_gametdb_id; +} + +const std::string SConfig::GetTitleName() const +{ + std::lock_guard lock(m_metadata_lock); + return m_title_name; +} + +const std::string SConfig::GetTitleDescription() const +{ + std::lock_guard lock(m_metadata_lock); + return m_title_description; +} + +u64 SConfig::GetTitleID() const +{ + std::lock_guard lock(m_metadata_lock); + return m_title_id; +} + +u16 SConfig::GetRevision() const +{ + std::lock_guard lock(m_metadata_lock); + return m_revision; +} + void SConfig::ResetRunningGameMetadata() { + std::lock_guard lock(m_metadata_lock); SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown); } void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition) { + std::lock_guard lock(m_metadata_lock); if (partition == volume.GetGamePartition()) { SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(), @@ -122,6 +161,7 @@ void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform) { + std::lock_guard lock(m_metadata_lock); const u64 tmd_title_id = tmd.GetTitleId(); // If we're launching a disc game, we want to read the revision from @@ -139,12 +179,14 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat void SConfig::SetRunningGameMetadata(const std::string& game_id) { + std::lock_guard lock(m_metadata_lock); SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown); } void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, u64 title_id, u16 revision, DiscIO::Region region) { + std::lock_guard lock(m_metadata_lock); const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id || m_title_id != title_id || m_revision != revision; m_game_id = game_id; diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 999cc9f86cff..d21b087a51f8 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -58,15 +59,16 @@ struct SConfig std::string m_strSRAM; std::string m_debugger_game_id; + // TODO: remove this as soon as the ticket view hack in IOS/ES/Views is dropped. bool m_disc_booted_from_game_list = false; - const std::string& GetGameID() const { return m_game_id; } - const std::string& GetGameTDBID() const { return m_gametdb_id; } - const std::string& GetTitleName() const { return m_title_name; } - const std::string& GetTitleDescription() const { return m_title_description; } - u64 GetTitleID() const { return m_title_id; } - u16 GetRevision() const { return m_revision; } + const std::string GetGameID() const; + const std::string GetGameTDBID() const; + const std::string GetTitleName() const; + const std::string GetTitleDescription() const; + u64 GetTitleID() const; + u16 GetRevision() const; void ResetRunningGameMetadata(); void SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition); void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform); @@ -114,6 +116,7 @@ struct SConfig u64 title_id, u16 revision, DiscIO::Region region); static SConfig* m_Instance; + mutable std::recursive_mutex m_metadata_lock; std::string m_game_id; std::string m_gametdb_id; diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp index 1eae912476c5..36a7b208ecad 100644 --- a/Source/Core/Core/HW/CPU.cpp +++ b/Source/Core/Core/HW/CPU.cpp @@ -10,12 +10,16 @@ #include "AudioCommon/AudioCommon.h" #include "Common/CommonTypes.h" #include "Common/Event.h" +#include "Common/Timer.h" #include "Core/CPUThreadConfigCallback.h" +#include "Core/Config/MainSettings.h" +#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Host.h" #include "Core/PowerPC/GDBStub.h" #include "Core/PowerPC/PowerPC.h" #include "Core/System.h" +#include "Core/TimePlayed.h" #include "VideoCommon/Fifo.h" namespace CPU @@ -63,6 +67,40 @@ void CPUManager::ExecutePendingJobs(std::unique_lock& state_lock) } } +void CPUManager::StartTimePlayedTimer() +{ + // Steady clock for greater accuracy of timing + std::chrono::steady_clock timer; + auto prev_time = timer.now(); + + while (true) + { + const std::string game_id = SConfig::GetInstance().GetGameID(); + TimePlayed time_played(game_id); + auto curr_time = timer.now(); + + // Check that emulation is not paused + // If the emulation is paused, wait for SetStepping() to reactivate + if (m_state == State::Running) + { + auto diff_time = std::chrono::duration_cast(curr_time - prev_time); + time_played.AddTime(diff_time); + } + else if (m_state == State::Stepping) + { + m_time_played_finish_sync.Wait(); + curr_time = timer.now(); + } + + prev_time = curr_time; + + if (m_state == State::PowerDown) + return; + + m_time_played_finish_sync.WaitFor(std::chrono::seconds(30)); + } +} + void CPUManager::Run() { auto& power_pc = m_system.GetPowerPC(); @@ -71,6 +109,13 @@ void CPUManager::Run() // We can't rely on PowerPC::Init doing it, since it's called from EmuThread. PowerPC::RoundingModeUpdated(power_pc.GetPPCState()); + // Start a separate time tracker thread + std::thread timing; + if (Config::Get(Config::MAIN_TIME_TRACKING)) + { + timing = std::thread(&CPUManager::StartTimePlayedTimer, this); + } + std::unique_lock state_lock(m_state_change_lock); while (m_state != State::PowerDown) { @@ -165,6 +210,13 @@ void CPUManager::Run() break; } } + + if (timing.joinable()) + { + m_time_played_finish_sync.Set(); + timing.join(); + } + state_lock.unlock(); Host_UpdateDisasmDialog(); } @@ -266,6 +318,7 @@ void CPUManager::SetStepping(bool stepping) else if (SetStateLocked(State::Running)) { m_state_cpu_cvar.notify_one(); + m_time_played_finish_sync.Set(); RunAdjacentSystems(true); } } diff --git a/Source/Core/Core/HW/CPU.h b/Source/Core/Core/HW/CPU.h index 57e8a31a29ea..b73471136d14 100644 --- a/Source/Core/Core/HW/CPU.h +++ b/Source/Core/Core/HW/CPU.h @@ -8,6 +8,8 @@ #include #include +#include "Common/Event.h" + namespace Common { class Event; @@ -102,6 +104,7 @@ class CPUManager private: void FlushStepSyncEventLocked(); void ExecutePendingJobs(std::unique_lock& state_lock); + void StartTimePlayedTimer(); void RunAdjacentSystems(bool running); bool SetStateLocked(State s); @@ -133,6 +136,7 @@ class CPUManager bool m_state_cpu_step_instruction = false; Common::Event* m_state_cpu_step_instruction_sync = nullptr; std::queue> m_pending_jobs; + Common::Event m_time_played_finish_sync; Core::System& m_system; }; diff --git a/Source/Core/Core/TimePlayed.cpp b/Source/Core/Core/TimePlayed.cpp new file mode 100644 index 000000000000..57b577257a21 --- /dev/null +++ b/Source/Core/Core/TimePlayed.cpp @@ -0,0 +1,65 @@ +#pragma once + +#include + +#include "Common/FileUtil.h" +#include "Common/IniFile.h" +#include "Common/NandPaths.h" +#include "TimePlayed.h" + +// used for QT interface - general access to time played for games +TimePlayed::TimePlayed() +{ + m_game_id = ""; + m_ini_path = File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini"; + m_ini.Load(m_ini_path); + m_time_list = m_ini.GetOrCreateSection("Time Played"); +} + +TimePlayed::TimePlayed(std::string game_id) +{ + // filter for unsafe characters + m_game_id = Common::EscapeFileName(game_id); + m_ini_path = File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini"; + m_ini.Load(m_ini_path); + m_time_list = m_ini.GetOrCreateSection("Time Played"); +} + +void TimePlayed::AddTime(std::chrono::milliseconds time_emulated) +{ + if (m_game_id == "") + { + return; + } + + u64 previous_time; + m_time_list->Get(m_game_id, &previous_time); + m_time_list->Set(m_game_id, previous_time + u64(time_emulated.count())); + m_ini.Save(m_ini_path); +} + +std::chrono::milliseconds TimePlayed::GetTimePlayed() const +{ + if (m_game_id == "") + { + return std::chrono::milliseconds(0); + } + + u64 previous_time; + m_time_list->Get(m_game_id, &previous_time); + return std::chrono::milliseconds(previous_time); +} + +std::chrono::milliseconds TimePlayed::GetTimePlayed(std::string game_id) const +{ + std::string filtered_game_id = Common::EscapeFileName(game_id); + u64 previous_time; + m_time_list->Get(filtered_game_id, &previous_time); + return std::chrono::milliseconds(previous_time); +} + +void TimePlayed::Reload() +{ + m_ini.Load(m_ini_path); + m_time_list = m_ini.GetOrCreateSection("Time Played"); +} diff --git a/Source/Core/Core/TimePlayed.h b/Source/Core/Core/TimePlayed.h new file mode 100644 index 000000000000..282e4a02f583 --- /dev/null +++ b/Source/Core/Core/TimePlayed.h @@ -0,0 +1,24 @@ +#pragma once +#include "Common/CommonTypes.h" +#include "Common/IniFile.h" + +class TimePlayed +{ +public: + TimePlayed(); + TimePlayed(std::string game_id); + + void AddTime(std::chrono::milliseconds time_emulated); + + std::chrono::milliseconds GetTimePlayed() const; + std::chrono::milliseconds GetTimePlayed(std::string game_id) const; + + void Reload(); + +private: + std::string m_game_id; + Common::IniFile m_ini; + std::string m_ini_path; + + Common::IniFile::Section* m_time_list; +}; diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index e41e9fc26a09..367c347213d2 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -459,6 +459,7 @@ + @@ -1125,6 +1126,7 @@ + diff --git a/Source/Core/DolphinQt/GameList/GameList.cpp b/Source/Core/DolphinQt/GameList/GameList.cpp index 782492539615..a4514a044934 100644 --- a/Source/Core/DolphinQt/GameList/GameList.cpp +++ b/Source/Core/DolphinQt/GameList/GameList.cpp @@ -208,6 +208,7 @@ void GameList::MakeListView() SetResizeMode(Column::FileFormat, Mode::Fixed); SetResizeMode(Column::BlockSize, Mode::Fixed); SetResizeMode(Column::Compression, Mode::Fixed); + SetResizeMode(Column::TimePlayed, Mode::Interactive); SetResizeMode(Column::Tags, Mode::Interactive); // Cells have 3 pixels of padding, so the width of these needs to be image width + 6. Banners @@ -273,6 +274,7 @@ void GameList::UpdateColumnVisibility() SetVisiblity(Column::FileFormat, Config::Get(Config::MAIN_GAMELIST_COLUMN_FILE_FORMAT)); SetVisiblity(Column::BlockSize, Config::Get(Config::MAIN_GAMELIST_COLUMN_BLOCK_SIZE)); SetVisiblity(Column::Compression, Config::Get(Config::MAIN_GAMELIST_COLUMN_COMPRESSION)); + SetVisiblity(Column::TimePlayed, Config::Get(Config::MAIN_GAMELIST_COLUMN_TIME_PLAYED)); SetVisiblity(Column::Tags, Config::Get(Config::MAIN_GAMELIST_COLUMN_TAGS)); } @@ -1005,6 +1007,7 @@ void GameList::OnColumnVisibilityToggled(const QString& row, bool visible) {tr("File Format"), Column::FileFormat}, {tr("Block Size"), Column::BlockSize}, {tr("Compression"), Column::Compression}, + {tr("Time Played"), Column::TimePlayed}, {tr("Tags"), Column::Tags}, }; diff --git a/Source/Core/DolphinQt/GameList/GameListModel.cpp b/Source/Core/DolphinQt/GameList/GameListModel.cpp index c70a870c2310..967fef4f465f 100644 --- a/Source/Core/DolphinQt/GameList/GameListModel.cpp +++ b/Source/Core/DolphinQt/GameList/GameListModel.cpp @@ -9,6 +9,8 @@ #include #include "Core/Config/MainSettings.h" +#include "Core/Core.h" +#include "Core/TimePlayed.h" #include "DiscIO/Enums.h" @@ -32,6 +34,8 @@ GameListModel::GameListModel(QObject* parent) : QAbstractTableModel(parent) &GameTracker::RefreshAll); connect(&Settings::Instance(), &Settings::TitleDBReloadRequested, [this] { m_title_database = Core::TitleDatabase(); }); + connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, + &GameListModel::OnEmulationStateChanged); for (const QString& dir : Settings::Instance().GetPaths()) m_tracker.AddDirectory(dir); @@ -187,6 +191,22 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const return compression.isEmpty() ? tr("No Compression") : compression; } break; + case Column::TimePlayed: + if (role == Qt::DisplayRole || role == SORT_ROLE) + { + std::string game_id = game.GetGameID(); + std::chrono::milliseconds total_time = m_timer.GetTimePlayed(game_id); + std::chrono::minutes total_minutes = + std::chrono::duration_cast(total_time); + std::chrono::hours total_hours = std::chrono::duration_cast(total_time); + + // i18n: A time displayed as hours and minutes + QString formatted_time = tr("%1h %2m") + .arg(total_hours.count()) + .arg(total_minutes.count() - total_hours.count() * 60); + return formatted_time; + } + break; case Column::Tags: if (role == Qt::DisplayRole || role == SORT_ROLE) { @@ -232,6 +252,8 @@ QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int return tr("Block Size"); case Column::Compression: return tr("Compression"); + case Column::TimePlayed: + return tr("Time Played"); case Column::Tags: return tr("Tags"); default: @@ -480,3 +502,11 @@ void GameListModel::PurgeCache() { m_tracker.PurgeCache(); } + +void GameListModel::OnEmulationStateChanged(Core::State state) +{ + if (state == Core::State::Uninitialized) + { + m_timer.Reload(); + } +} diff --git a/Source/Core/DolphinQt/GameList/GameListModel.h b/Source/Core/DolphinQt/GameList/GameListModel.h index b76e0a37c275..baac5d507d4a 100644 --- a/Source/Core/DolphinQt/GameList/GameListModel.h +++ b/Source/Core/DolphinQt/GameList/GameListModel.h @@ -12,6 +12,8 @@ #include #include +#include "Core/Core.h" +#include "Core/TimePlayed.h" #include "Core/TitleDatabase.h" #include "DolphinQt/GameList/GameTracker.h" @@ -58,6 +60,7 @@ class GameListModel final : public QAbstractTableModel FileFormat, BlockSize, Compression, + TimePlayed, Tags, Count, }; @@ -87,12 +90,15 @@ class GameListModel final : public QAbstractTableModel // Index in m_games, or -1 if it isn't found int FindGameIndex(const std::string& path) const; + void OnEmulationStateChanged(Core::State state); + QStringList m_tag_list; QMap m_game_tags; GameTracker m_tracker; QList> m_games; Core::TitleDatabase m_title_database; + TimePlayed m_timer; QString m_term; float m_scale = 1.0; }; diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index fef441c0626d..24c6cc2d04a2 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -701,6 +701,7 @@ void MenuBar::AddListColumnsMenu(QMenu* view_menu) {tr("File Format"), &Config::MAIN_GAMELIST_COLUMN_FILE_FORMAT}, {tr("Block Size"), &Config::MAIN_GAMELIST_COLUMN_BLOCK_SIZE}, {tr("Compression"), &Config::MAIN_GAMELIST_COLUMN_COMPRESSION}, + {tr("Time Played"), &Config::MAIN_GAMELIST_COLUMN_TIME_PLAYED}, {tr("Tags"), &Config::MAIN_GAMELIST_COLUMN_TAGS}}; QActionGroup* column_group = new QActionGroup(this); diff --git a/Source/Core/DolphinQt/Settings/InterfacePane.cpp b/Source/Core/DolphinQt/Settings/InterfacePane.cpp index e52fb6920001..1972c003efe5 100644 --- a/Source/Core/DolphinQt/Settings/InterfacePane.cpp +++ b/Source/Core/DolphinQt/Settings/InterfacePane.cpp @@ -22,6 +22,7 @@ #include "Core/AchievementManager.h" #include "Core/Config/MainSettings.h" #include "Core/Config/UISettings.h" +#include "Core/System.h" #include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigChoice.h" @@ -32,6 +33,7 @@ #include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/Settings.h" +#include #include "UICommon/GameFile.h" static ConfigStringChoice* MakeLanguageComboBox() @@ -95,6 +97,10 @@ InterfacePane::InterfacePane(QWidget* parent) : QWidget(parent) connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, &InterfacePane::UpdateShowDebuggingCheckbox); + connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, + &InterfacePane::OnEmulationStateChanged); + + OnEmulationStateChanged(Core::GetState(Core::System::GetInstance())); } void InterfacePane::CreateLayout() @@ -168,12 +174,15 @@ void InterfacePane::CreateUI() new ConfigBool(tr("Hotkeys Require Window Focus"), Config::MAIN_FOCUSED_HOTKEYS); m_checkbox_disable_screensaver = new ConfigBool(tr("Inhibit Screensaver During Emulation"), Config::MAIN_DISABLE_SCREENSAVER); + m_checkbox_time_tracking = + new ConfigBool(tr("Enable Play Time Tracking"), Config::MAIN_TIME_TRACKING); groupbox_layout->addWidget(m_checkbox_use_builtin_title_database); groupbox_layout->addWidget(m_checkbox_use_covers); groupbox_layout->addWidget(m_checkbox_show_debugging_ui); groupbox_layout->addWidget(m_checkbox_focused_hotkeys); groupbox_layout->addWidget(m_checkbox_disable_screensaver); + groupbox_layout->addWidget(m_checkbox_time_tracking); } void InterfacePane::CreateInGame() @@ -313,6 +322,12 @@ void InterfacePane::OnLanguageChanged() tr("You must restart Dolphin in order for the change to take effect.")); } +void InterfacePane::OnEmulationStateChanged(Core::State state) +{ + const bool running = state != Core::State::Uninitialized; + m_checkbox_time_tracking->setEnabled(!running); +} + void InterfacePane::AddDescriptions() { static constexpr char TR_TITLE_DATABASE_DESCRIPTION[] = QT_TR_NOOP( @@ -341,6 +356,10 @@ void InterfacePane::AddDescriptions() static constexpr char TR_DISABLE_SCREENSAVER_DESCRIPTION[] = QT_TR_NOOP("Disables your screensaver while running a game." "

If unsure, leave this checked."); + static constexpr char TR_TIME_TRACKING[] = QT_TR_NOOP( + "Tracks the time you spend playing games and shows it in the List View (as hours/minutes)." + "

This setting cannot be changed while emulation is active." + "

If unsure, leave this checked."); static constexpr char TR_CONFIRM_ON_STOP_DESCRIPTION[] = QT_TR_NOOP("Prompts you to confirm that you want to end emulation when you press Stop." "

If unsure, leave this checked."); @@ -394,6 +413,8 @@ void InterfacePane::AddDescriptions() m_checkbox_disable_screensaver->SetDescription(tr(TR_DISABLE_SCREENSAVER_DESCRIPTION)); + m_checkbox_time_tracking->SetDescription(tr(TR_TIME_TRACKING)); + m_checkbox_confirm_on_stop->SetDescription(tr(TR_CONFIRM_ON_STOP_DESCRIPTION)); m_checkbox_use_panic_handlers->SetDescription(tr(TR_USE_PANIC_HANDLERS_DESCRIPTION)); diff --git a/Source/Core/DolphinQt/Settings/InterfacePane.h b/Source/Core/DolphinQt/Settings/InterfacePane.h index 90a81d2114e2..ffe1efa0872e 100644 --- a/Source/Core/DolphinQt/Settings/InterfacePane.h +++ b/Source/Core/DolphinQt/Settings/InterfacePane.h @@ -13,6 +13,11 @@ class QVBoxLayout; class ToolTipCheckBox; class ToolTipComboBox; +namespace Core +{ +enum class State; +} + class InterfacePane final : public QWidget { Q_OBJECT @@ -30,6 +35,8 @@ class InterfacePane final : public QWidget void OnUserStyleChanged(); void OnLanguageChanged(); + void OnEmulationStateChanged(Core::State state); + QVBoxLayout* m_main_layout; ConfigStringChoice* m_combobox_language; @@ -42,6 +49,7 @@ class InterfacePane final : public QWidget ConfigBool* m_checkbox_focused_hotkeys; ConfigBool* m_checkbox_use_covers; ConfigBool* m_checkbox_disable_screensaver; + ConfigBool* m_checkbox_time_tracking; ConfigBool* m_checkbox_confirm_on_stop; ConfigBool* m_checkbox_use_panic_handlers;