diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index 9a24663e9..0c20b03ae 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -194,13 +194,26 @@ typedef intptr_t ssize_t; #define TEST_FILL_BITS(SRC, START, END, TEST) ((TEST) ? (FILL_BITS(SRC, START, END)) : (CLEAR_BITS(SRC, START, END))) #ifdef _MSC_VER +#pragma section(".CRT$XCU",read) #define ATTRIBUTE_UNUSED #define ATTRIBUTE_FORMAT(X, Y, Z) #define ATTRIBUTE_NOINLINE +// Adapted from https://stackoverflow.com/a/2390626 +#define _CONSTRUCTOR(FN, PRE) \ + static void FN(void); \ + __declspec(allocate(".CRT$XCU")) void (*_CONSTRUCTOR_ ## FN)(void) = FN; \ + __pragma(comment(linker,"/include:" PRE "_CONSTRUCTOR_" #FN)) \ + static void FN(void) +#ifdef _WIN64 +#define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "") +#else +#define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "_") +#endif #else #define ATTRIBUTE_UNUSED __attribute__((unused)) #define ATTRIBUTE_FORMAT(X, Y, Z) __attribute__((format(X, Y, Z))) #define ATTRIBUTE_NOINLINE __attribute__((noinline)) +#define CONSTRUCTOR(FN) static __attribute__((constructor)) void FN(void) #endif #define DECL_BITFIELD(NAME, TYPE) typedef TYPE NAME diff --git a/include/mgba/core/log.h b/include/mgba/core/log.h index 5a6fc6128..d7491f4cf 100644 --- a/include/mgba/core/log.h +++ b/include/mgba/core/log.h @@ -47,24 +47,23 @@ struct mCoreConfig; void mLogFilterInit(struct mLogFilter*); void mLogFilterDeinit(struct mLogFilter*); void mLogFilterLoad(struct mLogFilter*, const struct mCoreConfig*); +void mLogFilterSave(const struct mLogFilter*, struct mCoreConfig*); void mLogFilterSet(struct mLogFilter*, const char* category, int levels); -bool mLogFilterTest(struct mLogFilter*, int category, enum mLogLevel level); +void mLogFilterReset(struct mLogFilter*, const char* category); +bool mLogFilterTest(const struct mLogFilter*, int category, enum mLogLevel level); +int mLogFilterLevels(const struct mLogFilter*, int category); ATTRIBUTE_FORMAT(printf, 3, 4) void mLog(int category, enum mLogLevel level, const char* format, ...); -#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__) +#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY, mLOG_ ## LEVEL, __VA_ARGS__) -#define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void); extern const char* _mLOG_CAT_ ## CATEGORY ## _ID; +#define mLOG_DECLARE_CATEGORY(CATEGORY) extern int _mLOG_CAT_ ## CATEGORY; #define mLOG_DEFINE_CATEGORY(CATEGORY, NAME, ID) \ - int _mLOG_CAT_ ## CATEGORY (void) { \ - static int category = 0; \ - if (!category) { \ - category = mLogGenerateCategory(NAME, ID); \ - } \ - return category; \ - } \ - const char* _mLOG_CAT_ ## CATEGORY ## _ID = ID; + int _mLOG_CAT_ ## CATEGORY; \ + CONSTRUCTOR(_mLOG_CAT_ ## CATEGORY ## _INIT) { \ + _mLOG_CAT_ ## CATEGORY = mLogGenerateCategory(NAME, ID); \ + } mLOG_DECLARE_CATEGORY(STATUS) diff --git a/src/core/log.c b/src/core/log.c index 118d6fff3..e0f5a002a 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -104,7 +104,7 @@ static void _setFilterLevel(const char* key, const char* value, enum mCoreConfig char* end; int ivalue = strtol(value, &end, 10); if (ivalue == 0) { - ivalue = INT_MIN; // Zero is reserved + ivalue = 0x80; // Zero is reserved } if (!end) { return; @@ -113,34 +113,66 @@ static void _setFilterLevel(const char* key, const char* value, enum mCoreConfig } void mLogFilterLoad(struct mLogFilter* filter, const struct mCoreConfig* config) { + HashTableClear(&filter->categories); + TableClear(&filter->levels); + mCoreConfigEnumerate(config, "logLevel.", _setFilterLevel, filter); filter->defaultLevels = mLOG_ALL; mCoreConfigGetIntValue(config, "logLevel", &filter->defaultLevels); } +void mLogFilterSave(const struct mLogFilter* filter, struct mCoreConfig* config) { + mCoreConfigSetIntValue(config, "logLevel", filter->defaultLevels); + int i; + for (i = 0; i < _category; ++i) { + char configName[128] = {0}; + snprintf(configName, sizeof(configName) - 1, "logLevel.%s", mLogCategoryId(i)); + int levels = mLogFilterLevels(filter, i); + if (levels) { + mCoreConfigSetIntValue(config, configName, levels & ~0x80); + } else { + mCoreConfigSetValue(config, configName, NULL); + } + } +} + void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels) { + levels |= 0x80; HashTableInsert(&filter->categories, category, (void*)(intptr_t) levels); // Can't do this eagerly because not all categories are initialized immediately int cat = mLogCategoryById(category); if (cat >= 0) { TableInsert(&filter->levels, cat, (void*)(intptr_t) levels); } - } -bool mLogFilterTest(struct mLogFilter* filter, int category, enum mLogLevel level) { - int value = (intptr_t) TableLookup(&filter->levels, category); + +void mLogFilterReset(struct mLogFilter* filter, const char* category) { + HashTableRemove(&filter->categories, category); + // Can't do this eagerly because not all categories are initialized immediately + int cat = mLogCategoryById(category); + if (cat >= 0) { + TableRemove(&filter->levels, cat); + } +} + +bool mLogFilterTest(const struct mLogFilter* filter, int category, enum mLogLevel level) { + int value = mLogFilterLevels(filter, category); if (value) { return value & level; } - const char* cat = mLogCategoryId(category); - if (cat) { - value = (intptr_t) HashTableLookup(&filter->categories, cat); - if (value) { - TableInsert(&filter->levels, category, (void*)(intptr_t) value); - return value & level; - } - } return level & filter->defaultLevels; } +int mLogFilterLevels(const struct mLogFilter* filter , int category) { + int value = (intptr_t) TableLookup(&filter->levels, category); + if (value) { + return value; + } + const char* cat = mLogCategoryId(category); + if (cat) { + value = (intptr_t) HashTableLookup(&filter->categories, cat); + } + return value; +} + mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status") diff --git a/src/gba/gba.c b/src/gba/gba.c index 7cb3faafe..8954df16b 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -524,7 +524,7 @@ void GBADebug(struct GBA* gba, uint16_t flags) { strncpy(oolBuf, gba->debugString, sizeof(oolBuf) - 1); memset(gba->debugString, 0, sizeof(gba->debugString)); oolBuf[0x100] = '\0'; - mLog(_mLOG_CAT_GBA_DEBUG(), level, "%s", oolBuf); + mLog(_mLOG_CAT_GBA_DEBUG, level, "%s", oolBuf); } gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags); } diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 629f45082..b9c7d0ed8 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -90,6 +90,7 @@ set(SOURCE_FILES KeyEditor.cpp LoadSaveState.cpp LogController.cpp + LogConfigModel.cpp LogView.cpp MapView.cpp MemoryModel.cpp @@ -104,6 +105,7 @@ set(SOURCE_FILES PrinterView.cpp RegisterView.cpp ROMInfo.cpp + RotatedHeaderView.cpp SavestateButton.cpp SensorView.cpp SettingsView.cpp diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 89581beaf..e21fbade5 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -82,7 +82,8 @@ public: Configuration* input() { return mCoreConfigGetInput(&m_config); } - const mCoreConfig* config() { return &m_config; } + const mCoreConfig* config() const { return &m_config; } + mCoreConfig* config() { return &m_config; } static const QString& configDir(); diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index c331c11f3..b88328778 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -10,8 +10,9 @@ #include "CoreManager.h" #include "ConfigController.h" #include "Display.h" -#include "Window.h" +#include "LogController.h" #include "VFileDevice.h" +#include "Window.h" #include #include @@ -65,6 +66,8 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config) AudioProcessor::setDriver(static_cast(m_configController->getQtOption("audioDriver").toInt())); } + LogController::global()->load(m_configController); + connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup); } diff --git a/src/platform/qt/LogConfigModel.cpp b/src/platform/qt/LogConfigModel.cpp new file mode 100644 index 000000000..0320d2497 --- /dev/null +++ b/src/platform/qt/LogConfigModel.cpp @@ -0,0 +1,149 @@ +/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "LogConfigModel.h" + +#include + +using namespace QGBA; + +LogConfigModel::LogConfigModel(LogController* controller, QObject* parent) + : QAbstractItemModel(parent) + , m_controller(controller) +{ + for (int i = 0; mLogCategoryId(i); ++i) { + int levels = controller->levels(i); + m_cache.append({ i, mLogCategoryName(i), mLogCategoryId(i), levels ? levels : -1 }); + } + std::sort(m_cache.begin(), m_cache.end()); + m_levels = m_controller->levels(); +} + +QVariant LogConfigModel::data(const QModelIndex& index, int role) const { + if (role != Qt::CheckStateRole) { + return QVariant(); + } + int levels; + if (index.row() == 0) { + levels = m_levels; + } else { + levels = m_cache[index.row() - 1].levels; + } + if (index.column() == 0) { + return levels < 0 ? Qt::Checked : Qt::Unchecked; + } else if (levels < 0 && index.row() > 0) { + return (m_levels >> (index.column() - 1)) & 1 ? Qt::PartiallyChecked : Qt::Unchecked; + } else { + return (levels >> (index.column() - 1)) & 1 ? Qt::Checked : Qt::Unchecked; + } +} + +bool LogConfigModel::setData(const QModelIndex& index, const QVariant& value, int role) { + if (role != Qt::CheckStateRole) { + return false; + } + int levels; + if (index.row() == 0) { + levels = m_levels; + } else { + levels = m_cache[index.row() - 1].levels; + } + if (index.column() == 0) { + levels = -1; + } else { + if (levels < 0) { + levels = m_levels; + } + levels ^= 1 << (index.column() - 1); + } + if (index.row() == 0) { + beginResetModel(); + m_levels = levels; + endResetModel(); + } else { + m_cache[index.row() - 1].levels = levels; + emit dataChanged(createIndex(0, index.row(), nullptr), createIndex(8, index.row(), nullptr)); + } + return true; +} + +QVariant LogConfigModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (role != Qt::DisplayRole) { + return QVariant(); + } + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return tr("Default"); + case 1: + return tr("Fatal"); + case 2: + return tr("Error"); + case 3: + return tr("Warning"); + case 4: + return tr("Info"); + case 5: + return tr("Debug"); + case 6: + return tr("Stub"); + case 7: + return tr("Game Error"); + default: + return QVariant(); + } + } else if (section) { + return m_cache[section - 1].name; + } else { + return tr("Default"); + } +} + +QModelIndex LogConfigModel::index(int row, int column, const QModelIndex& parent) const { + return createIndex(row, column, nullptr); +} + +QModelIndex LogConfigModel::parent(const QModelIndex& index) const { + return QModelIndex(); +} + +int LogConfigModel::columnCount(const QModelIndex& parent) const { + return 8; +} + +int LogConfigModel::rowCount(const QModelIndex& parent) const { + return m_cache.size() + 1; +} + +Qt::ItemFlags LogConfigModel::flags(const QModelIndex& index) const { + if (!index.isValid()) { + return 0; + } + return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled; +} + +void LogConfigModel::reset() { + beginResetModel(); + for (auto& row : m_cache) { + row.levels = m_controller->levels(row.index); + if (!row.levels) { + row.levels = -1; + } + } + m_levels = m_controller->levels(); + endResetModel(); +} + +void LogConfigModel::save(ConfigController* config) { + for (auto& row : m_cache) { + if (row.levels < 0) { + m_controller->clearLevels(row.index); + } else { + m_controller->setLevels(row.levels, row.index); + } + } + m_controller->setLevels(m_levels); + m_controller->save(config); +} \ No newline at end of file diff --git a/src/platform/qt/LogConfigModel.h b/src/platform/qt/LogConfigModel.h new file mode 100644 index 000000000..b936bc43f --- /dev/null +++ b/src/platform/qt/LogConfigModel.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include + +#include "LogController.h" + +namespace QGBA { + +class ConfigController; + +class LogConfigModel : public QAbstractItemModel { +Q_OBJECT + +public: + LogConfigModel(LogController*, QObject* parent = nullptr); + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; + virtual QModelIndex parent(const QModelIndex& index) const override; + + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + +public slots: + void reset(); + void save(ConfigController*); + +private: + struct ConfigSetting { + int index; + QString name; + const char* id; + int levels; + + bool operator<(struct ConfigSetting& other) { + return name < other.name; + } + }; + + LogController* m_controller; + + QList m_cache; + int m_levels; +}; + +} \ No newline at end of file diff --git a/src/platform/qt/LogController.cpp b/src/platform/qt/LogController.cpp index 85de8720b..95d515205 100644 --- a/src/platform/qt/LogController.cpp +++ b/src/platform/qt/LogController.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "LogController.h" +#include "ConfigController.h" + using namespace QGBA; LogController LogController::s_global(mLOG_ALL); @@ -19,9 +21,9 @@ LogController::LogController(int levels, QObject* parent) if (this != &s_global) { connect(&s_global, &LogController::logPosted, this, &LogController::postLog); - connect(this, &LogController::levelsSet, &s_global, &LogController::setLevels); - connect(this, &LogController::levelsEnabled, &s_global, &LogController::enableLevels); - connect(this, &LogController::levelsDisabled, &s_global, &LogController::disableLevels); + connect(this, static_cast(&LogController::levelsSet), &s_global, static_cast(&LogController::setLevels)); + connect(this, static_cast(&LogController::levelsEnabled), &s_global, static_cast(&LogController::enableLevels)); + connect(this, static_cast(&LogController::levelsDisabled), &s_global, static_cast(&LogController::disableLevels)); } } @@ -29,10 +31,22 @@ LogController::~LogController() { mLogFilterDeinit(&m_filter); } +int LogController::levels(int category) const { + return mLogFilterLevels(&m_filter, category); +} + LogController::Stream LogController::operator()(int category, int level) { return Stream(this, category, level); } +void LogController::load(const ConfigController* config) { + mLogFilterLoad(&m_filter, config->config()); +} + +void LogController::save(ConfigController* config) const { + mLogFilterSave(&m_filter, config->config()); +} + void LogController::postLog(int level, int category, const QString& string) { if (!mLogFilterTest(&m_filter, category, static_cast(level))) { return; @@ -55,6 +69,31 @@ void LogController::disableLevels(int levels) { emit levelsDisabled(levels); } +void LogController::setLevels(int levels, int category) { + auto id = mLogCategoryId(category); + mLogFilterSet(&m_filter, id, levels); + emit levelsSet(levels, category); +} + +void LogController::enableLevels(int levels, int category) { + auto id = mLogCategoryId(category); + int newLevels = mLogFilterLevels(&m_filter, category) | levels; + mLogFilterSet(&m_filter, id, newLevels); + emit levelsEnabled(levels, category); +} + +void LogController::disableLevels(int levels, int category) { + auto id = mLogCategoryId(category); + int newLevels = mLogFilterLevels(&m_filter, category) & ~levels; + mLogFilterSet(&m_filter, id, newLevels); + emit levelsDisabled(levels, category); +} + +void LogController::clearLevels(int category) { + auto id = mLogCategoryId(category); + mLogFilterReset (&m_filter, id); +} + LogController* LogController::global() { return &s_global; } diff --git a/src/platform/qt/LogController.h b/src/platform/qt/LogController.h index b3c9470cf..c189e03ae 100644 --- a/src/platform/qt/LogController.h +++ b/src/platform/qt/LogController.h @@ -14,6 +14,8 @@ namespace QGBA { +class ConfigController; + class LogController : public QObject { Q_OBJECT @@ -38,24 +40,36 @@ public: ~LogController(); int levels() const { return m_filter.defaultLevels; } + int levels(int category) const; mLogFilter* filter() { return &m_filter; } Stream operator()(int category, int level); static LogController* global(); static QString toString(int level); + static int categoryId(const char*); + + void load(const ConfigController*); + void save(ConfigController*) const; signals: void logPosted(int level, int category, const QString& log); void levelsSet(int levels); void levelsEnabled(int levels); void levelsDisabled(int levels); + void levelsSet(int levels, int category); + void levelsEnabled(int levels, int category); + void levelsDisabled(int levels, int category); public slots: void postLog(int level, int category, const QString& string); void setLevels(int levels); void enableLevels(int levels); void disableLevels(int levels); + void setLevels(int levels, int category); + void enableLevels(int levels, int category); + void disableLevels(int levels, int category); + void clearLevels(int category); private: mLogFilter m_filter; @@ -63,6 +77,6 @@ private: static LogController s_global; }; -#define LOG(C, L) (*LogController::global())(mLOG_ ## L, _mLOG_CAT_ ## C ()) +#define LOG(C, L) (*LogController::global())(mLOG_ ## L, _mLOG_CAT_ ## C) } diff --git a/src/platform/qt/LogView.cpp b/src/platform/qt/LogView.cpp index 7ed748e78..32e0b1841 100644 --- a/src/platform/qt/LogView.cpp +++ b/src/platform/qt/LogView.cpp @@ -43,19 +43,19 @@ LogView::LogView(LogController* log, QWidget* parent) m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT); connect(log, &LogController::logPosted, this, &LogView::postLog); - connect(log, &LogController::levelsSet, this, &LogView::setLevels); - connect(log, &LogController::levelsEnabled, [this](int level) { + connect(log, static_cast(&LogController::levelsSet), this, &LogView::setLevels); + connect(log, static_cast(&LogController::levelsEnabled), [this](int level) { bool s = blockSignals(true); setLevel(level, true); blockSignals(s); }); - connect(log, &LogController::levelsDisabled, [this](int level) { + connect(log, static_cast(&LogController::levelsDisabled), [this](int level) { bool s = blockSignals(true); setLevel(level, false); blockSignals(s); }); - connect(this, &LogView::levelsEnabled, log, &LogController::enableLevels); - connect(this, &LogView::levelsDisabled, log, &LogController::disableLevels); + connect(this, &LogView::levelsEnabled, log, static_cast(&LogController::enableLevels)); + connect(this, &LogView::levelsDisabled, log, static_cast(&LogController::disableLevels)); } void LogView::postLog(int level, int category, const QString& log) { diff --git a/src/platform/qt/RotatedHeaderView.cpp b/src/platform/qt/RotatedHeaderView.cpp new file mode 100644 index 000000000..15d927628 --- /dev/null +++ b/src/platform/qt/RotatedHeaderView.cpp @@ -0,0 +1,27 @@ +/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "RotatedHeaderView.h" + +#include + +using namespace QGBA; + +RotatedHeaderView::RotatedHeaderView(Qt::Orientation orientation, QWidget* parent) + : QHeaderView(orientation, parent) +{ +} + +void RotatedHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const { + painter->save(); + painter->translate(rect.x() + rect.width(), rect.y()); + painter->rotate(90); + QHeaderView::paintSection(painter, QRect(0, 0, rect.height(), rect.width()), logicalIndex); + painter->restore(); +} + +QSize RotatedHeaderView::sectionSizeFromContents(int logicalIndex) const { + return QHeaderView::sectionSizeFromContents(logicalIndex).transposed(); +} \ No newline at end of file diff --git a/src/platform/qt/RotatedHeaderView.h b/src/platform/qt/RotatedHeaderView.h new file mode 100644 index 000000000..47d406bb7 --- /dev/null +++ b/src/platform/qt/RotatedHeaderView.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include + +namespace QGBA { + +class RotatedHeaderView : public QHeaderView { +Q_OBJECT + +public: + RotatedHeaderView(Qt::Orientation orientation, QWidget* parent = nullptr); + +protected: + void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const; + virtual QSize sectionSizeFromContents(int logicalIndex) const override; +}; + +} \ No newline at end of file diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 07f38edf9..b7611b148 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -11,6 +11,7 @@ #include "GBAApp.h" #include "GBAKeyEditor.h" #include "InputController.h" +#include "RotatedHeaderView.h" #include "ShaderSelector.h" #include "ShortcutView.h" @@ -24,9 +25,10 @@ using namespace QGBA; QList SettingsView::s_gbModelList; #endif -SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent) +SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) + , m_logModel(logController) { m_ui.setupUi(this); @@ -293,6 +295,11 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC } } + m_ui.loggingView->setModel(&m_logModel); + m_ui.loggingView->setHorizontalHeader(new RotatedHeaderView(Qt::Horizontal)); + m_ui.loggingView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + m_ui.loggingView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ShortcutView* shortcutView = new ShortcutView(); shortcutView->setController(shortcutController); shortcutView->setInputController(inputController); @@ -430,6 +437,8 @@ void SettingsView::updateConfig() { emit languageChanged(); } + m_logModel.save(m_controller); + #ifdef M_CORE_GB GBModel modelGB = s_gbModelList[m_ui.gbModel->currentIndex()]; m_controller->setOption("gb.model", GBModelToName(modelGB)); @@ -535,6 +544,8 @@ void SettingsView::reloadConfig() { m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA); m_ui.saveStateCheats->setChecked(saveState & SAVESTATE_CHEATS); + m_logModel.reset(); + #ifdef M_CORE_GB QString modelGB = m_controller->getOption("gb.model"); if (!modelGB.isNull()) { diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index 2be49d32d..017b55693 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -8,6 +8,7 @@ #include #include "ColorPicker.h" +#include "LogConfigModel.h" #include @@ -28,7 +29,7 @@ class SettingsView : public QDialog { Q_OBJECT public: - SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent = nullptr); + SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent = nullptr); ~SettingsView(); void setShaderSelector(ShaderSelector* shaderSelector); @@ -53,6 +54,7 @@ private: ConfigController* m_controller; InputController* m_input; ShaderSelector* m_shader = nullptr; + LogConfigModel m_logModel; #ifdef M_CORE_GB uint32_t m_gbColors[12]{}; diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 43725c118..7cd9f727f 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -23,6 +23,13 @@ QLayout::SetFixedSize + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + @@ -65,6 +72,11 @@ Paths + + + Logging + + Game Boy @@ -72,17 +84,10 @@ - - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - 0 + 5 @@ -1205,6 +1210,20 @@ + + + + + + 0 + + + 0 + + + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 97f1d285f..404bdd5dd 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -141,6 +141,7 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi connect(&m_inputController, &InputController::profileLoaded, m_shortcutController, &ShortcutController::loadProfile); m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL); + m_log.load(m_config); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); m_focusCheck.setInterval(200); @@ -432,7 +433,7 @@ void Window::exportSharkport() { } void Window::openSettingsWindow() { - SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController); + SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController, &m_log); #if defined(BUILD_GL) || defined(BUILD_GLES2) if (m_display->supportsShaders()) { settingsWindow->setShaderSelector(m_shaderView.get()); diff --git a/src/util/table.c b/src/util/table.c index 179d40a00..20f4e159b 100644 --- a/src/util/table.c +++ b/src/util/table.c @@ -111,7 +111,9 @@ void TableInsert(struct Table* table, uint32_t key, void* value) { struct TableList* list; TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) { if (value != lookupResult->value) { - table->deinitializer(lookupResult->value); + if (table->deinitializer) { + table->deinitializer(lookupResult->value); + } lookupResult->value = value; } return;