Replace stale desktop architecture documentation and honor the temporary directory
This commit is contained in:
1 parent
b4553db769
commit
72f4258410
2 files changed
+126
-219
No files matched your search
+11
-1
@@ -403,7 +403,17 @@ struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct
|
||||
// Create instance from config text string (writes to temp file, parses, cleans up)
|
||||
struct UTUN_INSTANCE* utun_instance_create_from_str(struct UASYNC* ua, const char* config_text) {
|
||||
if (!ua || !config_text) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "instance create requires uasync and config text"); return NULL; }
|
||||
char tmp_path[] = "/tmp/utun_cfg_XXXXXX";
|
||||
const char* tmp_dir = getenv("TMPDIR");
|
||||
#ifdef _WIN32
|
||||
if (!tmp_dir || !tmp_dir[0]) tmp_dir = getenv("TEMP");
|
||||
if (!tmp_dir || !tmp_dir[0]) tmp_dir = getenv("TMP");
|
||||
#endif
|
||||
if (!tmp_dir || !tmp_dir[0]) tmp_dir = "/tmp";
|
||||
char tmp_path[1024];
|
||||
int path_len = snprintf(tmp_path, sizeof(tmp_path), "%s/utun_cfg_XXXXXX", tmp_dir);
|
||||
if (path_len < 0 || (size_t)path_len >= sizeof(tmp_path)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "temporary config directory path too long"); return NULL;
|
||||
}
|
||||
int fd = mkstemp(tmp_path);
|
||||
if (fd < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "cannot create temporary config: %s", strerror(errno)); return NULL; }
|
||||
size_t len = strlen(config_text);
|
||||
|
||||
+115
-218
@@ -1,253 +1,150 @@
|
||||
# chatgui
|
||||
# Desktop chatgui
|
||||
|
||||
## 1. Назначение
|
||||
## Запуск и сборка
|
||||
|
||||
Десктопный GUI-чат (Telegram-подобный) со встроенным P2P uTun-узлом. Приложение объединяет полнофункциональный интерфейс обмена сообщениями и децентрализованный транспортный слой в едином процессе. Рассчитан на работу без центральных серверов — вся маршрутизация, синхронизация и доставка сообщений идут напрямую между узлами по ETCP-протоколу.
|
||||
Приложение называется `vibechat`: Qt Widgets UI и общее C-ядро в одном процессе.
|
||||
Обычная desktop-сборка использует Qt Multimedia, FFmpeg и встроенный голосовой стек.
|
||||
CMake требует C++20 для GUI и C99 для ядра; Qt 6 предпочтителен, предусмотрен Qt 5.15 fallback.
|
||||
Зависимости и платформенные настройки находятся в `CMakeLists.txt` и `libutun/CMakeLists.txt`.
|
||||
|
||||
Язык интерфейса: русский или английский. По умолчанию используется первый поддерживаемый язык из списка языков интерфейса ОС;
|
||||
если поддерживаемых языков нет, используется английский. Выбор в «Настройки → Профиль → Язык интерфейса»:
|
||||
«Язык системы», English или Русский. Смена языка применяется после перезапуска, настройка хранится в `[gui] language=system|en|ru`.
|
||||
Региональный формат дат и чисел остаётся системным. Диагностические дампы ядра сохраняют исходные имена полей.
|
||||
Из корня репозитория:
|
||||
|
||||
Каталоги `translations/chatgui_en.ts` и `translations/chatgui_ru.ts` компилируются `lrelease` и встраиваются в приложение вместе
|
||||
с `qtbase_ru.qm` для стандартных виджетов Qt. Для сборки нужны инструменты Qt Linguist и пакет переводов Qt
|
||||
(Debian/Ubuntu: `qt6-tools-dev-tools`, `qt6-translations-l10n`; для Qt 5: `qttools5-dev-tools`, `qttranslations5-l10n`).
|
||||
После добавления строк обновить каталоги из `tools/chatgui`:
|
||||
`lupdate src -no-obsolete -locations relative -ts translations/chatgui_en.ts translations/chatgui_ru.ts`,
|
||||
заполнить новые переводы и пересобрать. Проверка: `ctest --test-dir build -R test_gui_language --output-on-failure`.
|
||||
|
||||
## 2. Архитектура
|
||||
|
||||
### 2.1. Уровни приложения
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ GUI (Qt 6/5 Widgets, C++20) │ поток GUI
|
||||
│ ├─ MainWindow (QSplitter) │
|
||||
│ ├─ ChannelList / MessageList │
|
||||
│ ├─ MessageDelegate (бабблы+реакции) │
|
||||
│ ├─ EmojiPanel (статика+анимация) │
|
||||
│ └─ LottieIcon / AnimTimer │
|
||||
├──────────────────────────────────────┤
|
||||
│ GuiBridge (Qt signals ↔ uasync) │ очередь событий
|
||||
│ gui_bridge_post / uasync_post │
|
||||
├──────────────────────────────────────┤
|
||||
│ Транспорт (C, поток uasync) │ поток uasync
|
||||
│ ├─ UtunNode (std::thread + UTUN_INSTANCE)│
|
||||
│ ├─ ChatCore (отправка, БД, invite) │
|
||||
│ ├─ ChatSync (P2P синхронизация) │
|
||||
│ ├─ MemberSync (мемберы через Merkle)│
|
||||
│ └─ MerkleSync (Merkle-дерево, 5 ур.)│
|
||||
├──────────────────────────────────────┤
|
||||
│ libutun (C, статическая библиотека) │ uasync
|
||||
│ ├─ uasync (event loop, таймеры) │
|
||||
│ ├─ ETCP (надежная доставка, ACK) │
|
||||
│ ├─ SecureChannel (X25519+AES-CCM) │
|
||||
│ ├─ conn_mgr (управление подключ.) │
|
||||
│ ├─ topo_node/topo_group (BGP) │
|
||||
│ └─ ntp_time (синхронизация времени) │
|
||||
├──────────────────────────────────────┤
|
||||
│ SQLite3 (WAL mode) │ чтение+запись
|
||||
│ ├─ DbManager (C++, read-only для GUI)│
|
||||
│ └─ chat_core (C, write из uasync) │
|
||||
└──────────────────────────────────────┘
|
||||
```sh
|
||||
cmake -S tools/chatgui -B tools/chatgui/build
|
||||
cmake --build tools/chatgui/build --target vibechat -j4
|
||||
./tools/chatgui/build/vibechat
|
||||
```
|
||||
|
||||
### 2.2. Потоковая модель
|
||||
Корневой `build.sh` также собирает GUI при наличии `tools/chatgui/build/CMakeCache.txt`.
|
||||
Скрипты desktop-сборки: `build.sh`, `build_nix.sh`, `build_nix_full.sh`, `build.bat` внутри этого каталога.
|
||||
Native API и протоколы находятся в общем `../../src`; копий chat_core/chat_sync в `transport/` нет.
|
||||
|
||||
Приложение работает в двух потоках:
|
||||
## Владельцы и потоки
|
||||
|
||||
- **Поток GUI** — QApplication, все виджеты, отрисовка, QTimer для анимаций (AnimTimer 30fps). Только *чтение* из SQLite через DbManager.
|
||||
- **Поток uasync** — UTUN_INSTANCE в выделенном std::thread. Вся сеть, криптография, ETCP, синхронизация, *запись* в SQLite. Один uasync-экземпляр на поток (правило u_async).
|
||||
- GUI-поток владеет QApplication, виджетами, моделями, Qt-таймерами и desktop media engines.
|
||||
- `UtunNode` создаёт uasync и UTUN_INSTANCE в worker, явно запускает core/chat и медиапривязки.
|
||||
`[gui] utun_enabled=yes` дополнительно запускает UTUN; создание TUN управляется отдельно.
|
||||
- Сетевые операции, подписанные записи, чат и изменения данных SQLite выполняет uasync.
|
||||
Декодеры, FFmpeg, media_async и audio callbacks работают в своих workers и не обращаются к QWidget.
|
||||
- `start()` возвращает подтверждённую готовность либо причину отказа с откатом ресурсов.
|
||||
Идентичность, NTP и фактический db_path читаются в GUI через защищённую копию состояния.
|
||||
- `requestStop()` просит выход, `stop()` выполняется в GUI и присоединяет worker.
|
||||
Worker перед destroy ждёт подтверждения `stopping` в GUI. В этом сигнале GUI снимает SQLite handle;
|
||||
слоты не должны вызывать stop/join. Синхронный stop сам выполняет отсоединение до join.
|
||||
- Instance уничтожает worker; uasync освобождается после join. Старый queued `stopped` отсеивается по поколению запуска.
|
||||
|
||||
**Мост между потоками (gui_bridge):**
|
||||
- **uasync → GUI:** `gui_bridge_post(event_type, data, len)` — сериализованные события в Qt main event loop через `QMetaObject::invokeMethod` с `Qt::QueuedConnection`. 10 типов событий: MSG_RECEIVED, CONNECT_RESULT, NEW_PEER, CHANNEL_UPDATED, MEMBERS_CHANGED, MY_NODE_ID, AUTO_CONNECT_STATUS, CHANNEL_PEERS_ONLINE, DB_READY, STATUS_REFRESH.
|
||||
- **GUI → uasync:** `gui_bridge_post_uasync_fn(fn, arg)` — выполнение функции в uasync-потоке через `uasync_post`. Используется для отправки сообщений, создания каналов, invite-подключений.
|
||||
Штатный выход MainWindow ставит stop после принятых записей геометрии/read/scroll.
|
||||
Закрытие крестиком сохраняет геометрию и скрывает окно в tray.
|
||||
Перед native teardown очищаются выбранный target, ожидания auto-play и заимствованный handle БД.
|
||||
|
||||
Всё взаимодействие с БД (INSERT/UPDATE/DELETE) выполняется только из uasync-потока. GUI читает через DbManager (read-only), синхронизация не требуется — SQLite в WAL-режиме обеспечивает одновременное чтение.
|
||||
## Мост GUI ↔ ядро
|
||||
|
||||
## 3. Ключевые компоненты
|
||||
`transport/gui_bridge.h` описывает контракты; номера и форматы событий определяет `src/chat/chat_event.h`.
|
||||
|
||||
### 3.1. GUI-слой
|
||||
Ядро отправляет `gui_bridge_post(type, data, len)`: байты копируются синхронно,
|
||||
обработка и зарегистрированные callbacks выполняются через Qt queued connection в GUI.
|
||||
Данные callback заимствованы только до возврата; строка с длиной не обязана иметь NUL.
|
||||
|
||||
**MainWindow** (`src/mainwindow.h`) — главное окно 900×600. Горизонтальный QSplitter из трёх панелей:
|
||||
- **ChannelList** — список каналов (QListView + QStandardItemModel), контекстное меню (invite, join, create group, settings).
|
||||
- **MessageList** — список сообщений канала (ChatView + QStandardItemModel), поле ввода InputBar, EmojiPanel.
|
||||
- **AccountList** — список участников канала (MemberListModel), онлайн-статус, имена.
|
||||
GUI использует API с владением запросами:
|
||||
|
||||
Системный трей: QSystemTrayIcon + QMenu (показать/скрыть/выход). Закрытие окна сворачивает в трей.
|
||||
- `gui_bridge_post_setting` копирует key/value в ui_state либо live chat_setting.
|
||||
- `gui_bridge_submit_message` и `gui_bridge_submit_dm_message` фиксируют composer request_id.
|
||||
Результат локальной записи возвращается MSG_SUBMITTED; отмена до исполнения даёт отказ.
|
||||
- `gui_bridge_submit_attachment` передаёт подготовку PCM/файла; отмена освобождает PCM owner,
|
||||
compressor и собственный временный source. При отказе постановки caller вызывает `attachment_send_discard`.
|
||||
- `gui_bridge_download_attachment` обслуживает delegate, menu и auto-play.
|
||||
- Обычные звонки, приглашения в рацию, редактирование участников, member-files и JOIN имеют специализированные API.
|
||||
- `gui_bridge_post_bound_fn` предназначен для оставшихся простых native-команд:
|
||||
один u_malloc-блок без вложенных ресурсов и адрес его поля inst.
|
||||
Проверка running, привязка к ядру и постановка выполняются под mutex.
|
||||
При -1 caller освобождает блок; при принятии его освобождает trampoline либо мост при отмене.
|
||||
- `gui_bridge_post_uasync_fn` оставляет arg заимствованным; он подходит только для данных
|
||||
с отдельно гарантированным временем жизни. Чтение `gui_bridge_get_inst()` не резервирует ядро.
|
||||
|
||||
**Уведомления о сообщениях** (`src/message_notification.*`, `src/user_activity_monitor.*`): чужое новое сообщение
|
||||
в канале или личной беседе показывает одно окно возле трея, если главное окно не активно. Загрузка истории не уведомляет:
|
||||
`MSG_RECEIVED` содержит `[ch_id_len:1][ch_id:var][author_node_id:8][initial_sync:1]`.
|
||||
Без активности уведомление остаётся; первый ввод запускает отсчёт 10 секунд, дальнейший ввод его не продлевает.
|
||||
Новое сообщение заменяет содержимое и отменяет прежний отсчёт. Нажатие открывает соответствующий чат; крестик закрывает окно.
|
||||
Detach закрывает приём и отменяет ожидающие команды до освобождения native-ресурсов.
|
||||
Приём команды подтверждает enqueue, а не успех её последующего исполнения.
|
||||
|
||||
Windows использует `GetLastInputInfo`, macOS — CoreGraphics, X11 — опциональную библиотеку Xss (`libxss-dev`).
|
||||
Wayland/XWayland в GNOME использует асинхронный `org.gnome.Mutter.IdleMonitor` через D-Bus. При нативном Qt Wayland
|
||||
доступен также опциональный KF6/KF5 KIdleTime с установленным backend композитора; idle inhibitors могут ограничивать его события.
|
||||
X11 idle time в Wayland-сессии не используется: он не гарантирует активность в других приложениях.
|
||||
Если системный механизм недоступен, учитываются только события внутри ChatGUI; закрытие крестиком остаётся доступным.
|
||||
При отсутствии координат трея выбирается угол доступной области экрана; нативный Wayland управляет позицией и порядком окон.
|
||||
В X11/XWayland уведомление — отдельное overlay-окно (`override_redirect`) типа notification:
|
||||
оконный менеджер не привязывает его к одному виртуальному столу, оно остаётся доступным при переключении столов.
|
||||
Уведомление не является дочерним окном главного чата: сворачивание или рабочий стол главного окна не определяют его видимость.
|
||||
На нативном Wayland поверхность пересоздаётся при новом сообщении, чтобы не сохранять прежний рабочий стол;
|
||||
выбор текущего стола и возможность показа поверх других окон остаются политикой композитора.
|
||||
Показ/скрытие и причины ограничений логируются в `general`; запуск отсчёта и первая активность — на уровне `debug`.
|
||||
Проверка: `ctest --test-dir build -R 'test_message_(notification|events)' --output-on-failure`.
|
||||
При доступном X11/XWayland: `./build/test_message_notification_x11` проверяет overlay после показа, обновления и скрытия.
|
||||
## SQLite и профиль
|
||||
|
||||
**ChatView** (`src/chatview.h`) — QListView с фоновым изображением и drag-to-select. При движении >5px от точки нажатия стартует выделение сообщений. Ctrl+C копирует: для одного — выделенный текст, для нескольких — `[time] author: text`. Сигнал `hoveredIndexChanged` используется для активации анимаций эмодзи в MessageList.
|
||||
Ядро владеет общей SQLite: `db_path/chats.db`, без db_path — in-memory.
|
||||
Соединение открывается с FULLMUTEX, WAL и foreign_keys.
|
||||
`DbManager` заимствует handle; обычные запросы GUI только читают и возвращают Qt-копии.
|
||||
Каждый statement закрывается до возврата; ошибки bind/step/finalize не выдаются за частичные успешные снимки.
|
||||
Низкоуровневый `prepareOrNull` отклоняет write statements, caller обязан завершить read statement до следующего GUI-события.
|
||||
WAL не продлевает время жизни handle и не заменяет барьер stopping.
|
||||
|
||||
**MessageList / MessageDelegate** — баббл-интерфейс сообщений:
|
||||
- Аватары (32×32, цвет зависит от node_id), бабблы со скруглёнными углами (r=10px) и хвостиком.
|
||||
- Цитаты: цветная полоса + мини-аватар + автор + текст.
|
||||
- Реакции (эмодзи) в status bar под бабблом.
|
||||
- Inline-анимированные эмодзи: при hover над сообщением — перебор кадров из LottieIcon, отрисовка поверх текста через QTextLayout.
|
||||
- Растягивающийся InputBar (QTextEdit, Enter — отправка, Shift+Enter — перевод строки).
|
||||
Основные данные: `channels`, `msg_<channel>`, `peers_<channel>`, `nodes`, `node_addresses`,
|
||||
`accounts`, `ui_state`, DM history/media и attachment keys.
|
||||
Сообщение имеет подписанный 32-байтный message_id, версию и reply_to.
|
||||
Локальный rowid применяется для read/scroll; timestamp/message_id определяют порядок истории.
|
||||
История PM имеет generation: старый снимок или результат удаления не применяется к новой истории.
|
||||
|
||||
Карточка опроса открывает «Голосование / результаты» одиночным левым кликом по телу сообщения.
|
||||
Перетаскивание выделяет текст; аватар и строка реакций обрабатывают свои действия.
|
||||
В DbManager нет устаревшего sync-протокола, долгоживущих sync-курсоров и GUI-удаления peers.
|
||||
Синхронизация принадлежит общему ядру: `src/chat`, `src/routing_layer`.
|
||||
Подробные wire-контракты находятся в заголовках этих модулей и документах `../../doc`.
|
||||
|
||||
**EmojiPanel** (`src/emojipanel.h`) — QTabWidget с категориями (смайлики, жесты, символы, анимации). Каждая категория — QGridLayout из EmojiButton (QPushButton без Q_OBJECT, hover-анимация через per-widget QTimer). Статические эмодзи — Unicode символы. Анимированные — TGS-файлы, рендерятся в EmojiButton::paintEvent.
|
||||
PRAGMA quick_check/integrity_check проверяют текстовый ответ SQLite, optimize оценивает SQL-успех
|
||||
без требования строки результата. Обслуживание синхронно; handle сохраняется живым до возврата.
|
||||
Оно не меняет подписанные сообщения/членство.
|
||||
|
||||
**LottieIcon** (`src/lottieicon.h`) — загрузка и рендер TGS-анимаций:
|
||||
1. Gzip-декомпрессия через zlib (`inflateInit2` с `16+MAX_WBITS`)
|
||||
2. Парсинг Lottie JSON через rlottie C API (`lottie_animation_from_data`)
|
||||
3. Рендер кадра: `lottie_animation_render` → ARGB32 буфер → QImage
|
||||
Фактический каталог профиля публикует UtunNode после применения override.
|
||||
GUI использует его для media; пустой native db_path означает текущий каталог.
|
||||
Идентичность INI нормализует общий config_updater, сохраняя корректный private key.
|
||||
NodeConfig хранит GUI-проекцию, сетевые секции проверяет общий parser в worker.
|
||||
|
||||
5 встроенных TGS-анимаций (вшиты в бинарник через chatgui.qrc): sparkles, white_flag, greeting, stop_hand, robot.
|
||||
## Модель и отрисовка сообщений
|
||||
|
||||
**AnimTimer** (`src/animtimer.h`) — синглтон-таймер 30fps с refcount-механизмом:
|
||||
- `activate()` / `deactivate()` увеличивают/уменьшают счётчик ссылок.
|
||||
- Срабатывает только когда есть активные анимации в видимых сообщениях.
|
||||
- Сигнал `ticked` обновляет кадры всех зарегистрированных LottieIcon.
|
||||
`MainWindow` связывает ChannelList, MessageList, AccountList, tray, media windows и bridge.
|
||||
`MessageList` владеет моделью, composer и панелями; db/recorder заимствованы.
|
||||
Основная история ограничена 200 строками. Навигация по reply загружает окно вокруг оригинала;
|
||||
пока это окно открыто, новые сообщения не вытесняют его и не двигают read watermark свежей истории.
|
||||
|
||||
**Invite-ссылки** (`src/invite_link.h`) — кодирование/декодирование приглашений в формат `utun://...` (base64, содержит channel_id, pubkey, список адресов). QR-коды через zxing-cpp.
|
||||
`MessageDelegate` использует один layout для sizeHint, paint и hit-test.
|
||||
Аватар имеет размер 54px; узкий режим убирает его. Поддерживаются текст, цитаты, реакции,
|
||||
опросы, voice, файлы, изображения, видео и состояния encrypted-вложений.
|
||||
Медиа открывается левым кликом по телу; drag или release без соответствующего press не запускает действие.
|
||||
QPersistentModelIndex и QPointer защищают от замены модели и уничтожения UI в модальном цикле.
|
||||
Опросы и реакции реализованы в `src/messagefeedback.cpp`; канал диалога фиксируется до exec.
|
||||
|
||||
### 3.2. Транспортный слой
|
||||
PCM-позиция voice берётся из SoundManager. Таймер обновляет состояние вне paint,
|
||||
останавливается после затухания/паузы и не удерживает уничтоженный view.
|
||||
VoicePlayer отменяет устаревшую подготовку; загруженные PCM-сессии принадлежат SoundManager.
|
||||
Auto-play не реагирует на initial sync/собственные сообщения, подавляет повторы и различает (channel,rowid).
|
||||
Encrypted voice распознаётся по проверенным metadata/plaintext после decrypt, неизвестный MIME не считается voice.
|
||||
|
||||
**UtunNode** (`transport/utun_node.h`) — C++ обёртка над UTUN_INSTANCE:
|
||||
- В `runLoop()` создаёт uasync и UTUN_INSTANCE, явно запускает core/chat и проверяет медиапривязки; `[gui] utun_enabled=yes` добавляет UTUN.
|
||||
- `start()` подтверждает готовность или возвращает причину отказа с откатом ресурсов. Команды и события чата проходят через GuiBridge.
|
||||
- `stop()` просит выход из poll, ждёт worker; instance освобождает worker, затем GUI освобождает uasync.
|
||||
- Идентичность и NTP доступны GUI через защищённую копию состояния, без чтения живых структур ядра.
|
||||
- Перед чтением существующего профиля desktop вызывает общий `config_ensure_keys_and_node_id()`: корректный X25519 private key сохраняется, public key и node_id восстанавливаются из него.
|
||||
Превью видео создаёт ограниченный QThreadPool; paint не запускает декодеры.
|
||||
Сетевые загрузки и подготовка вложений принадлежат native media_delivery/media_async.
|
||||
Обычный phone call и адресное приглашение в групповую рацию используют разные сервисы;
|
||||
FINISHED приглашения в рацию завершает приглашение, дальнейшая рация живёт обычным циклом.
|
||||
|
||||
**NodeConfig** (`transport/node_config.h`) — управление INI-конфигом uTun-узла:
|
||||
- `create(name)` создаёт новый профиль с X25519-ключами, производным node_id, автосокетами TCP+UDP и control 127.0.0.1:9999. Ошибки генерации/сохранения возвращаются вызывающему.
|
||||
- `load()` читает собственную GUI-проекцию без DNS и заменяет её целиком; удалённые поля не остаются от предыдущего чтения. Ошибки полей доступны через `hasErrors()/errors()`.
|
||||
- Сетевые секции, включая `[server:name]` и `[client:name]`, проверяет общий парсер в потоке ядра. GUI не содержит отдельного списка допустимых сетевых опций.
|
||||
- `save()` обновляет поля идентичности, control и `[gui]`/`[debug]`, сохраняя остальные секции и комментарии. Неиспользуемый режим полной перезаписи из неполной модели удалён.
|
||||
- ConfigUpdater проверяет UTF-8/физический INI-формат и публикует весь набор правок через QSaveFile; replace обновляет все дубли, append добавляет значение. Отказ записи не усекает исходный файл.
|
||||
## Анимации, уведомления и язык
|
||||
|
||||
**ChatCore** (`transport/chat_core.h`) — центральный API чата в потоке uasync:
|
||||
- `chat_core_submit_message` — отправка сообщения: сохранение в локальную БД (таблица `msg_<channel_id>`), рассылка онлайн-пирам через etcp_send.
|
||||
- `chat_core_create_channel` — создание канала: генерация X25519/Ed25519 ключей канала, подпись Ed25519, запись в `channels`, членство владельца.
|
||||
- `chat_core_connect_from_invite` — подключение к пиру из invite-ссылки.
|
||||
- `chat_core_connect_auto` — авто-подключение к узлу из SQLite (topo_node), коллбэк с результатом.
|
||||
- Работа с БД: цепочка хешей (chain hash = SHA256(prev_hash || timestamp || data_hash)), список каналов, список пиров.
|
||||
AnimTimer управляет общими LottieIcon; регистрация выполняется до EmojiPanel, shutdown — до очистки иконок.
|
||||
TGS декомпрессируется zlib и рендерится rlottie. Unicode emoji и ресурсы перечислены в `src/emoji.cpp` и `resources/chatgui.qrc`.
|
||||
|
||||
**ChatSync** (`transport/chat_sync.h`) — P2P синхронизация каналов:
|
||||
- ETCP-сервисы: `0x30` (chat_sync — синхронизация каналов), `0x31` (member_sync — синхронизация участников).
|
||||
- Протокол сообщений: INIT_SYNC → INIT_RESP → SEND_DATA ↔ PUSH+ACK_PUSH → SYNC_DONE.
|
||||
- Batch-загрузка: до 32 сообщений за запрос, sparse-синхронизация (до 16 диапазонов).
|
||||
- Таймаут пира: 5 секунд.
|
||||
- Auto-connect: параллельное подключение до 10 узлов, останавливается при 3 успешных. Переключение группы при смене канала.
|
||||
Окно нового сообщения показывает MessageNotification, активность пользователя отслеживает UserActivityMonitor.
|
||||
Нотификация не показывается для своего сообщения, initial sync и активного главного окна.
|
||||
Ограничения размещения/показа определяет compositor; детали — в заголовках этих компонентов.
|
||||
|
||||
**MemberSync** (`transport/member_sync.h`) — синхронизация участников канала на основе Merkle-деревьев:
|
||||
- Данные: таблицы `peers_<channel_id>`, `nodes`, `node_addresses`.
|
||||
- Хеш мембера = SHA256(node_id || x25519 || ed25519 || join_sig || join_ts || update_sig || update_ts || addrs).
|
||||
- Автоматический пересчёт дерева при добавлении/удалении/обновлении мембера.
|
||||
- Онлайн-статус: `member_sync_set_online(node_id, 0/1)`.
|
||||
Язык выбирается через `[gui] language=system|en|ru`; смена применяется после перезапуска.
|
||||
Русский и английский каталоги компилирует lrelease, инструменты Linguist обязательны.
|
||||
Региональные форматы дат/чисел остаются системными.
|
||||
Новые UI-строки обновляются из `tools/chatgui`:
|
||||
|
||||
**MerkleSync** (`transport/merkle_sync.h`) — универсальный протокол синхронизации на Merkle-деревьях:
|
||||
- 5-уровневое префиксное дерево над 64-битными ключами (node_id). 32 бакета на уровень (по 5 бит).
|
||||
- SHA256-хеши бакетов. Два пира обмениваются хешами, находят различающиеся бакеты, передают только их.
|
||||
- Wire-протокол: MSG_HASHES → MSG_REQUEST → MSG_BATCH, рекурсивно до совпадения хешей.
|
||||
- Таймаут 10s, 3 ретрая. Фоновая проверка консистентности (`merkle_sync_bg_check`).
|
||||
- Не зависит от типа данных — потребитель предоставляет три коллбэка (update_bucket_hash, get_items, apply_items). member_sync адаптирует это под мемберов каналов.
|
||||
|
||||
### 3.3. База данных
|
||||
|
||||
**DbManager** (`db/db_manager.h`) — C++/Qt обёртка над SQLite3 (read-only для GUI-потока):
|
||||
- **Схема:** `channels` (каналы с ключами X25519/Ed25519, подписи), `msg_<channel_id>` (per-channel таблицы сообщений с chain_hash), `peers_<channel_id>` (участники), `nodes` (узлы), `node_addresses`, `accounts`, `ui_state`, `merkle_tree_hash`.
|
||||
- Чтение: `getChannels()`, `getMessages()`, `getChannelMembers()`, `loadNodeInfo()`, `getAccounts()`.
|
||||
- Методы для GUI: список каналов, сообщения с пагинацией, участники, онлайн-статус.
|
||||
- Sync-операции (курсоры): `syncListChannels()`, `syncCount()`, `syncChainHashAt()`, `syncCursorOpen/Next/Close()`.
|
||||
- Проверка целостности: `integrityCheckQuick()` (PRAGMA quick_check), `integrityCheckFull()` (PRAGMA integrity_check), `integrityOptimize()` (PRAGMA optimize).
|
||||
|
||||
## 4. Сборка
|
||||
|
||||
**Система сборки:** CMake 3.16+
|
||||
**Стандарты:** C++20 (GUI), C99 (транспорт/libutun)
|
||||
**Зависимости:**
|
||||
- Qt 6 (Widgets + Network) или Qt 5.15+
|
||||
- rlottie (анимированные эмодзи)
|
||||
- zlib (gzip-декомпрессия TGS)
|
||||
- OpenSSL (X25519, Ed25519, SHA256, AES-CCM)
|
||||
- zxing-cpp (QR-коды, встроен как submodule)
|
||||
|
||||
**Структура сборки:**
|
||||
- `libutun/` — статическая библиотека uTun: все исходники из `src/` (кроме utun.c) + lib/ (uasync, ll_queue, memory_pool, etc) + транспорт (chat_sync, member_sync, merkle_sync)
|
||||
- `chatgui` — финальный бинарник: GUI-исходники + транспортные C++/C файлы + SQLite3 amalgamation
|
||||
- Платформенный TUN: `tun_linux.c` / `tun_freebsd.c` / `tun_windows.c`
|
||||
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake .. && make -j4
|
||||
./chatgui
|
||||
```sh
|
||||
lupdate src -no-obsolete -locations relative -ts translations/chatgui_en.ts translations/chatgui_ru.ts
|
||||
```
|
||||
|
||||
## 5. Файловая структура
|
||||
## Проверки
|
||||
|
||||
Состав целей и окружение тестов определяет CMakeLists.txt. Основные команды:
|
||||
|
||||
```sh
|
||||
cmake --build tools/chatgui/build --target vibechat test_desktop_commands test_utun_node -j4
|
||||
ctest --test-dir tools/chatgui/build -R 'test_(desktop_commands|utun_node|message_events|channel_replies|database_maintenance)' --output-on-failure
|
||||
```
|
||||
tools/chatgui/
|
||||
├── CMakeLists.txt # проект CMake
|
||||
├── build.sh / build.bat # скрипты сборки
|
||||
├── src/ # GUI-слой (Qt C++)
|
||||
│ ├── main.cpp # точка входа, QApplication
|
||||
│ ├── mainwindow.cpp # главное окно, трей
|
||||
│ ├── channellist.cpp # список каналов
|
||||
│ ├── chatview.cpp # QListView с фоном и drag-to-select
|
||||
│ ├── messagelist.cpp # модель сообщений, инициализация анимаций
|
||||
│ ├── messagedelegate.cpp # отрисовка бабблов, цитат, реакций, эмодзи
|
||||
│ ├── inputbar.cpp # поле ввода + кнопка эмодзи
|
||||
│ ├── emojipanel.cpp # QTabWidget с категориями эмодзи
|
||||
│ ├── emojitabbar.cpp # кастомный QTabBar
|
||||
│ ├── emoji.cpp # данные эмодзи, builtinEmojiSet()
|
||||
│ ├── lottieicon.cpp # загрузка TGS, рендер через rlottie
|
||||
│ ├── animtimer.cpp # синглтон-таймер 30fps с refcount
|
||||
│ ├── accountlist.cpp # список участников канала
|
||||
│ ├── memberlistmodel.cpp # модель для AccountList
|
||||
│ ├── invite_link.cpp # кодирование/декодирование utun:// ссылок
|
||||
│ ├── invitedialog.cpp # диалог приглашения
|
||||
│ ├── joindialog.cpp # диалог входа по invite-ссылке
|
||||
│ ├── settingsdialog.cpp # окно настроек
|
||||
│ ├── networksettingspage.cpp # страница сетевых настроек
|
||||
│ ├── databasesettingspage.cpp # настройки БД
|
||||
│ ├── statuspage.cpp # страница статуса
|
||||
│ ├── creategroupdialog.cpp # создание группы
|
||||
│ └── qrcode_utils.cpp # генерация QR-кодов
|
||||
├── transport/ # транспортный слой (C/C++)
|
||||
│ ├── utun_node.cpp # C++ обёртка UTUN_INSTANCE в std::thread
|
||||
│ ├── node_config.cpp # INI-конфиг узла (ключи, серверы, клиенты)
|
||||
│ ├── config_updater.cpp # утилита редактирования INI-файлов
|
||||
│ ├── gui_bridge_impl.cpp # мост uasync↔Qt (10 типов событий)
|
||||
│ ├── chat_core.c # центральный API чата (отправка, БД, invite)
|
||||
│ ├── chat_sync.c # P2P синхронизация каналов (ETCP 0x30)
|
||||
│ ├── member_sync.c # синхронизация мемберов через Merkle
|
||||
│ └── merkle_sync.c # универсальный Merkle-протокол синхронизации
|
||||
├── db/ # база данных
|
||||
│ ├── db_manager.h/cpp # C++/Qt обёртка SQLite3 (read-only для GUI)
|
||||
│ └── sqlite3.c/h # SQLite3 amalgamation (из ../../lib/)
|
||||
├── libutun/ # сборка статической библиотеки uTun
|
||||
│ └── CMakeLists.txt # uasync + utun = все .c из lib/ и src/
|
||||
├── resources/ # ресурсы
|
||||
│ ├── chatgui.qrc # Qt resource file (TGS вшиты в бинарник)
|
||||
│ └── animations/ # TGS-анимации (5 шт.)
|
||||
└── doc/ # документация
|
||||
├── desc.txt # описание схемы БД
|
||||
└── db_schema.md # детальная схема таблиц SQLite
|
||||
```
|
||||
|
||||
Linux-тесты команд используют allocation hooks и null audio backend с обычной Multimedia-сборкой.
|
||||
Это позволяет проверить очередь, native trampolines, SQLite и QWidget без физических audio devices.
|
||||
Категории диагностики определяются `lib/debug_config.h`; используйте уровни config/chat/dm/media/radio/sys
|
||||
для ошибок, при проверке состояний повышайте нужную категорию до debug.
|
||||
Reference in new issue
Block a user