From 6cafff200bae468374f1f1ba1bca7d665eb66bf4 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 24 Jul 2026 03:47:15 +0300 Subject: [PATCH] chatgui: add SIGINT/SIGTERM handler (as in utun.c), save scroll pos on every debounce Signal handler via g_shutdown + uasync_wakeup ensures clean uasync poll exit. updateReadPosition() now saves both lastReadMsgId and lastPosMsgId on every scroll stop (300ms debounce), not just when read position changes. connect(stopped, quit) triggers graceful Qt shutdown after uasync cleanup. --- tools/chatgui/src/mainwindow.cpp | 8 +++----- tools/chatgui/src/messagelist.cpp | 19 ++++++++++++++----- tools/chatgui/transport/utun_node.cpp | 20 +++++++++++++++++++- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/tools/chatgui/src/mainwindow.cpp b/tools/chatgui/src/mainwindow.cpp index 1e9d93d2..add220b0 100644 --- a/tools/chatgui/src/mainwindow.cpp +++ b/tools/chatgui/src/mainwindow.cpp @@ -84,6 +84,8 @@ MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath, } MainWindow::~MainWindow() { + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MainWindow: ~MainWindow — saving channel state"); + if (m_messageList) m_messageList->saveChannelState(); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MainWindow: ~MainWindow — stopping utun node"); if (m_node) { m_node->stop(); delete m_node; m_node = nullptr; } DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MainWindow: ~MainWindow done"); @@ -120,6 +122,7 @@ void MainWindow::setupNode() { connect(m_node, &UtunNode::stopped, this, []() { qDebug("UtunNode stopped"); }); + connect(m_node, &UtunNode::stopped, qApp, &QApplication::quit); if (!m_cfgDebugFile.isEmpty()) m_node->setDebugFile(m_cfgDebugFile); @@ -230,11 +233,6 @@ void MainWindow::setupConnects() { m_channelList->setChannelUnreadCount(channelId, m_db->getUnreadCount(channelId, lastReadMsgId)); }); - - connect(qApp, &QApplication::aboutToQuit, this, [this]() { - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "MainWindow: aboutToQuit — saving channel state"); - if (m_messageList) m_messageList->saveChannelState(); - }); } void MainWindow::setupTray() { diff --git a/tools/chatgui/src/messagelist.cpp b/tools/chatgui/src/messagelist.cpp index 701863c8..57fc80da 100644 --- a/tools/chatgui/src/messagelist.cpp +++ b/tools/chatgui/src/messagelist.cpp @@ -352,19 +352,28 @@ void MessageList::updateReadPosition() { newReadMsgId = msgId; } + QModelIndex topIdx = m_view->indexAt(QPoint(0, 0)); + qint64 lastPosMsgId = topIdx.isValid() ? topIdx.data(MsgIdRole).toLongLong() : 0; + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, - "MessageList::updateReadPosition ch=%s oldRead=%lld newRead=%lld lastVisRow=%d/%d", + "MessageList::updateReadPosition ch=%s oldRead=%lld newRead=%lld pos=%lld lastVisRow=%d/%d", qPrintable(m_currentChannelId), (long long)m_lastReadMsgId, - (long long)newReadMsgId, lastFullyVisibleRow, m_model->rowCount()); + (long long)newReadMsgId, (long long)lastPosMsgId, + lastFullyVisibleRow, m_model->rowCount()); + + QByteArray cid = m_currentChannelId.toUtf8(); if (newReadMsgId > m_lastReadMsgId) { m_lastReadMsgId = newReadMsgId; - - QByteArray cid = m_currentChannelId.toUtf8(); QByteArray key = "ch_" + cid + "_last_read"; QByteArray valStr = QByteArray::number(m_lastReadMsgId); chat_core_save_ui_state(key.constData(), valStr.constData()); - emit readPositionChanged(m_currentChannelId, m_lastReadMsgId); } + + if (lastPosMsgId > 0) { + QByteArray posKey = "ch_" + cid + "_last_pos"; + QByteArray posVal = QByteArray::number(lastPosMsgId); + chat_core_save_ui_state(posKey.constData(), posVal.constData()); + } } diff --git a/tools/chatgui/transport/utun_node.cpp b/tools/chatgui/transport/utun_node.cpp index 53525877..e242ebd1 100644 --- a/tools/chatgui/transport/utun_node.cpp +++ b/tools/chatgui/transport/utun_node.cpp @@ -2,6 +2,7 @@ #include "utun_node.h" #include #include +#include #include "../../lib/socket_compat.h" #include "../db/db_manager.h" @@ -34,6 +35,15 @@ extern "C" { static thread_local UtunNode* g_currentNode = nullptr; +static volatile sig_atomic_t g_shutdown = 0; +static struct UASYNC* g_signal_ua = nullptr; + +static void signal_handler(int sig) { + (void)sig; + g_shutdown = 1; + if (g_signal_ua) uasync_wakeup(g_signal_ua); +} + UtunNode::UtunNode(QObject* parent) : QObject(parent) {} UtunNode::~UtunNode() { @@ -321,10 +331,18 @@ void UtunNode::runLoop() { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "chat_core + chat_sync initialized"); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "utun_node: entering poll loop"); - while (!m_stop) { + g_signal_ua = ua; + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + while (!m_stop && !g_shutdown) { uasync_poll(ua, 100); } + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + g_signal_ua = nullptr; + etcp_router_bind(m_instance, ETCP_RT_ID_CHAT, nullptr); chat_sync_destroy(m_instance); chat_core_destroy(m_instance);