summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/common_paths.h3
-rw-r--r--src/common/file_util.cpp17
-rw-r--r--src/common/file_util.h11
-rw-r--r--src/common/logging/backend.cpp157
-rw-r--r--src/common/logging/backend.h87
-rw-r--r--src/yuzu/CMakeLists.txt10
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.cpp22
-rw-r--r--src/yuzu/configuration/configure_debug.ui41
-rw-r--r--src/yuzu/debugger/console.cpp45
-rw-r--r--src/yuzu/debugger/console.h14
-rw-r--r--src/yuzu/main.cpp10
-rw-r--r--src/yuzu/ui_settings.h3
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
14 files changed, 408 insertions, 22 deletions
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 0a6132dab..d03fca314 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -32,12 +32,15 @@
#define SDMC_DIR "sdmc"
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
+#define LOG_DIR "log"
// Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
#define EMU_CONFIG "emu.ini"
#define DEBUGGER_CONFIG "debugger.ini"
#define LOGGER_CONFIG "logger.ini"
+// Files in the directory returned by GetUserPath(D_LOGS_IDX)
+#define LOG_FILE "citra_log.txt"
// Sys files
#define SHARED_FONT "shared_font.bin"
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 2152e3fea..b9e1fd1f6 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -713,6 +713,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
+ // TODO: Put the logs in a better location for each OS
+ paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP;
}
if (!newPath.empty()) {
@@ -799,8 +801,8 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
IOFile::IOFile() {}
-IOFile::IOFile(const std::string& filename, const char openmode[]) {
- Open(filename, openmode);
+IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
+ Open(filename, openmode, flags);
}
IOFile::~IOFile() {
@@ -821,11 +823,16 @@ void IOFile::Swap(IOFile& other) noexcept {
std::swap(m_good, other.m_good);
}
-bool IOFile::Open(const std::string& filename, const char openmode[]) {
+bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
Close();
#ifdef _WIN32
- _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
- Common::UTF8ToUTF16W(openmode).c_str());
+ if (flags != 0) {
+ m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
+ Common::UTF8ToUTF16W(openmode).c_str(), flags);
+ } else {
+ _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
+ Common::UTF8ToUTF16W(openmode).c_str());
+ }
#else
m_file = fopen(filename.c_str(), openmode);
#endif
diff --git a/src/common/file_util.h b/src/common/file_util.h
index fc6b3ea46..5bc7fbf7c 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -156,7 +156,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
class IOFile : public NonCopyable {
public:
IOFile();
- IOFile(const std::string& filename, const char openmode[]);
+ // flags is used for windows specific file open mode flags, which
+ // allows yuzu to open the logs in shared write mode, so that the file
+ // isn't considered "locked" while yuzu is open and people can open the log file and view it
+ IOFile(const std::string& filename, const char openmode[], int flags = 0);
~IOFile();
@@ -165,7 +168,7 @@ public:
void Swap(IOFile& other) noexcept;
- bool Open(const std::string& filename, const char openmode[]);
+ bool Open(const std::string& filename, const char openmode[], int flags = 0);
bool Close();
template <typename T>
@@ -220,6 +223,10 @@ public:
return WriteArray(&object, 1);
}
+ size_t WriteString(const std::string& str) {
+ return WriteArray(str.c_str(), str.length());
+ }
+
bool IsOpen() const {
return nullptr != m_file;
}
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index c26b20062..242914c6a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -2,16 +2,145 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <utility>
+#include <algorithm>
+#include <array>
+#include <chrono>
+#include <condition_variable>
+#include <memory>
+#include <thread>
+#ifdef _WIN32
+#include <share.h> // For _SH_DENYWR
+#else
+#define _SH_DENYWR 0
+#endif
#include "common/assert.h"
+#include "common/common_funcs.h" // snprintf compatibility define
#include "common/logging/backend.h"
-#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
+#include "common/threadsafe_queue.h"
namespace Log {
+/**
+ * Static state as a singleton.
+ */
+class Impl {
+public:
+ static Impl& Instance() {
+ static Impl backend;
+ return backend;
+ }
+
+ Impl(Impl const&) = delete;
+ const Impl& operator=(Impl const&) = delete;
+
+ void PushEntry(Entry e) {
+ std::lock_guard<std::mutex> lock(message_mutex);
+ message_queue.Push(std::move(e));
+ message_cv.notify_one();
+ }
+
+ void AddBackend(std::unique_ptr<Backend> backend) {
+ std::lock_guard<std::mutex> lock(writing_mutex);
+ backends.push_back(std::move(backend));
+ }
+
+ void RemoveBackend(const std::string& backend_name) {
+ std::lock_guard<std::mutex> lock(writing_mutex);
+ auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
+ return !strcmp(i->GetName(), backend_name.c_str());
+ });
+ backends.erase(it, backends.end());
+ }
+
+ const Filter& GetGlobalFilter() const {
+ return filter;
+ }
+
+ void SetGlobalFilter(const Filter& f) {
+ filter = f;
+ }
+
+ Backend* GetBackend(const std::string& backend_name) {
+ auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
+ return !strcmp(i->GetName(), backend_name.c_str());
+ });
+ if (it == backends.end())
+ return nullptr;
+ return it->get();
+ }
+
+private:
+ Impl() {
+ backend_thread = std::thread([&] {
+ Entry entry;
+ auto write_logs = [&](Entry& e) {
+ std::lock_guard<std::mutex> lock(writing_mutex);
+ for (const auto& backend : backends) {
+ backend->Write(e);
+ }
+ };
+ while (true) {
+ std::unique_lock<std::mutex> lock(message_mutex);
+ message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
+ if (!running) {
+ break;
+ }
+ write_logs(entry);
+ }
+ // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
+ // where a system is repeatedly spamming logs even on close.
+ constexpr int MAX_LOGS_TO_WRITE = 100;
+ int logs_written = 0;
+ while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
+ write_logs(entry);
+ }
+ });
+ }
+
+ ~Impl() {
+ running = false;
+ message_cv.notify_one();
+ backend_thread.join();
+ }
+
+ std::atomic_bool running{true};
+ std::mutex message_mutex, writing_mutex;
+ std::condition_variable message_cv;
+ std::thread backend_thread;
+ std::vector<std::unique_ptr<Backend>> backends;
+ Common::MPSCQueue<Log::Entry> message_queue;
+ Filter filter;
+};
+
+void ConsoleBackend::Write(const Entry& entry) {
+ PrintMessage(entry);
+}
+
+void ColorConsoleBackend::Write(const Entry& entry) {
+ PrintColoredMessage(entry);
+}
+
+// _SH_DENYWR allows read only access to the file for other programs.
+// It is #defined to 0 on other platforms
+FileBackend::FileBackend(const std::string& filename)
+ : file(filename, "w", _SH_DENYWR), bytes_written(0) {}
+
+void FileBackend::Write(const Entry& entry) {
+ // prevent logs from going over the maximum size (in case its spamming and the user doesn't
+ // know)
+ constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
+ if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
+ return;
+ }
+ bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
+ if (entry.log_level >= Level::Error) {
+ file.Flush();
+ }
+}
+
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \
CLS(Log) \
@@ -125,20 +254,32 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign
return entry;
}
-static Filter* filter = nullptr;
+void SetGlobalFilter(const Filter& filter) {
+ Impl::Instance().SetGlobalFilter(filter);
+}
+
+void AddBackend(std::unique_ptr<Backend> backend) {
+ Impl::Instance().AddBackend(std::move(backend));
+}
-void SetFilter(Filter* new_filter) {
- filter = new_filter;
+void RemoveBackend(const std::string& backend_name) {
+ Impl::Instance().RemoveBackend(backend_name);
+}
+
+Backend* GetBackend(const std::string& backend_name) {
+ return Impl::Instance().GetBackend(backend_name);
}
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) {
- if (filter && !filter->CheckMessage(log_class, log_level))
+ auto filter = Impl::Instance().GetGlobalFilter();
+ if (!filter.CheckMessage(log_class, log_level))
return;
+
Entry entry =
CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
- PrintColoredMessage(entry);
+ Impl::Instance().PushEntry(std::move(entry));
}
-} // namespace Log
+} // namespace Log \ No newline at end of file
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 7e81efb23..57cdf6b2d 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -1,13 +1,15 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-
#pragma once
#include <chrono>
#include <cstdarg>
+#include <memory>
#include <string>
#include <utility>
+#include "common/file_util.h"
+#include "common/logging/filter.h"
#include "common/logging/log.h"
namespace Log {
@@ -35,6 +37,80 @@ struct Entry {
};
/**
+ * Interface for logging backends. As loggers can be created and removed at runtime, this can be
+ * used by a frontend for adding a custom logging backend as needed
+ */
+class Backend {
+public:
+ virtual ~Backend() = default;
+ virtual void SetFilter(const Filter& new_filter) {
+ filter = new_filter;
+ }
+ virtual const char* GetName() const = 0;
+ virtual void Write(const Entry& entry) = 0;
+
+private:
+ Filter filter;
+};
+
+/**
+ * Backend that writes to stderr without any color commands
+ */
+class ConsoleBackend : public Backend {
+public:
+ static const char* Name() {
+ return "console";
+ }
+ const char* GetName() const override {
+ return Name();
+ }
+ void Write(const Entry& entry) override;
+};
+
+/**
+ * Backend that writes to stderr and with color
+ */
+class ColorConsoleBackend : public Backend {
+public:
+ static const char* Name() {
+ return "color_console";
+ }
+
+ const char* GetName() const override {
+ return Name();
+ }
+ void Write(const Entry& entry) override;
+};
+
+/**
+ * Backend that writes to a file passed into the constructor
+ */
+class FileBackend : public Backend {
+public:
+ explicit FileBackend(const std::string& filename);
+
+ static const char* Name() {
+ return "file";
+ }
+
+ const char* GetName() const override {
+ return Name();
+ }
+
+ void Write(const Entry& entry) override;
+
+private:
+ FileUtil::IOFile file;
+ size_t bytes_written;
+};
+
+void AddBackend(std::unique_ptr<Backend> backend);
+
+void RemoveBackend(const std::string& backend_name);
+
+Backend* GetBackend(const std::string& backend_name);
+
+/**
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
* instead of underscores as in the enumeration.
*/
@@ -49,5 +125,10 @@ const char* GetLevelName(Level log_level);
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message);
-void SetFilter(Filter* filter);
-} // namespace Log
+/**
+ * The global filter will prevent any messages from even being processed if they are filtered. Each
+ * backend can have a filter, but if the level is lower than the global filter, the backend will
+ * never get the message
+ */
+void SetGlobalFilter(const Filter& filter);
+} // namespace Log \ No newline at end of file
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index c662570d2..7de919a8e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,6 +30,8 @@ add_executable(yuzu
debugger/graphics/graphics_breakpoints_p.h
debugger/graphics/graphics_surface.cpp
debugger/graphics/graphics_surface.h
+ debugger/console.cpp
+ debugger/console.h
debugger/profiler.cpp
debugger/profiler.h
debugger/wait_tree.cpp
@@ -81,6 +83,14 @@ if (APPLE)
target_sources(yuzu PRIVATE ${MACOSX_ICON})
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
+elseif(WIN32)
+ # compile as a win32 gui application instead of a console application
+ target_link_libraries(yuzu PRIVATE Qt5::WinMain)
+ if(MSVC)
+ set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
+ elseif(MINGW)
+ set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
+ endif()
endif()
create_target_directory_groups(yuzu)
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index cd7986efa..a32134fbe 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -160,6 +160,7 @@ void Config::ReadValues() {
UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
+ UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
qt_config->endGroup();
}
@@ -246,7 +247,7 @@ void Config::SaveValues() {
qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
qt_config->setValue("firstStart", UISettings::values.first_start);
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
-
+ qt_config->setValue("showConsole", UISettings::values.show_console);
qt_config->endGroup();
}
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index a45edd510..241db4ae3 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -2,13 +2,26 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <QDesktopServices>
+#include <QUrl>
+#include "common/file_util.h"
+#include "common/logging/backend.h"
+#include "common/logging/filter.h"
+#include "common/logging/log.h"
+#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_debug.h"
#include "yuzu/configuration/configure_debug.h"
+#include "yuzu/debugger/console.h"
+#include "yuzu/ui_settings.h"
ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
ui->setupUi(this);
this->setConfiguration();
+ connect(ui->open_log_button, &QPushButton::pressed, []() {
+ QString path = QString::fromStdString(FileUtil::GetUserPath(D_LOGS_IDX));
+ QDesktopServices::openUrl(QUrl::fromLocalFile(path));
+ });
}
ConfigureDebug::~ConfigureDebug() {}
@@ -17,10 +30,19 @@ void ConfigureDebug::setConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
+ ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
+ ui->toggle_console->setChecked(UISettings::values.show_console);
+ ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
}
void ConfigureDebug::applyConfiguration() {
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
+ UISettings::values.show_console = ui->toggle_console->isChecked();
+ Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
+ Debugger::ToggleConsole();
+ Log::Filter filter;
+ filter.ParseFilterString(Settings::values.log_filter);
+ Log::SetGlobalFilter(filter);
Settings::Apply();
}
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index a10bea2f4..118e91cf1 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -73,6 +73,47 @@
</layout>
</item>
<item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Logging</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Global Log Filter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="log_filter_edit"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="toggle_console">
+ <property name="text">
+ <string>Show Log Console (Windows Only)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="open_log_button">
+ <property name="text">
+ <string>Open Log Location</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp
new file mode 100644
index 000000000..e3d2d975f
--- /dev/null
+++ b/src/yuzu/debugger/console.cpp
@@ -0,0 +1,45 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#ifdef _WIN32
+#include <windows.h>
+
+#include <wincon.h>
+#endif
+
+#include "common/logging/backend.h"
+#include "yuzu/debugger/console.h"
+#include "yuzu/ui_settings.h"
+
+namespace Debugger {
+void ToggleConsole() {
+#if defined(_WIN32) && !defined(_DEBUG)
+ FILE* temp;
+ if (UISettings::values.show_console) {
+ if (AllocConsole()) {
+ // The first parameter for freopen_s is a out parameter, so we can just ignore it
+ freopen_s(&temp, "CONIN$", "r", stdin);
+ freopen_s(&temp, "CONOUT$", "w", stdout);
+ freopen_s(&temp, "CONOUT$", "w", stderr);
+ Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
+ }
+ } else {
+ if (FreeConsole()) {
+ // In order to close the console, we have to also detach the streams on it.
+ // Just redirect them to NUL if there is no console window
+ Log::RemoveBackend(Log::ColorConsoleBackend::Name());
+ freopen_s(&temp, "NUL", "r", stdin);
+ freopen_s(&temp, "NUL", "w", stdout);
+ freopen_s(&temp, "NUL", "w", stderr);
+ }
+ }
+#else
+ if (UISettings::values.show_console) {
+ Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
+ } else {
+ Log::RemoveBackend(Log::ColorConsoleBackend::Name());
+ }
+#endif
+}
+} // namespace Debugger
diff --git a/src/yuzu/debugger/console.h b/src/yuzu/debugger/console.h
new file mode 100644
index 000000000..d1990c496
--- /dev/null
+++ b/src/yuzu/debugger/console.h
@@ -0,0 +1,14 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Debugger {
+
+/**
+ * Uses the WINAPI to hide or show the stderr console. This function is a placeholder until we can
+ * get a real qt logging window which would work for all platforms.
+ */
+void ToggleConsole();
+} // namespace Debugger \ No newline at end of file
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2c52415f9..05a8ae6d2 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -31,6 +31,7 @@
#include "yuzu/bootmanager.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_dialog.h"
+#include "yuzu/debugger/console.h"
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
#include "yuzu/debugger/graphics/graphics_surface.h"
#include "yuzu/debugger/profiler.h"
@@ -261,6 +262,7 @@ void GMainWindow::RestoreUIState() {
ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
+ Debugger::ToggleConsole();
}
void GMainWindow::ConnectWidgetEvents() {
@@ -906,8 +908,7 @@ void GMainWindow::UpdateUITheme() {
#endif
int main(int argc, char* argv[]) {
- Log::Filter log_filter(Log::Level::Info);
- Log::SetFilter(&log_filter);
+ Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -925,7 +926,12 @@ int main(int argc, char* argv[]) {
GMainWindow main_window;
// After settings have been loaded by GMainWindow, apply the filter
+ Log::Filter log_filter;
log_filter.ParseFilterString(Settings::values.log_filter);
+ Log::SetGlobalFilter(log_filter);
+ FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
+ Log::AddBackend(
+ std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
main_window.show();
return app.exec();
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 8e215a002..2286c2559 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -51,6 +51,9 @@ struct Values {
std::vector<Shortcut> shortcuts;
uint32_t callout_flags;
+
+ // logging
+ bool show_console;
};
extern Values values;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 079f93736..89a3e9f30 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -126,7 +126,12 @@ int main(int argc, char** argv) {
#endif
Log::Filter log_filter(Log::Level::Debug);
- Log::SetFilter(&log_filter);
+ Log::SetGlobalFilter(log_filter);
+
+ Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
+ FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
+ Log::AddBackend(
+ std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); });