diff --git a/src/chat/chat_headless_control.c b/src/chat/chat_headless_control.c
index 088f9995..bd6e86f9 100644
--- a/src/chat/chat_headless_control.c
+++ b/src/chat/chat_headless_control.c
@@ -208,10 +208,15 @@ static void hc_on_chat_event(struct UTUN_INSTANCE* inst, int type, const uint8_t
for (unsigned i = 0; i < 32; i++) snprintf(message_id + i * 2, 3, "%02x", data[1 + cl + i]);
snprintf(json, sizeof(json), "{\"event\":\"feedback\",\"ch\":\"%.*s\",\"message_id\":\"%s\",\"kind\":%u}",
(int)cl, data + 1, message_id, data[33 + cl]);
- } else if (type == 1 && data && len >= 10) { /* MSG_RECEIVED */
+ } else if (type == CHAT_EVT_MSG_RECEIVED) {
+ if (!data || len < 11 || !data[0] || len != data[0] + 10 || data[9 + data[0]] > 1) {
+ DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "headless: invalid MSG_RECEIVED length=%d", len);
+ return;
+ }
uint8_t cl = data[0];
uint64_t author; memcpy(&author, data + 1 + cl, 8);
- snprintf(json, sizeof(json), "{\"event\":\"msg\",\"ch\":\"%.*s\",\"author_id\":\"0x%016llx\"}", (int)cl, data + 1, (unsigned long long)author);
+ snprintf(json, sizeof(json), "{\"event\":\"msg\",\"ch\":\"%.*s\",\"author_id\":\"0x%016llx\",\"initial_sync\":%u}",
+ (int)cl, data + 1, (unsigned long long)author, data[9 + cl]);
} else if (type == 5 && data && len >= 2) { /* MEMBERS_CHANGED */
uint8_t cl = data[0];
snprintf(json, sizeof(json), "{\"event\":\"members_changed\",\"ch\":\"%.*s\"}", (int)cl, data + 1);
diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt
index 3a52229b..649593e9 100644
--- a/tools/chatgui/CMakeLists.txt
+++ b/tools/chatgui/CMakeLists.txt
@@ -120,6 +120,14 @@ endif()
# uTun core library (C, built from ../src/ and ../lib/)
add_subdirectory(libutun)
+add_library(chatgui_notifications STATIC src/message_notification.cpp src/user_activity_monitor.cpp)
+target_link_libraries(chatgui_notifications PUBLIC ${QT_PACKAGE}::Widgets PRIVATE utun)
+if(WIN32)
+ target_link_libraries(chatgui_notifications PRIVATE user32)
+elseif(APPLE)
+ target_link_libraries(chatgui_notifications PRIVATE "-framework ApplicationServices")
+endif()
+
# Голосовой стек звонка (SoundTouch + voice_jitter + call_audio + call_tones),
# C++ внутри, C-интерфейс наружу (call_audio.h).
set(LIB_DIR "${CMAKE_SOURCE_DIR}/../../lib")
@@ -239,8 +247,33 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_compile_definitions(vibechat PRIVATE UTUN_PTT_PORTAL)
target_link_libraries(vibechat PRIVATE ${QT_DBUS})
target_include_directories(vibechat PRIVATE ${X11_INCLUDE_DIR})
+ target_compile_definitions(chatgui_notifications PRIVATE UTUN_ACTIVITY_DBUS)
+ target_link_libraries(chatgui_notifications PRIVATE ${QT_DBUS})
+ if(Qt6_FOUND)
+ find_package(KF6IdleTime QUIET)
+ set(ACTIVITY_KIDLETIME_TARGET KF6::IdleTime)
+ else()
+ find_package(KF5IdleTime QUIET)
+ set(ACTIVITY_KIDLETIME_TARGET KF5::IdleTime)
+ endif()
+ if(TARGET ${ACTIVITY_KIDLETIME_TARGET})
+ target_compile_definitions(chatgui_notifications PRIVATE UTUN_ACTIVITY_KIDLETIME)
+ target_link_libraries(chatgui_notifications PRIVATE ${ACTIVITY_KIDLETIME_TARGET})
+ else()
+ message(STATUS "KIdleTime not found: Wayland activity uses GNOME IdleMonitor or ChatGUI input")
+ endif()
+elseif(UNIX AND NOT APPLE)
+ find_package(X11 QUIET)
endif()
+if(TARGET X11::Xss)
+ target_compile_definitions(chatgui_notifications PRIVATE UTUN_ACTIVITY_X11)
+ target_link_libraries(chatgui_notifications PRIVATE X11::Xss)
+elseif(UNIX AND NOT APPLE)
+ message(STATUS "X11 screen saver library not found: X11 activity uses ChatGUI input")
+endif()
+target_link_libraries(vibechat PRIVATE chatgui_notifications)
+
if(WIN32)
target_link_libraries(vibechat PRIVATE ${QT_LIBS} rlottie::rlottie ZLIB::ZLIB OpenSSL::Crypto ZXing::ZXing utun utun_voice pthread user32 ${FFMPEG_TARGET} ${FFMPEG_LIBRARIES})
else()
@@ -273,6 +306,11 @@ endif()
enable_testing()
+add_executable(test_message_notification tests/test_message_notification.cpp)
+target_link_libraries(test_message_notification PRIVATE chatgui_notifications ${QT_TEST_LIB} pthread ${CMAKE_DL_LIBS})
+add_test(NAME test_message_notification COMMAND test_message_notification)
+set_tests_properties(test_message_notification PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 60)
+
add_executable(test_file_drop tests/test_file_drop.cpp src/inputbar.cpp src/audiorecorder.cpp src/audio_device.cpp src/sound_manager.cpp
src/voicemessageencoder.cpp src/emojipanel.cpp src/emojitabbar.cpp src/emoji.cpp src/animtimer.cpp src/lottieicon.cpp
src/voice_audio_io.cpp transport/miniaudio_impl.c transport/audio_diagnostics.cpp)
diff --git a/tools/chatgui/translations/chatgui_ru.ts b/tools/chatgui/translations/chatgui_ru.ts
index c95331ae..e6f86dba 100644
--- a/tools/chatgui/translations/chatgui_ru.ts
+++ b/tools/chatgui/translations/chatgui_ru.ts
@@ -2152,5 +2152,6 @@ Directories: %5
Mute this member in radioНе слушать этого участника в рации
Listen to this member in radioСлушать этого участника в рации
Cannot change radio listening settingНе удалось изменить настройку прослушивания рации
+ New message from %1Новое сообщение от %1