diff --git a/tools/chatgui/db/db_manager.cpp b/tools/chatgui/db/db_manager.cpp index 0a3c3a10..975d66cb 100644 --- a/tools/chatgui/db/db_manager.cpp +++ b/tools/chatgui/db/db_manager.cpp @@ -363,6 +363,14 @@ QString DbManager::getUiState(const QString& key) const { return val; } +int DbManager::getUiStateInt(const QString& key, int defaultVal) const { + QString val = getUiState(key); + if (val.isEmpty()) return defaultVal; + bool ok = false; + int v = val.toInt(&ok); + return ok ? v : defaultVal; +} + /* ── sync operations (read-only, for gui_bridge) ── */ QByteArray DbManager::syncListChannels() const { diff --git a/tools/chatgui/db/db_manager.h b/tools/chatgui/db/db_manager.h index 285c70ab..37a99d42 100644 --- a/tools/chatgui/db/db_manager.h +++ b/tools/chatgui/db/db_manager.h @@ -104,6 +104,7 @@ public: /* ── UI state (read-only используем QSettings, оставлен для совместимости) ── */ QString getUiState(const QString& key) const; + int getUiStateInt(const QString& key, int defaultVal) const; /* ── Sync-операции (read-only, для gui_bridge/inline) ── */ QByteArray syncListChannels() const; diff --git a/tools/chatgui/resources/chatgui.qrc b/tools/chatgui/resources/chatgui.qrc index ccb2e2c7..24f828e8 100644 --- a/tools/chatgui/resources/chatgui.qrc +++ b/tools/chatgui/resources/chatgui.qrc @@ -5,6 +5,5 @@ animations/greeting.tgs animations/stop.tgs animations/robot.tgs - sounds/msg_incoming.mp3 diff --git a/tools/chatgui/src/mainwindow.cpp b/tools/chatgui/src/mainwindow.cpp index 7d860090..5d2a49a9 100644 --- a/tools/chatgui/src/mainwindow.cpp +++ b/tools/chatgui/src/mainwindow.cpp @@ -21,6 +21,7 @@ #include #include #include +#include extern "C" { #include "../transport/chat_core.h" @@ -85,25 +86,65 @@ MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath, SoundManager* sm = SoundManager::instance(); if (sm->init()) { + QString soundDir; + QString msgInFile, msgOutFile, connectFile, disconnectFile; + QFile cfgFile(m_cfgPath); if (cfgFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - bool inGui = false; bool enabled = true; int volume = 80; + bool inGui = false; QTextStream in(&cfgFile); while (!in.atEnd()) { QString line = in.readLine().trimmed(); if (line == "[gui]") { inGui = true; continue; } if (line.startsWith("[") && line.endsWith("]")) inGui = false; if (!inGui) continue; - if (line.startsWith("sound_enabled=")) - enabled = line.mid(15).trimmed() != "0"; - else if (line.startsWith("sound_volume=")) - volume = line.mid(13).trimmed().toInt(); + if (line.startsWith("sound_dir=")) + soundDir = line.mid(10).trimmed(); + else if (line.startsWith("sound_msg_incoming=")) + msgInFile = line.mid(19).trimmed(); + else if (line.startsWith("sound_msg_outgoing=")) + msgOutFile = line.mid(19).trimmed(); + else if (line.startsWith("sound_connect=")) + connectFile = line.mid(14).trimmed(); + else if (line.startsWith("sound_disconnect=")) + disconnectFile = line.mid(17).trimmed(); } cfgFile.close(); - sm->setEnabled(enabled); - sm->setVolume(volume / 100.0f); } - sm->loadSound("msg_incoming", ":/sounds/msg_incoming.mp3"); + + if (soundDir.isEmpty()) soundDir = "sounds"; + QDir binDir(QApplication::applicationDirPath()); + QString baseDir = QDir::isAbsolutePath(soundDir) ? soundDir + : binDir.absoluteFilePath(soundDir); + auto resolvePath = [&baseDir](const QString& file) -> QString { + if (file.isEmpty()) return {}; + return QDir::cleanPath(baseDir + "/" + file); + }; + + struct { const char* name; QString file; } events[] = { + {"msg_incoming", msgInFile}, + {"msg_outgoing", msgOutFile}, + {"connect", connectFile}, + {"disconnect", disconnectFile}, + }; + for (auto& e : events) { + if (!e.file.isEmpty()) { + QString path = resolvePath(e.file); + sm->loadSound(e.name, path); + } + } + + if (m_db && m_db->isOpen()) { + sm->setEnabled(m_db->getUiStateInt("sound_enabled", 1)); + sm->setVolume(m_db->getUiStateInt("sound_volume", 80) / 100.0f); + QString soundEvents[] = {"msg_incoming", "msg_outgoing", "connect", "disconnect"}; + for (int i = 0; i < 4; i++) { + const QString& ek = soundEvents[i]; + bool on = m_db->getUiStateInt(QStringLiteral("sound_%1_on").arg(ek), i == 0 ? 1 : 0); + float ev = m_db->getUiStateInt(QStringLiteral("sound_%1_vol").arg(ek), 80) / 100.0f; + sm->setEvent(ek, on, ev); + } + } } if (m_db) { diff --git a/tools/chatgui/src/settingsdialog.cpp b/tools/chatgui/src/settingsdialog.cpp index cc5180ed..8ec92d49 100644 --- a/tools/chatgui/src/settingsdialog.cpp +++ b/tools/chatgui/src/settingsdialog.cpp @@ -86,7 +86,7 @@ SettingsDialog::SettingsDialog(const QString& configPath, DbManager* db, QWidget m_nodesPage = new NodesPage(db, m_pages); m_pages->addWidget(m_nodesPage); - m_soundPage = new SoundSettingsPage(m_pages); + m_soundPage = new SoundSettingsPage(db, m_pages); m_pages->addWidget(m_soundPage); rightLayout->addWidget(m_pages, 1); @@ -124,7 +124,7 @@ SettingsDialog::SettingsDialog(const QString& configPath, DbManager* db, QWidget // Load current values loadProfileFromConfig(m_configPath); m_networkPage->loadFromConfig(m_configPath); - m_soundPage->loadFromConfig(m_configPath); + m_soundPage->loadFromDb(); m_categoryList->setCurrentRow(0); } @@ -148,7 +148,7 @@ void SettingsDialog::onCategoryChanged(int row) { if (row == 2) m_databasePage->refreshStatus(); if (row == 3) m_statusPage->refreshStatus(); if (row == 4) m_nodesPage->refreshNodes(); - if (row == 5) m_soundPage->loadFromConfig(m_configPath); + if (row == 5) m_soundPage->loadFromDb(); } void SettingsDialog::onSave() { @@ -188,6 +188,6 @@ void SettingsDialog::onSave() { f.close(); m_networkPage->saveToConfig(m_configPath); - m_soundPage->saveToConfig(m_configPath); + m_soundPage->applyAndSave(); accept(); } diff --git a/tools/chatgui/src/sound_manager.cpp b/tools/chatgui/src/sound_manager.cpp index 7b8813d3..da477ab6 100644 --- a/tools/chatgui/src/sound_manager.cpp +++ b/tools/chatgui/src/sound_manager.cpp @@ -31,30 +31,17 @@ void SoundManager::shutdown() { if (!m_initialized) return; for (auto it = m_sounds.begin(); it != m_sounds.end(); ++it) { - if (it->sound) { - ma_sound_uninit(it->sound); - delete it->sound; - } - if (it->decoder) { - ma_decoder_uninit(it->decoder); - delete it->decoder; - } + if (it->sound) { ma_sound_uninit(it->sound); delete it->sound; } + if (it->decoder) { ma_decoder_uninit(it->decoder); delete it->decoder; } } m_sounds.clear(); - if (m_engine) { - ma_engine_uninit(m_engine); - delete m_engine; - m_engine = nullptr; - } + if (m_engine) { ma_engine_uninit(m_engine); delete m_engine; m_engine = nullptr; } m_initialized = false; qDebug("SoundManager: shutdown"); } -void SoundManager::setEnabled(bool enabled) { - m_enabled = enabled; - qDebug("SoundManager: enabled=%d", enabled); -} +void SoundManager::setEnabled(bool enabled) { m_enabled = enabled; } bool SoundManager::isEnabled() const { return m_enabled; } @@ -62,15 +49,25 @@ void SoundManager::setVolume(float volume) { if (volume < 0.0f) volume = 0.0f; if (volume > 1.0f) volume = 1.0f; m_volume = volume; - for (auto it = m_sounds.begin(); it != m_sounds.end(); ++it) { - if (it->sound) - ma_sound_set_volume(it->sound, m_volume); - } } float SoundManager::volume() const { return m_volume; } -bool SoundManager::loadSound(const QString& name, const QString& qrcPath) { +void SoundManager::setEvent(const QString& name, bool enabled, float volume) { + if (volume < 0.0f) volume = 0.0f; + if (volume > 1.0f) volume = 1.0f; + EventSettings es; + es.enabled = enabled; + es.volume = volume; + m_eventSettings[name] = es; + qDebug("SoundManager: event '%s' enabled=%d vol=%.2f", qPrintable(name), enabled, volume); +} + +SoundManager::EventSettings SoundManager::eventSettings(const QString& name) const { + return m_eventSettings.value(name); +} + +bool SoundManager::loadSound(const QString& name, const QString& filePath) { if (!m_initialized) { qWarning("SoundManager: not initialized, cannot load %s", qPrintable(name)); return false; @@ -80,9 +77,9 @@ bool SoundManager::loadSound(const QString& name, const QString& qrcPath) { return false; } - QFile f(qrcPath); + QFile f(filePath); if (!f.open(QIODevice::ReadOnly)) { - qWarning("SoundManager: cannot open %s", qPrintable(qrcPath)); + qWarning("SoundManager: cannot open %s", qPrintable(filePath)); return false; } @@ -105,26 +102,29 @@ bool SoundManager::loadSound(const QString& name, const QString& qrcPath) { 0, NULL, ls.sound); if (result != MA_SUCCESS) { qWarning("SoundManager: sound init failed for %s (%d)", qPrintable(name), result); - ma_decoder_uninit(ls.decoder); - delete ls.decoder; + ma_decoder_uninit(ls.decoder); delete ls.decoder; delete ls.sound; return false; } - ma_sound_set_volume(ls.sound, m_volume); m_sounds.insert(name, ls); qDebug("SoundManager: loaded '%s' from %s (%lld bytes)", - qPrintable(name), qPrintable(qrcPath), (long long)ls.mp3Data.size()); + qPrintable(name), qPrintable(filePath), (long long)ls.mp3Data.size()); return true; } void SoundManager::play(const QString& name) { if (!m_enabled || !m_initialized) return; + EventSettings es = m_eventSettings.value(name); + if (!es.enabled) return; + auto it = m_sounds.find(name); if (it == m_sounds.end()) return; ma_sound* s = it->sound; + float effectiveVolume = m_volume * es.volume; + ma_sound_set_volume(s, effectiveVolume); ma_sound_stop(s); ma_sound_seek_to_pcm_frame(s, 0); ma_sound_start(s); diff --git a/tools/chatgui/src/sound_manager.h b/tools/chatgui/src/sound_manager.h index 48e78bd2..a30172c1 100644 --- a/tools/chatgui/src/sound_manager.h +++ b/tools/chatgui/src/sound_manager.h @@ -12,6 +12,11 @@ struct ma_sound; class SoundManager : public QObject { Q_OBJECT public: + struct EventSettings { + bool enabled = true; + float volume = 0.8f; + }; + static SoundManager* instance(); bool init(); @@ -22,7 +27,10 @@ public: void setVolume(float volume); float volume() const; - bool loadSound(const QString& name, const QString& qrcPath); + void setEvent(const QString& name, bool enabled, float volume); + EventSettings eventSettings(const QString& name) const; + + bool loadSound(const QString& name, const QString& filePath); void play(const QString& name); private: @@ -39,5 +47,6 @@ private: bool m_initialized = false; bool m_enabled = true; float m_volume = 0.8f; + QHash m_eventSettings; QHash m_sounds; }; diff --git a/tools/chatgui/src/soundsettingspage.cpp b/tools/chatgui/src/soundsettingspage.cpp index 0864a558..6e38e13f 100644 --- a/tools/chatgui/src/soundsettingspage.cpp +++ b/tools/chatgui/src/soundsettingspage.cpp @@ -1,16 +1,22 @@ #include "soundsettingspage.h" #include "sound_manager.h" +#include "../db/db_manager.h" +#include "../transport/gui_bridge.h" +#include "../../lib/mem.h" +extern "C" { +#include "../transport/chat_core.h" +} #include #include #include #include #include -#include -#include +#include #include -SoundSettingsPage::SoundSettingsPage(QWidget* parent) +SoundSettingsPage::SoundSettingsPage(DbManager* db, QWidget* parent) : QWidget(parent) + , m_db(db) { auto* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 12, 0, 0); @@ -28,8 +34,9 @@ SoundSettingsPage::SoundSettingsPage(QWidget* parent) m_volumeSlider = new QSlider(Qt::Horizontal, this); m_volumeSlider->setRange(0, 100); m_volumeSlider->setFixedWidth(200); - m_volumeSlider->setStyleSheet("QSlider::groove:horizontal { height: 6px; border-radius: 3px; background: palette(mid); }" - "QSlider::handle:horizontal { width: 14px; height: 14px; margin: -4px 0; border-radius: 7px; background: #3390EC; }"); + m_volumeSlider->setStyleSheet( + "QSlider::groove:horizontal { height: 6px; border-radius: 3px; background: palette(mid); }" + "QSlider::handle:horizontal { width: 14px; height: 14px; margin: -4px 0; border-radius: 7px; background: #3390EC; }"); volLayout->addWidget(m_volumeSlider); m_volumeLabel = new QLabel("80%", this); @@ -39,82 +46,136 @@ SoundSettingsPage::SoundSettingsPage(QWidget* parent) volLayout->addStretch(); layout->addLayout(volLayout); + auto* sep = new QFrame(this); + sep->setFrameShape(QFrame::HLine); + sep->setStyleSheet("QFrame { color: palette(mid); }"); + layout->addWidget(sep); + + auto* header = new QLabel("Events", this); + header->setStyleSheet("font-size: 13px; font-weight: bold;"); + layout->addWidget(header); + + m_events[0] = {"msg_incoming", "Incoming message", nullptr, nullptr, nullptr}; + m_events[1] = {"msg_outgoing", "Outgoing message", nullptr, nullptr, nullptr}; + m_events[2] = {"connect", "Peer connected", nullptr, nullptr, nullptr}; + m_events[3] = {"disconnect", "Peer disconnected", nullptr, nullptr, nullptr}; + + for (int i = 0; i < 4; i++) { + auto& e = m_events[i]; + auto* row = new QHBoxLayout(); + row->setSpacing(8); + + auto* lbl = new QLabel(e.label, this); + lbl->setFixedWidth(140); + lbl->setStyleSheet("font-size: 13px;"); + row->addWidget(lbl); + + e.check = new QCheckBox(this); + row->addWidget(e.check); + + e.slider = new QSlider(Qt::Horizontal, this); + e.slider->setRange(0, 100); + e.slider->setFixedWidth(100); + e.slider->setStyleSheet( + "QSlider::groove:horizontal { height: 4px; border-radius: 2px; background: palette(mid); }" + "QSlider::handle:horizontal { width: 10px; height: 10px; margin: -3px 0; border-radius: 5px; background: #3390EC; }"); + row->addWidget(e.slider); + + e.volLabel = new QLabel("80%", this); + e.volLabel->setFixedWidth(30); + e.volLabel->setStyleSheet("font-size: 12px;"); + row->addWidget(e.volLabel); + + row->addStretch(); + layout->addLayout(row); + + connect(e.slider, &QSlider::valueChanged, this, [this, i](int val) { + m_events[i].volLabel->setText(QString::number(val) + "%"); + }); + } + layout->addStretch(); - connect(m_enabledCheck, &QCheckBox::stateChanged, this, &SoundSettingsPage::onEnabledChanged); + connect(m_enabledCheck, &QCheckBox::stateChanged, this, [this](int state) { + bool on = (state == Qt::Checked); + m_volumeSlider->setEnabled(on); + m_volumeLabel->setEnabled(on); + }); connect(m_volumeSlider, &QSlider::valueChanged, this, [this](int val) { m_volumeLabel->setText(QString::number(val) + "%"); }); } -void SoundSettingsPage::onEnabledChanged(int state) { - m_volumeSlider->setEnabled(state == Qt::Checked); - m_volumeLabel->setEnabled(state == Qt::Checked); -} - -void SoundSettingsPage::loadFromConfig(const QString& path) { - QFile f(path); - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return; - - bool enabled = true; - int volume = 80; - bool inGui = false; - QTextStream in(&f); - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - if (line == "[gui]") { inGui = true; continue; } - if (line.startsWith("[") && line.endsWith("]")) inGui = false; - if (!inGui) continue; - if (line.startsWith("sound_enabled=")) - enabled = line.mid(QString("sound_enabled=").size()).trimmed() != "0"; - else if (line.startsWith("sound_volume=")) - volume = line.mid(QString("sound_volume=").size()).trimmed().toInt(); - } - f.close(); +void SoundSettingsPage::loadFromDb() { + if (!m_db) return; + bool enabled = m_db->getUiStateInt("sound_enabled", 1); m_enabledCheck->setChecked(enabled); - m_volumeSlider->setValue(volume); + + int vol = m_db->getUiStateInt("sound_volume", 80); + m_volumeSlider->setValue(vol); m_volumeSlider->setEnabled(enabled); m_volumeLabel->setEnabled(enabled); - m_volumeLabel->setText(QString::number(volume) + "%"); + m_volumeLabel->setText(QString::number(vol) + "%"); + + for (int i = 0; i < 4; i++) { + auto& e = m_events[i]; + QString onKey = QStringLiteral("sound_%1_on").arg(e.key); + QString volKey = QStringLiteral("sound_%1_vol").arg(e.key); + bool on = m_db->getUiStateInt(onKey, i == 0 ? 1 : 0); // only msg_incoming on by default + int ev = m_db->getUiStateInt(volKey, 80); + e.check->setChecked(on); + e.slider->setValue(ev); + e.volLabel->setText(QString::number(ev) + "%"); + } } -void SoundSettingsPage::saveToConfig(const QString& path) const { - QFile f(path); - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return; - QString content = f.readAll(); - f.close(); - - QStringList lines = content.split('\n'); - - bool inGui = false; - bool foundEnabled = false, foundVolume = false; - for (int i = 0; i < lines.size(); i++) { - QString line = lines[i].trimmed(); - if (line == "[gui]") { inGui = true; continue; } - if (line.startsWith("[") && line.endsWith("]")) inGui = false; - if (!inGui) continue; - - if (line.startsWith("sound_enabled=")) { - lines[i] = QString("sound_enabled=%1").arg(m_enabledCheck->isChecked() ? 1 : 0); - foundEnabled = true; - } else if (line.startsWith("sound_volume=")) { - lines[i] = QString("sound_volume=%1").arg(m_volumeSlider->value()); - foundVolume = true; +void SoundSettingsPage::applyAndSave() { + SoundManager* sm = SoundManager::instance(); + + bool enabled = m_enabledCheck->isChecked(); + float masterVol = m_volumeSlider->value() / 100.0f; + sm->setEnabled(enabled); + sm->setVolume(masterVol); + + for (int i = 0; i < 4; i++) { + auto& e = m_events[i]; + bool on = e.check->isChecked(); + float ev = e.slider->value() / 100.0f; + sm->setEvent(e.key, on, ev); + + /* async DB persist — key\0value\0 */ + QString onKey = QStringLiteral("sound_%1_on").arg(e.key); + QString volKey = QStringLiteral("sound_%1_vol").arg(e.key); + QByteArray onVal = on ? QByteArray("1") : QByteArray("0"); + QByteArray volVal = QByteArray::number(e.slider->value()); + + for (int j = 0; j < 2; j++) { + const char* k = j == 0 ? onKey.toUtf8().constData() : volKey.toUtf8().constData(); + const char* v = j == 0 ? onVal.constData() : volVal.constData(); + size_t klen = strlen(k), vlen = strlen(v); + size_t total = klen + 1 + vlen + 1; + if (total > 256) continue; + void* arg = u_malloc(total); + memcpy(arg, k, klen + 1); + memcpy((char*)arg + klen + 1, v, vlen + 1); + gui_bridge_post_uasync_fn(chat_core_save_ui_state_trampoline, arg); } } - if (!foundEnabled || !foundVolume) { - for (int i = 0; i < lines.size(); i++) { - if (lines[i].trimmed() == "[gui]") { - if (!foundEnabled) lines.insert(i + 1, QString("sound_enabled=%1").arg(m_enabledCheck->isChecked() ? 1 : 0)); - if (!foundVolume) lines.insert(i + 1, QString("sound_volume=%1").arg(m_volumeSlider->value())); - break; - } + /* save global settings */ + { + const char* keys[] = {"sound_enabled", "sound_volume"}; + QByteArray vals[] = {enabled ? QByteArray("1") : QByteArray("0"), + QByteArray::number(m_volumeSlider->value())}; + for (int j = 0; j < 2; j++) { + size_t klen = strlen(keys[j]), vlen = vals[j].size(); + size_t total = klen + 1 + vlen + 1; + if (total > 256) continue; + void* arg = u_malloc(total); + memcpy(arg, keys[j], klen + 1); + memcpy((char*)arg + klen + 1, vals[j].constData(), vlen + 1); + gui_bridge_post_uasync_fn(chat_core_save_ui_state_trampoline, arg); } } - - f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); - f.write(lines.join('\n').toUtf8()); - f.close(); } diff --git a/tools/chatgui/src/soundsettingspage.h b/tools/chatgui/src/soundsettingspage.h index e553b278..bf1e0fa1 100644 --- a/tools/chatgui/src/soundsettingspage.h +++ b/tools/chatgui/src/soundsettingspage.h @@ -2,6 +2,7 @@ #include +class DbManager; class QCheckBox; class QSlider; class QLabel; @@ -9,16 +10,23 @@ class QLabel; class SoundSettingsPage : public QWidget { Q_OBJECT public: - explicit SoundSettingsPage(QWidget* parent = nullptr); + explicit SoundSettingsPage(DbManager* db, QWidget* parent = nullptr); - void loadFromConfig(const QString& configPath); - void saveToConfig(const QString& configPath) const; - -private slots: - void onEnabledChanged(int state); + void loadFromDb(); + void applyAndSave(); private: - QCheckBox* m_enabledCheck; - QSlider* m_volumeSlider; - QLabel* m_volumeLabel; + struct EventRow { + const char* key; + const char* label; + QCheckBox* check = nullptr; + QSlider* slider = nullptr; + QLabel* volLabel = nullptr; + }; + + DbManager* m_db = nullptr; + QCheckBox* m_enabledCheck; + QSlider* m_volumeSlider; + QLabel* m_volumeLabel; + EventRow m_events[4]; }; diff --git a/tools/chatgui/transport/chat_core.c b/tools/chatgui/transport/chat_core.c index 674311f5..708e1642 100644 --- a/tools/chatgui/transport/chat_core.c +++ b/tools/chatgui/transport/chat_core.c @@ -394,6 +394,27 @@ void chat_core_update_my_name_trampoline(void* arg) { u_free(arg); } +void chat_core_save_ui_state(const char* key, const char* value) { + if (!g_cc.initialized || !key || !value) return; + sqlite3_stmt* st = NULL; + sqlite3_prepare_v2(g_cc.db, + "INSERT OR REPLACE INTO ui_state(key, value) VALUES(?, ?)", -1, &st, NULL); + if (st) { + sqlite3_bind_text(st, 1, key, -1, SQLITE_STATIC); + sqlite3_bind_text(st, 2, value, -1, SQLITE_STATIC); + sqlite3_step(st); sqlite3_finalize(st); + } +} + +struct save_ui_state_arg { char data[256]; }; +void chat_core_save_ui_state_trampoline(void* arg) { + struct save_ui_state_arg* a = (struct save_ui_state_arg*)arg; + const char* key = a->data; + const char* value = key + strlen(key) + 1; + chat_core_save_ui_state(key, value); + u_free(arg); +} + static void chat_core_collect_status(void); void chat_core_collect_status_trampoline(void* arg) { diff --git a/tools/chatgui/transport/chat_core.h b/tools/chatgui/transport/chat_core.h index bdea89f3..3eda1ba4 100644 --- a/tools/chatgui/transport/chat_core.h +++ b/tools/chatgui/transport/chat_core.h @@ -98,6 +98,10 @@ void chat_core_update_my_name(const char* name); void chat_core_update_my_name_trampoline(void* arg); void chat_core_sync_my_addresses(void); +/* Сохранение key-value в ui_state (вызывается из uasync-потока) */ +void chat_core_save_ui_state(const char* key, const char* value); +void chat_core_save_ui_state_trampoline(void* arg); + /* Трамплин для gui_bridge_post_uasync (GUI → uasync) */ void chat_core_submit_trampoline(void* arg); diff --git a/tools/chatgui/transport/node_config.cpp b/tools/chatgui/transport/node_config.cpp index 2556e37f..5ea5dd14 100644 --- a/tools/chatgui/transport/node_config.cpp +++ b/tools/chatgui/transport/node_config.cpp @@ -37,7 +37,8 @@ static const QSet CONTROL_KEYS = { static const QSet GUI_KEYS = { "db_path", "debug_file", "debug_level", "debug_categories", - "sound_enabled", "sound_volume" + "sound_dir", "sound_msg_incoming", "sound_msg_outgoing", + "sound_connect", "sound_disconnect" }; static const QSet ALLOWED_KEYS_KEYS = {