Commit Graph
1171 Commits
Author SHA1 Message Date
Evgeny 2687c3ed82 fix: play button not responding + debug logging to log file
- chatview: QListView::mousePressEvent(event) instead of event->accept()
  (Qt needs base class press to set pressedIndex for editorEvent to fire)
- messagedelegate: GUI_DEBUG replaces qDebug (writes to log file)
- voiceplayback: DEBUG_DEBUG/DEBUG_WARN + debug_config.h include
- voicemessageencoder: same debug migration
- inputbar: GUI_INFO replaces qDebug
- messagelist: DEBUG_DEBUG/DEBUG_WARN replaces qDebug/qWarning
2026-07-27 00:30:09 +03:00
Evgeny 01012675d0 fix: voice playback coordinate bug + debug logging + device name encoding
- messagedelegate: editorEvent now adjusts me->pos() by option.rect.topLeft()
- voiceplayback: enhanced debug logging for file open/decode/play
- messagelist: debug voice file path + exists check
- inputbar: debug mediaDir path on record
- sound_manager: debug in playRawPcm, decodeDeviceName() for mixed encodings
- voiceencoder: debug file write path
2026-07-27 00:22:00 +03:00
Evgeny 324d285145 fix: voice message display and playback — complete flow
- db_manager: getMessagesSince() now parses m.contentType from JSON (was missing)
- messagelist: setVoiceMessageRoles uses data directly as payload, not re-parsed as JSON
  (data is already the 'd' field value extracted by db_manager)
2026-07-27 00:13:58 +03:00
Evgeny fd3b125ecb fix: voice message JSON double-nesting, '{' display
- req->data now stores 'filename|duration' instead of full nested JSON
- chat_core_submit_message builds the JSON with d='filename|duration'
- setVoiceMessageRoles splits d field by '|' to extract filename and duration
2026-07-26 22:16:09 +03:00
Evgeny ca9d1384f2 fix: PCM copy, device name encoding, double shutdown
- playRawPcm: ma_audio_buffer_init_copy instead of ma_audio_buffer_ref (copies PCM)
- enumerateDevices: fromLocal8Bit instead of fromUtf8 for device names
- AudioRecorder: m_deviceStarted flag prevents double ma_device_uninit
- stopMicTest: delete instead of deleteLater to avoid deferred double shutdown
2026-07-26 22:01:46 +03:00
Evgeny a4ae1c6b41 fix: RMS mic level, 25ms timer, speaker test, overlay recording indicator
- AudioRecorder: peak replaced with RMS (sqrt mean of squares)
- AudioDeviceSettings: level meter timer 100ms -> 25ms
- SoundManager: playRawPcm uses ma_audio_buffer_ref instead of ma_decoder (fixes speaker test)
- InputBar: recording overlay positioned over text input, no layout shift
- messagelist: removed old recording indicator widget
2026-07-26 21:27:57 +03:00
Evgeny 850a260aac fix: voice message display, playback, recording UI, audio settings
- messagelist: parse content_type from JSON when DB column is empty
- messagelist: recording indicator (red dot + timer + cancel button)
- messagedelegate: editorEvent for play/pause toggle + progress bar
- messagedelegate: VoicePlayState tracking with QTimer
- InputBar: proper recordingContext (mediaDir + channelId)
- DbManager: dbPath() for media directory resolution
- AudioRecorder: peakLevel() for mic level meter
- VoiceEncoder: codec quality presets (Low/Standard/High)
- audiodevicesettingspage: Default device option first
- audiodevicesettingspage: mic level meter + test microphone
- audiodevicesettingspage: test speaker button
- audiodevicesettingspage: codec preset combo (Low/Standard/High)
- mainwindow: load opus_codec_preset from ui_state on startup
2026-07-26 21:19:29 +03:00
Evgeny 4fde0b0325 feat: integrate Opus codec into lib/libopus + voice messages in chatgui
- Add lib/libopus/ (opus v1.6.1 embedded source: CELT 18 .c, SILK 77 .c, SILK float 28 .c, OPUS core 14 .c)
- Add lib/opus_codec.h/.c (wrapper: encoder, decoder, raw PCM int16)
- Add tests/test_opus_codec.c (7 tests: silence, sine, compression, PLC, null, defaults, multi-frame)
- chatgui: AudioRecorder (miniaudio capture, 48kHz mono)
- chatgui: VoiceEncoder (PCM→Opus file with custom container)
- chatgui: VoicePlayer (Opus→PCM playback via SoundManager)
- chatgui: PTT button in InputBar, voice message send/receive in MessageList
- chatgui: AudioDeviceSettingsPage (device selection, persisted in ui_state)
- chatgui: MessageDelegate voice message rendering (play button + duration)
- Update autotools: configure.ac, Makefile.am, src/Makefile.am, tests/Makefile.am
- Update CMake: chatgui CMakeLists.txt, libutun CMakeLists.txt (link libopus_internal.a)
2026-07-26 20:34:38 +03:00
Evgeny 4a23593020 member_sync: add merkle_sync_broadcast relay to other SYNCED sessions 2026-07-26 18:44:45 +03:00
Evgeny ef1e064bdc merkle_sync: broadcast relay for star topology + all 6 test stages pass
- Replace active/synced with sess_state (SESS_SYNCING/SESS_SYNCED)
- Add merkle_sync_broadcast() API for relaying changes to SYNCED peers
- apply_items callback now receives from_peer parameter
- _handle_hashes: reset pending_count on session reuse (overwrite fix)
- _handle_batch: send broadcast to synced sessions on new items
- Test Stage 5: sequential B->A + C->A with relay (no 3rd sync needed)
- Test Stage 6: cancel + start_overwrite pass
- Fix _wait_for: remove i_phase global timeout dependency
2026-07-26 18:20:10 +03:00
Evgeny a1ecab997e merkle_sync: fix 4 protocol bugs + add test suite (Stages 1-4)
- g_merkle singleton → per-instance msync (utun_instance.h)
- level_prefix: off-by-one shift (63→64)
- _handle_hashes: differs |= for shared buckets with different hashes
- _handle_batch: sub_pl double-level-offset (next_lvl→lvl)
- pending[] array replaces pending_requests counter
- member_sync: mask_shift 63→64 alignment
- test_merkle_sync.c: prefix arithmetic, tree building, 2-instance integration, randomized 50-iter sync
2026-07-26 17:42:27 +03:00
Evgeny 0e11d858e8 chatgui: fix all animations showing the same TGS (rlottie model cache key collision)
lottie_animation_from_data uses 'key' as a cache key for parsed models.
All LottieIcon instances were created with key="", causing rlottie's
model cache to return the first loaded animation for all subsequent
requests. Only visible on Windows where rlottie cache is enabled by
default.

Fix: pass unique animPath as key to each LottieIcon constructor.
Also replaced fprintf(stderr) diag with GUI_INFO logging via
AnimTimer::logRegisteredIcons() in debug_ui.h.
2026-07-26 15:18:59 +03:00
Evgeny 49da899964 chatgui: channel sorting by name or last message time with preview
- Add last_msg_at column to channels table
- Update last_msg_at on every message insert (send/receive)
- Parameterize getChannels() with ChannelSortMode enum (Alphabetical/Recent)
- Add getChannelLastMessage() for preview text in channel list
- Add sorting combo to Profile settings page (stored in ui_state)
- Reload channels on message receive when sort is recent
2026-07-26 11:08:59 +03:00
Evgeny 332df381d1 chatgui: propagate local nick changes to message authors via MsgAuthorNodeIdRole + reloadLocalNicks 2026-07-26 10:11:31 +03:00
Evgeny 22db84816e chatgui: add local_nick TEXT to peers table DDL, remove ALTER TABLE hacks 2026-07-26 09:45:00 +03:00
Evgeny 86843693c9 chatgui: ensure local_nick column exists before SELECT in getChannelMembers 2026-07-26 09:43:39 +03:00
Evgeny df3fc04523 chatgui: RenameDialog — quick rename via context menu, separate mini dialog 2026-07-26 09:33:31 +03:00
Evgeny 85bdc00aaf chatgui: local nicknames per channel member — edit via MemberPropsDialog, override display everywhere 2026-07-26 09:19:31 +03:00
Evgeny 373a2e4687 chatgui: member detail panel redesign — compact format with relative time, colored online dot, 5s auto-refresh 2026-07-26 09:09:21 +03:00
Evgeny 11ab897fe5 chatgui: fix RTT display 10x overstatement (DB stores 0.1ms, displayed as ms); remove unused rtt_avg_100 2026-07-26 08:57:02 +03:00
Evgeny 12904a97e0 chatgui: AccountDelegate — palette-consistent colors (light/alternateBase instead of hardcoded) 2026-07-26 07:43:01 +03:00
Evgeny 77d89b841a chatgui: double-click member edit, fix delegate backgrounds; transport: invite_sync/member_sync robustness 2026-07-26 07:18:27 +03:00
Evgeny cd3c3ad07d merkle_sync: fix level 0 SQL range, rebuild trees at startup
- _get_level_hashes: use separate SQL without prefix64<=? for level 0.
  UINT64_MAX cast to sqlite3_int64 becomes -1, making prefix64<=-1 always
  false for positive prefixes → bitmap always 0 → sync never exchanges data.
- member_sync_init: add _rebuild_all_trees to scan all peers_* tables and
  call merkle_sync_recompute_path for each member. Ensures merkle_tree_hash
  has entries for members added before merkle sync existed.
2026-07-25 23:31:36 +03:00
Evgeny 6bf8482558 merkle_sync: fix BATCH parse, synthetic HASHES, initiator done_cb
- Add 2-byte dlen prefix to terminal BATCH/HASHES data so receiver knows
  exact byte count — fixes rem=0 bug that truncated multi-item BATCHes.
- Fix synthetic HASHES prefix_bytes: use merkle_sync_prefix_bytes(next_lvl)
  for both declaration and _prefix_write, not current level's pb_i.
- Fix initiator never getting _session_done: responder sends confirmation
  HASHES(is_data=1,level=0) before _session_done so initiator's done_cb fires.
- Add level==0 guard in _member_get_items/_member_update_bucket_hash
  to prevent SQL queries with bogus mask on virtual root level.
2026-07-25 23:11:53 +03:00
Evgeny be10714972 merkle_sync: fix prefix reconstruction, remove timeouts/retries
- _get_level_hashes: remove (0x1F << parent_shift) from range_end so
  bitmap only includes children of the actual parent prefix (not all
  parents). Add level 0 virtual root that queries all level-1 entries.
- merkle_sync_start: send HASHES at level 0 (virtual root) instead of
  level 1/prefix 0. Root-level REQUEST prefix reconstruction now uses
  the correct level-1 prefix from DB.
- _handle_batch: use merkle_sync_prefix_bytes(next_lvl) for synthetic
  child HASHES instead of current level's pb_i (could be wrong size).
- Remove timeout/retry mechanism: delete _session_timeout_cb,
  _session_start_timer, timer/retries fields. ETCP provides reliable
  transport — no need for application-layer retries.
2026-07-25 23:00:42 +03:00
Evgeny 332e3a61e5 fix merkle_sync: prefix serialization writes LSB bytes instead of MSB
_prefix_write was writing the least significant pb bytes of the 64-bit
prefix, but the protocol expects the most significant pb bytes (bits
63..64-pb*8). For pb<=4 these ranges are disjoint → wire always got
zeros. _prefix_read also lacked the final left-shift to align the
result at bit 63.

This caused all level>=2 merkle sync requests/batches to receive
prefix=0 instead of the real prefix, making get_items/update_bucket_hash
find zero members → handle_batch len=7 with no data → sync completed
without actually exchanging member data.
2026-07-25 21:53:06 +03:00
Evgeny ccc5396167 merkle_sync: fix _handle_batch — call _session_done after terminal buckets 2026-07-25 21:32:00 +03:00
Evgeny d512bacc81 merkle_sync: fix message format — type after ns (match chat_sync) 2026-07-25 20:56:05 +03:00
Evgeny a4582202a8 merkle_sync: fix range_end formula — cover all parent+child bucket bits 2026-07-25 17:58:51 +03:00
Evgeny be5a760de9 merkle_sync: add debug logs to _get_level_hashes (SQL error + bitmap result) 2026-07-25 17:43:22 +03:00
Evgeny 5983acd988 merkle_sync: fix _get_level_hashes — range query instead of exact mismatch (prefix encoding bug) 2026-07-25 17:37:12 +03:00
Evgeny fe18c454e2 chatgui: add diagnostic logging for Windows emoji animation bug investigation 2026-07-24 22:11:03 +03:00
Evgeny e071a9e2f8 db_sync: fix test sync protocol isolation (add auto_sync flag, destroy order, test refactor)
- utun_instance: fix destroy order (topo_groups before conn_mgr to avoid UAF in topo_recovery)
- topo_recovery: add NULL guard for instance/ua in try_next
- db_sync_instance_add: add auto_sync flag (1=send INIT_SYNC, 0=quiet)
  replaces separate _quiet variant, cleaner API
- db_sync_peer_set_state, db_sync_reinitiate: test-only peer control utilities
- test_db_sync: full rewrite — stable, isolated sync protocol testing
  all 3 branches (peer_empty, hash_MATCH, divergence) tested without PUSH contamination
  Phase 6 extended: triple merge + tail PUSH + mid PUSH cascade on star topology
  runs in 1.4s, consistently passes 10/10
- db_sync_doc: updated protocol architecture description
- chatgui: update db_sync_instance_add call with auto_sync=1
2026-07-24 21:27:23 +03:00
Evgeny c46cb9a979 db_sync: fix si_send_data_batch column bug (author→node_id), add cascade_from to PUSH, add divergence+cascade test phases
- si_send_data_batch: неправильный SQL SELECT с несуществующей колонкой author
  вместо node_id → ломал всю передачу данных в REFINE/SEND_DATA
- Добавлена проверка si_prep и возврат int с кодом ошибки
- Все вызовы si_send_data_batch проверяют возврат, при ошибке сброс sync_state
- db_handle_push: добавлен db_cascade_from после вставки (раньше chain_hash
  не пересчитывался после PUSH)
- db_handle_refine: DEBUG_WARN при scnt==0 (fm за пределами диапазона)
- Добавлена публичная db_sync_chain_verify() для верификации цепочек
- test_db_sync: фаза 4 (divergence через разныe hashes + REFINE/SEND_DATA),
  фаза 5 (PUSH без хвоста + cascade verification)
- Убран мёртвый код db_sync_on_new_conn
- chatgui: адаптивная раскраска пузырей исходящих сообщений
2026-07-24 16:55:08 +03:00
Evgeny df8d910efa chatgui: fix Ctrl-C on Linux — remove thread_local from g_currentNode
SIGINT on Linux can be delivered to any thread (not just worker).
signal_handler uses g_currentNode->requestStop(); with thread_local,
g_currentNode was nullptr in non-worker threads — Ctrl-C did nothing.

Removing thread_local fixes signal delivery: g_currentNode is set once
before signal handler is registered, pointer read is atomic on x86/x64.
2026-07-24 16:02:49 +03:00
Evgeny e8920fe00e chatgui: MsgIsOutgoingRole support for outgoing message alignment 2026-07-24 15:55:34 +03:00
Evgeny fbc1bff1e1 chatgui: proper animation shutdown + TerminateProcess for rlottie threads
- AnimTimer::shutdown(): stop QTimer, clear m_icons before icon destruction
- ~MainWindow: AnimTimer::shutdown() -> cleanupAnimations() (correct order)
- main: replace ExitProcess with TerminateProcess(GetCurrentProcess,0)
  TerminateProcess skips DLL_PROCESS_DETACH, avoiding libwinpthread
  pthread_join hang on rlottie/miniaudio threads.
  rlottie C API has no global shutdown - internal RleTaskScheduler threads
  cannot be stopped through public API. Same for miniaudio WASAPI threads.
  All owned resources already freed before TerminateProcess.
2026-07-24 15:49:58 +03:00
Evgeny 73be79d204 chatgui: fix rlottie thread hang on Windows shutdown
Root cause: librlottie RleTaskScheduler creates 32 worker threads on first
lottie_animation_from_data call. These pthreads wait in WaitForMultipleObjects(INFINITE)
and never exit. On MinGW, exit()/pthread_join blocks forever.

Changes:
- messagelist: s_icons moved to file-static; cleanupAnimations() clears them,
  calling lottie_animation_destroy BEFORE static destruction
- mainwindow: ~MainWindow calls MessageList::cleanupAnimations() after node stop
- main: ShutdownSentinel counts threads at exit; if >4 remaining, calls
  ExitProcess(0) to bypass CRT pthread_join hang
- main: if rlottie properly shuts down (<=4 threads), normal return (no ExitProcess)
2026-07-24 14:54:12 +03:00
Evgeny 74c6b0d102 chatgui: add shutdown diagnostics — ~DbManager, ~SoundManager, g_shutdown_sentinel
Three diagnostic points covering the gap between ~MainWindow done and CRT exit:
- DbManager: destroyed — confirms local dtors started after ~MainWindow
- SoundManager: static destructor entered — confirms static destruction phase
- main: static destructors done (g_shutdown_sentinel dtor) — confirms ALL statics done

If process hangs, we know exactly which phase failed.
2026-07-24 14:34:00 +03:00
Evgeny 0d558245ce chatgui: move SoundManager shutdown to ~MainWindow before node cleanup
- SoundManager::shutdown() called explicitly in ~MainWindow (not static destruction)
- Before ma_engine_uninit: DEBUG_INFO diagnostic in timestamped log
- After ma_engine_uninit: DEBUG_INFO diagnostic
- Double-call safe: static destructor hits m_initialized=false, returns immediately
2026-07-24 14:22:28 +03:00
Evgeny fbaf58530f chatgui: rework shutdown — main thread owns cleanup, remove glШбаls
Changes:
- UtunNode::requestStop() — async stop from any thread (m_stop+wakeup)
- UtunNode::finalize() — cleanup in main thread (etcp_router, chat_sync, chat_core, utun_instance, uasync)
- UtunNode::stop() — requestStop + join + finalize (for tray Quit path)
- Worker runLoop: only exits poll loop on m_stop, then emits stopped — no cleanup
- stopped() handler: calls finalize() in main thread, then qApp->quit()
- Tray Quit: calls requestStop(), stopped handler does finalize+quit
- Ctrl-C (Windows): SetConsoleCtrlHandler calls requestStop() via g_ctrl_node
- Ctrl-C (Linux): signal_handler calls requestStop() via g_currentNode
- Removed: g_shutdown, g_signal_ua, utun_node_signal_shutdown globals
2026-07-24 14:14:27 +03:00
Evgeny 4c623e25ce chatgui: add shutdown diagnostics (poll exit reason, cleanup steps, app.exec return) 2026-07-24 13:56:29 +03:00
Evgeny faddbef32a chatgui: add SetConsoleCtrlHandler for reliable Ctrl-C on Windows 2026-07-24 13:51:23 +03:00
Evgeny ce9e58a925 chatgui: add scroll-to-bottom floating button, incremental message refresh via getMessagesSince 2026-07-24 04:07:51 +03:00
Evgeny b6d44bd947 chatgui: don't save scroll position when at bottom — restore to bottom on reopen
When scrollbar is at max, save lastPosMsgId=0 so restoreScrollPosition
falls through to scrollToBottom() instead of restoring a stale mid-scroll pos.
2026-07-24 03:52:05 +03:00
Evgeny 6cafff200b 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.
2026-07-24 03:47:15 +03:00
Evgeny 83ca0c65a4 chatgui: fix corrupted channel_id from non-null-terminated binary protocol
QString::fromUtf8(ch_id) read past length-prefixed string, capturing garbage.
Use fromUtf8(ch_id, ch_id_len) — length already available from bridge but was unused.
Messages now appear on refresh and unread badge correctly matches channel.
2026-07-24 03:33:14 +03:00
Evgeny fdc30d1a4e chatgui: synchronous DB write in saveChannelState/updateReadPosition, add shutdown debug logs
Replace gui_bridge_post_uasync_fn (async queue lost on shutdown) with direct
chat_core_save_ui_state (sync SQLite write). SQLite WAL handles concurrent access.
Add DEBUG_DEBUG traces in closeEvent, ~MainWindow, aboutToQuit.
2026-07-24 03:29:37 +03:00
Evgeny 393b7d36c8 chatgui: save scroll/read position on app quit via aboutToQuit
Make saveChannelState() public, connect to QApplication::aboutToQuit so
state is persisted on SIGINT, tray quit, or any other shutdown.
2026-07-24 03:24:54 +03:00
Evgeny 6ef21613c8 chatgui: pass author node_id in MSG_RECEIVED event, skip sound for own messages
Add author_node_id:8 to GUI_EVT_MSG_RECEIVED data format.
Callback now includes uint64_t author parameter.
SoundManager::play('msg_incoming') only when author != myNodeId.
2026-07-24 03:22:10 +03:00