Qt: Revamp logging configuration
jump to
@@ -194,13 +194,26 @@ #define FILL_BITS(SRC, START, END) ((SRC) | MAKE_MASK(START, END))
#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
@@ -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)
@@ -104,7 +104,7 @@ ++key;
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 @@ mLogFilterSet(filter, key, ivalue);
} 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); } +} +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(struct mLogFilter* filter, int category, enum mLogLevel level) { - int value = (intptr_t) TableLookup(&filter->levels, category); + +bool mLogFilterTest(const struct mLogFilter* filter, int category, enum mLogLevel level) { + int value = mLogFilterLevels(filter, category); if (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); - if (value) { - TableInsert(&filter->levels, category, (void*)(intptr_t) value); - return value & level; - } } - return level & filter->defaultLevels; + return value; } mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status")
@@ -524,7 +524,7 @@ char oolBuf[0x101];
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); }
@@ -90,6 +90,7 @@ InputProfile.cpp
KeyEditor.cpp LoadSaveState.cpp LogController.cpp + LogConfigModel.cpp LogView.cpp MapView.cpp MemoryModel.cpp@@ -104,6 +105,7 @@ PlacementControl.cpp
PrinterView.cpp RegisterView.cpp ROMInfo.cpp + RotatedHeaderView.cpp SavestateButton.cpp SensorView.cpp SettingsView.cpp
@@ -82,7 +82,8 @@ void saveOverride(const Override&);
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();
@@ -10,8 +10,9 @@ #include "CoreController.h"
#include "CoreManager.h" #include "ConfigController.h" #include "Display.h" +#include "LogController.h" +#include "VFileDevice.h" #include "Window.h" -#include "VFileDevice.h" #include <QFileInfo> #include <QFileOpenEvent>@@ -64,6 +65,8 @@
if (!m_configController->getQtOption("audioDriver").isNull()) { AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController->getQtOption("audioDriver").toInt())); } + + LogController::global()->load(m_configController); connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup); }
@@ -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 <algorithm> + +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); +}
@@ -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 <QAbstractItemModel> + +#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<ConfigSetting> m_cache; + int m_levels; +}; + +}
@@ -5,6 +5,8 @@ * 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 "LogController.h" +#include "ConfigController.h" + using namespace QGBA; LogController LogController::s_global(mLOG_ALL);@@ -19,20 +21,32 @@ m_filter.defaultLevels = levels;
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<void (LogController::*)(int)>(&LogController::levelsSet), &s_global, static_cast<void (LogController::*)(int)>(&LogController::setLevels)); + connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsEnabled), &s_global, static_cast<void (LogController::*)(int)>(&LogController::enableLevels)); + connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsDisabled), &s_global, static_cast<void (LogController::*)(int)>(&LogController::disableLevels)); } } 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<mLogLevel>(level))) { return;@@ -53,6 +67,31 @@
void LogController::disableLevels(int levels) { m_filter.defaultLevels &= ~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() {
@@ -14,6 +14,8 @@ #include <QStringList>
namespace QGBA { +class ConfigController; + class LogController : public QObject { Q_OBJECT@@ -38,24 +40,36 @@ LogController(int levels, QObject* parent = nullptr);
~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 @@
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) }
@@ -43,19 +43,19 @@ this, &LogView::setMaxLines);
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<void (LogController::*)(int)>(&LogController::levelsSet), this, &LogView::setLevels); + connect(log, static_cast<void (LogController::*)(int)>(&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<void (LogController::*)(int)>(&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<void (LogController::*)(int)>(&LogController::enableLevels)); + connect(this, &LogView::levelsDisabled, log, static_cast<void (LogController::*)(int)>(&LogController::disableLevels)); } void LogView::postLog(int level, int category, const QString& log) {
@@ -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 <QPainter> + +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(); +}
@@ -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 <QHeaderView> + +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; +}; + +}
@@ -11,6 +11,7 @@ #include "Display.h"
#include "GBAApp.h" #include "GBAKeyEditor.h" #include "InputController.h" +#include "RotatedHeaderView.h" #include "ShaderSelector.h" #include "ShortcutView.h"@@ -24,9 +25,10 @@ #ifdef M_CORE_GB
QList<enum GBModel> 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 @@ m_ui.languages->setCurrentIndex(m_ui.languages->count() - 1);
} } + 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 @@ m_controller->setQtOption("language", language.bcp47Name());
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));@@ -534,6 +543,8 @@ }
m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT); 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");
@@ -8,6 +8,7 @@
#include <QDialog> #include "ColorPicker.h" +#include "LogConfigModel.h" #include <mgba/core/core.h>@@ -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 @@
ConfigController* m_controller; InputController* m_input; ShaderSelector* m_shader = nullptr; + LogConfigModel m_logModel; #ifdef M_CORE_GB uint32_t m_gbColors[12]{};
@@ -23,6 +23,13 @@ <layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint"> <enum>QLayout::SetFixedSize</enum> </property> + <item row="2" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> <item row="1" column="0"> <widget class="QListWidget" name="tabs"> <property name="sizePolicy">@@ -67,22 +74,20 @@ </property>
</item> <item> <property name="text"> + <string>Logging</string> + </property> + </item> + <item> + <property name="text"> <string>Game Boy</string> </property> </item> </widget> </item> - <item row="2" column="0" colspan="2"> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> <item row="1" column="1"> <widget class="QStackedWidget" name="stackedWidget"> <property name="currentIndex"> - <number>0</number> + <number>5</number> </property> <widget class="QWidget" name="av"> <layout class="QFormLayout" name="formLayout">@@ -1201,6 +1206,20 @@ <widget class="QCheckBox" name="cheatsSameDir">
<property name="text"> <string>Same directory as the ROM</string> </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="logging"> + <layout class="QGridLayout" name="a"> + <item row="0" column="0"> + <widget class="QTableView" name="loggingView"> + <attribute name="horizontalHeaderDefaultSectionSize"> + <number>0</number> + </attribute> + <attribute name="horizontalHeaderMinimumSectionSize"> + <number>0</number> + </attribute> </widget> </item> </layout>
@@ -141,6 +141,7 @@ connect(&m_focusCheck, &QTimer::timeout, this, &Window::focusCheck);
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::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());
@@ -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;