mirror of https://github.com/mgba-emu/mgba.git
Qt: Revamp logging configuration
This commit is contained in:
parent
bd8fe4d878
commit
4e39875e7b
|
@ -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)))
|
#define TEST_FILL_BITS(SRC, START, END, TEST) ((TEST) ? (FILL_BITS(SRC, START, END)) : (CLEAR_BITS(SRC, START, END)))
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
#pragma section(".CRT$XCU",read)
|
||||||
#define ATTRIBUTE_UNUSED
|
#define ATTRIBUTE_UNUSED
|
||||||
#define ATTRIBUTE_FORMAT(X, Y, Z)
|
#define ATTRIBUTE_FORMAT(X, Y, Z)
|
||||||
#define ATTRIBUTE_NOINLINE
|
#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
|
#else
|
||||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||||
#define ATTRIBUTE_FORMAT(X, Y, Z) __attribute__((format(X, Y, Z)))
|
#define ATTRIBUTE_FORMAT(X, Y, Z) __attribute__((format(X, Y, Z)))
|
||||||
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
|
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||||
|
#define CONSTRUCTOR(FN) static __attribute__((constructor)) void FN(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define DECL_BITFIELD(NAME, TYPE) typedef TYPE NAME
|
#define DECL_BITFIELD(NAME, TYPE) typedef TYPE NAME
|
||||||
|
|
|
@ -47,24 +47,23 @@ struct mCoreConfig;
|
||||||
void mLogFilterInit(struct mLogFilter*);
|
void mLogFilterInit(struct mLogFilter*);
|
||||||
void mLogFilterDeinit(struct mLogFilter*);
|
void mLogFilterDeinit(struct mLogFilter*);
|
||||||
void mLogFilterLoad(struct mLogFilter*, const struct mCoreConfig*);
|
void mLogFilterLoad(struct mLogFilter*, const struct mCoreConfig*);
|
||||||
|
void mLogFilterSave(const struct mLogFilter*, struct mCoreConfig*);
|
||||||
void mLogFilterSet(struct mLogFilter*, const char* category, int levels);
|
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)
|
ATTRIBUTE_FORMAT(printf, 3, 4)
|
||||||
void mLog(int category, enum mLogLevel level, const char* format, ...);
|
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) \
|
#define mLOG_DEFINE_CATEGORY(CATEGORY, NAME, ID) \
|
||||||
int _mLOG_CAT_ ## CATEGORY (void) { \
|
int _mLOG_CAT_ ## CATEGORY; \
|
||||||
static int category = 0; \
|
CONSTRUCTOR(_mLOG_CAT_ ## CATEGORY ## _INIT) { \
|
||||||
if (!category) { \
|
_mLOG_CAT_ ## CATEGORY = mLogGenerateCategory(NAME, ID); \
|
||||||
category = mLogGenerateCategory(NAME, ID); \
|
}
|
||||||
} \
|
|
||||||
return category; \
|
|
||||||
} \
|
|
||||||
const char* _mLOG_CAT_ ## CATEGORY ## _ID = ID;
|
|
||||||
|
|
||||||
mLOG_DECLARE_CATEGORY(STATUS)
|
mLOG_DECLARE_CATEGORY(STATUS)
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ static void _setFilterLevel(const char* key, const char* value, enum mCoreConfig
|
||||||
char* end;
|
char* end;
|
||||||
int ivalue = strtol(value, &end, 10);
|
int ivalue = strtol(value, &end, 10);
|
||||||
if (ivalue == 0) {
|
if (ivalue == 0) {
|
||||||
ivalue = INT_MIN; // Zero is reserved
|
ivalue = 0x80; // Zero is reserved
|
||||||
}
|
}
|
||||||
if (!end) {
|
if (!end) {
|
||||||
return;
|
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) {
|
void mLogFilterLoad(struct mLogFilter* filter, const struct mCoreConfig* config) {
|
||||||
|
HashTableClear(&filter->categories);
|
||||||
|
TableClear(&filter->levels);
|
||||||
|
|
||||||
mCoreConfigEnumerate(config, "logLevel.", _setFilterLevel, filter);
|
mCoreConfigEnumerate(config, "logLevel.", _setFilterLevel, filter);
|
||||||
filter->defaultLevels = mLOG_ALL;
|
filter->defaultLevels = mLOG_ALL;
|
||||||
mCoreConfigGetIntValue(config, "logLevel", &filter->defaultLevels);
|
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) {
|
void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels) {
|
||||||
|
levels |= 0x80;
|
||||||
HashTableInsert(&filter->categories, category, (void*)(intptr_t) levels);
|
HashTableInsert(&filter->categories, category, (void*)(intptr_t) levels);
|
||||||
// Can't do this eagerly because not all categories are initialized immediately
|
// Can't do this eagerly because not all categories are initialized immediately
|
||||||
int cat = mLogCategoryById(category);
|
int cat = mLogCategoryById(category);
|
||||||
if (cat >= 0) {
|
if (cat >= 0) {
|
||||||
TableInsert(&filter->levels, cat, (void*)(intptr_t) levels);
|
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) {
|
if (value) {
|
||||||
return value & level;
|
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;
|
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")
|
mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status")
|
||||||
|
|
|
@ -524,7 +524,7 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
|
||||||
strncpy(oolBuf, gba->debugString, sizeof(oolBuf) - 1);
|
strncpy(oolBuf, gba->debugString, sizeof(oolBuf) - 1);
|
||||||
memset(gba->debugString, 0, sizeof(gba->debugString));
|
memset(gba->debugString, 0, sizeof(gba->debugString));
|
||||||
oolBuf[0x100] = '\0';
|
oolBuf[0x100] = '\0';
|
||||||
mLog(_mLOG_CAT_GBA_DEBUG(), level, "%s", oolBuf);
|
mLog(_mLOG_CAT_GBA_DEBUG, level, "%s", oolBuf);
|
||||||
}
|
}
|
||||||
gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags);
|
gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,7 @@ set(SOURCE_FILES
|
||||||
KeyEditor.cpp
|
KeyEditor.cpp
|
||||||
LoadSaveState.cpp
|
LoadSaveState.cpp
|
||||||
LogController.cpp
|
LogController.cpp
|
||||||
|
LogConfigModel.cpp
|
||||||
LogView.cpp
|
LogView.cpp
|
||||||
MapView.cpp
|
MapView.cpp
|
||||||
MemoryModel.cpp
|
MemoryModel.cpp
|
||||||
|
@ -104,6 +105,7 @@ set(SOURCE_FILES
|
||||||
PrinterView.cpp
|
PrinterView.cpp
|
||||||
RegisterView.cpp
|
RegisterView.cpp
|
||||||
ROMInfo.cpp
|
ROMInfo.cpp
|
||||||
|
RotatedHeaderView.cpp
|
||||||
SavestateButton.cpp
|
SavestateButton.cpp
|
||||||
SensorView.cpp
|
SensorView.cpp
|
||||||
SettingsView.cpp
|
SettingsView.cpp
|
||||||
|
|
|
@ -82,7 +82,8 @@ public:
|
||||||
|
|
||||||
Configuration* input() { return mCoreConfigGetInput(&m_config); }
|
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();
|
static const QString& configDir();
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,9 @@
|
||||||
#include "CoreManager.h"
|
#include "CoreManager.h"
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
#include "Display.h"
|
#include "Display.h"
|
||||||
#include "Window.h"
|
#include "LogController.h"
|
||||||
#include "VFileDevice.h"
|
#include "VFileDevice.h"
|
||||||
|
#include "Window.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QFileOpenEvent>
|
#include <QFileOpenEvent>
|
||||||
|
@ -65,6 +66,8 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config)
|
||||||
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController->getQtOption("audioDriver").toInt()));
|
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController->getQtOption("audioDriver").toInt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogController::global()->load(m_configController);
|
||||||
|
|
||||||
connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup);
|
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 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "LogController.h"
|
#include "LogController.h"
|
||||||
|
|
||||||
|
#include "ConfigController.h"
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
LogController LogController::s_global(mLOG_ALL);
|
LogController LogController::s_global(mLOG_ALL);
|
||||||
|
@ -19,9 +21,9 @@ LogController::LogController(int levels, QObject* parent)
|
||||||
|
|
||||||
if (this != &s_global) {
|
if (this != &s_global) {
|
||||||
connect(&s_global, &LogController::logPosted, this, &LogController::postLog);
|
connect(&s_global, &LogController::logPosted, this, &LogController::postLog);
|
||||||
connect(this, &LogController::levelsSet, &s_global, &LogController::setLevels);
|
connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsSet), &s_global, static_cast<void (LogController::*)(int)>(&LogController::setLevels));
|
||||||
connect(this, &LogController::levelsEnabled, &s_global, &LogController::enableLevels);
|
connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsEnabled), &s_global, static_cast<void (LogController::*)(int)>(&LogController::enableLevels));
|
||||||
connect(this, &LogController::levelsDisabled, &s_global, &LogController::disableLevels);
|
connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsDisabled), &s_global, static_cast<void (LogController::*)(int)>(&LogController::disableLevels));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,10 +31,22 @@ LogController::~LogController() {
|
||||||
mLogFilterDeinit(&m_filter);
|
mLogFilterDeinit(&m_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LogController::levels(int category) const {
|
||||||
|
return mLogFilterLevels(&m_filter, category);
|
||||||
|
}
|
||||||
|
|
||||||
LogController::Stream LogController::operator()(int category, int level) {
|
LogController::Stream LogController::operator()(int category, int level) {
|
||||||
return Stream(this, category, 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) {
|
void LogController::postLog(int level, int category, const QString& string) {
|
||||||
if (!mLogFilterTest(&m_filter, category, static_cast<mLogLevel>(level))) {
|
if (!mLogFilterTest(&m_filter, category, static_cast<mLogLevel>(level))) {
|
||||||
return;
|
return;
|
||||||
|
@ -55,6 +69,31 @@ void LogController::disableLevels(int levels) {
|
||||||
emit levelsDisabled(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() {
|
LogController* LogController::global() {
|
||||||
return &s_global;
|
return &s_global;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
|
class ConfigController;
|
||||||
|
|
||||||
class LogController : public QObject {
|
class LogController : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -38,24 +40,36 @@ public:
|
||||||
~LogController();
|
~LogController();
|
||||||
|
|
||||||
int levels() const { return m_filter.defaultLevels; }
|
int levels() const { return m_filter.defaultLevels; }
|
||||||
|
int levels(int category) const;
|
||||||
mLogFilter* filter() { return &m_filter; }
|
mLogFilter* filter() { return &m_filter; }
|
||||||
|
|
||||||
Stream operator()(int category, int level);
|
Stream operator()(int category, int level);
|
||||||
|
|
||||||
static LogController* global();
|
static LogController* global();
|
||||||
static QString toString(int level);
|
static QString toString(int level);
|
||||||
|
static int categoryId(const char*);
|
||||||
|
|
||||||
|
void load(const ConfigController*);
|
||||||
|
void save(ConfigController*) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void logPosted(int level, int category, const QString& log);
|
void logPosted(int level, int category, const QString& log);
|
||||||
void levelsSet(int levels);
|
void levelsSet(int levels);
|
||||||
void levelsEnabled(int levels);
|
void levelsEnabled(int levels);
|
||||||
void levelsDisabled(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:
|
public slots:
|
||||||
void postLog(int level, int category, const QString& string);
|
void postLog(int level, int category, const QString& string);
|
||||||
void setLevels(int levels);
|
void setLevels(int levels);
|
||||||
void enableLevels(int levels);
|
void enableLevels(int levels);
|
||||||
void disableLevels(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:
|
private:
|
||||||
mLogFilter m_filter;
|
mLogFilter m_filter;
|
||||||
|
@ -63,6 +77,6 @@ private:
|
||||||
static LogController s_global;
|
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 @@ LogView::LogView(LogController* log, QWidget* parent)
|
||||||
m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT);
|
m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT);
|
||||||
|
|
||||||
connect(log, &LogController::logPosted, this, &LogView::postLog);
|
connect(log, &LogController::logPosted, this, &LogView::postLog);
|
||||||
connect(log, &LogController::levelsSet, this, &LogView::setLevels);
|
connect(log, static_cast<void (LogController::*)(int)>(&LogController::levelsSet), this, &LogView::setLevels);
|
||||||
connect(log, &LogController::levelsEnabled, [this](int level) {
|
connect(log, static_cast<void (LogController::*)(int)>(&LogController::levelsEnabled), [this](int level) {
|
||||||
bool s = blockSignals(true);
|
bool s = blockSignals(true);
|
||||||
setLevel(level, true);
|
setLevel(level, true);
|
||||||
blockSignals(s);
|
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);
|
bool s = blockSignals(true);
|
||||||
setLevel(level, false);
|
setLevel(level, false);
|
||||||
blockSignals(s);
|
blockSignals(s);
|
||||||
});
|
});
|
||||||
connect(this, &LogView::levelsEnabled, log, &LogController::enableLevels);
|
connect(this, &LogView::levelsEnabled, log, static_cast<void (LogController::*)(int)>(&LogController::enableLevels));
|
||||||
connect(this, &LogView::levelsDisabled, log, &LogController::disableLevels);
|
connect(this, &LogView::levelsDisabled, log, static_cast<void (LogController::*)(int)>(&LogController::disableLevels));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogView::postLog(int level, int category, const QString& log) {
|
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 "GBAApp.h"
|
#include "GBAApp.h"
|
||||||
#include "GBAKeyEditor.h"
|
#include "GBAKeyEditor.h"
|
||||||
#include "InputController.h"
|
#include "InputController.h"
|
||||||
|
#include "RotatedHeaderView.h"
|
||||||
#include "ShaderSelector.h"
|
#include "ShaderSelector.h"
|
||||||
#include "ShortcutView.h"
|
#include "ShortcutView.h"
|
||||||
|
|
||||||
|
@ -24,9 +25,10 @@ using namespace QGBA;
|
||||||
QList<enum GBModel> SettingsView::s_gbModelList;
|
QList<enum GBModel> SettingsView::s_gbModelList;
|
||||||
#endif
|
#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)
|
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||||
, m_controller(controller)
|
, m_controller(controller)
|
||||||
|
, m_logModel(logController)
|
||||||
{
|
{
|
||||||
m_ui.setupUi(this);
|
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* shortcutView = new ShortcutView();
|
||||||
shortcutView->setController(shortcutController);
|
shortcutView->setController(shortcutController);
|
||||||
shortcutView->setInputController(inputController);
|
shortcutView->setInputController(inputController);
|
||||||
|
@ -430,6 +437,8 @@ void SettingsView::updateConfig() {
|
||||||
emit languageChanged();
|
emit languageChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_logModel.save(m_controller);
|
||||||
|
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
GBModel modelGB = s_gbModelList[m_ui.gbModel->currentIndex()];
|
GBModel modelGB = s_gbModelList[m_ui.gbModel->currentIndex()];
|
||||||
m_controller->setOption("gb.model", GBModelToName(modelGB));
|
m_controller->setOption("gb.model", GBModelToName(modelGB));
|
||||||
|
@ -535,6 +544,8 @@ void SettingsView::reloadConfig() {
|
||||||
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);
|
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);
|
||||||
m_ui.saveStateCheats->setChecked(saveState & SAVESTATE_CHEATS);
|
m_ui.saveStateCheats->setChecked(saveState & SAVESTATE_CHEATS);
|
||||||
|
|
||||||
|
m_logModel.reset();
|
||||||
|
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
QString modelGB = m_controller->getOption("gb.model");
|
QString modelGB = m_controller->getOption("gb.model");
|
||||||
if (!modelGB.isNull()) {
|
if (!modelGB.isNull()) {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "ColorPicker.h"
|
#include "ColorPicker.h"
|
||||||
|
#include "LogConfigModel.h"
|
||||||
|
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ class SettingsView : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent = nullptr);
|
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent = nullptr);
|
||||||
~SettingsView();
|
~SettingsView();
|
||||||
|
|
||||||
void setShaderSelector(ShaderSelector* shaderSelector);
|
void setShaderSelector(ShaderSelector* shaderSelector);
|
||||||
|
@ -53,6 +54,7 @@ private:
|
||||||
ConfigController* m_controller;
|
ConfigController* m_controller;
|
||||||
InputController* m_input;
|
InputController* m_input;
|
||||||
ShaderSelector* m_shader = nullptr;
|
ShaderSelector* m_shader = nullptr;
|
||||||
|
LogConfigModel m_logModel;
|
||||||
|
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
uint32_t m_gbColors[12]{};
|
uint32_t m_gbColors[12]{};
|
||||||
|
|
|
@ -23,6 +23,13 @@
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
<enum>QLayout::SetFixedSize</enum>
|
<enum>QLayout::SetFixedSize</enum>
|
||||||
</property>
|
</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">
|
<item row="1" column="0">
|
||||||
<widget class="QListWidget" name="tabs">
|
<widget class="QListWidget" name="tabs">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -65,6 +72,11 @@
|
||||||
<string>Paths</string>
|
<string>Paths</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Logging</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Game Boy</string>
|
<string>Game Boy</string>
|
||||||
|
@ -72,17 +84,10 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<item row="1" column="1">
|
||||||
<widget class="QStackedWidget" name="stackedWidget">
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="av">
|
<widget class="QWidget" name="av">
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
@ -1205,6 +1210,20 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</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>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="gb">
|
<widget class="QWidget" name="gb">
|
||||||
<layout class="QFormLayout" name="formLayout_1">
|
<layout class="QFormLayout" name="formLayout_1">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
|
|
@ -141,6 +141,7 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi
|
||||||
connect(&m_inputController, &InputController::profileLoaded, m_shortcutController, &ShortcutController::loadProfile);
|
connect(&m_inputController, &InputController::profileLoaded, m_shortcutController, &ShortcutController::loadProfile);
|
||||||
|
|
||||||
m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
|
m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
|
||||||
|
m_log.load(m_config);
|
||||||
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
||||||
m_focusCheck.setInterval(200);
|
m_focusCheck.setInterval(200);
|
||||||
|
|
||||||
|
@ -432,7 +433,7 @@ void Window::exportSharkport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::openSettingsWindow() {
|
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 defined(BUILD_GL) || defined(BUILD_GLES2)
|
||||||
if (m_display->supportsShaders()) {
|
if (m_display->supportsShaders()) {
|
||||||
settingsWindow->setShaderSelector(m_shaderView.get());
|
settingsWindow->setShaderSelector(m_shaderView.get());
|
||||||
|
|
|
@ -111,7 +111,9 @@ void TableInsert(struct Table* table, uint32_t key, void* value) {
|
||||||
struct TableList* list;
|
struct TableList* list;
|
||||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
|
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
|
||||||
if (value != lookupResult->value) {
|
if (value != lookupResult->value) {
|
||||||
table->deinitializer(lookupResult->value);
|
if (table->deinitializer) {
|
||||||
|
table->deinitializer(lookupResult->value);
|
||||||
|
}
|
||||||
lookupResult->value = value;
|
lookupResult->value = value;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue