diff --git a/tools/chatgui/src/ptt_hotkey_manager.cpp b/tools/chatgui/src/ptt_hotkey_manager.cpp index c9794fd7..25063b7e 100644 --- a/tools/chatgui/src/ptt_hotkey_manager.cpp +++ b/tools/chatgui/src/ptt_hotkey_manager.cpp @@ -24,6 +24,7 @@ PttHotkeyManager* PttHotkeyManager::instance() { PttHotkeyManager::PttHotkeyManager() : QObject(qApp) { m_clock.start(); + connect(RadioAudioEngine::instance(), &RadioAudioEngine::pttLatchChanged, this, [this]() { m_activation.reset(); }); connect(RadioAudioEngine::instance(), &RadioAudioEngine::stateChanged, this, [this]() { auto* engine = RadioAudioEngine::instance(); if (!engine->isActive() || !engine->captureAvailable()) @@ -113,23 +114,23 @@ void PttHotkeyManager::updatePressed(bool pressed) { return; } if (m_activation.pressed == pressed) return; - bool latched = m_activation.latched; - m_activation.observe(pressed, m_clock.elapsed(), m_doublePressEnabled ? QApplication::doubleClickInterval() : 0); - DEBUG_DEBUG(DEBUG_CATEGORY_RADIO, "PTT: shortcut pressed=%d latched=%d talking=%d group=%016llx", - pressed, m_activation.latched, m_activation.talking, (unsigned long long)engine->groupId()); - if (latched != m_activation.latched) - DEBUG_INFO(DEBUG_CATEGORY_RADIO, "PTT: shortcut %s group=%016llx", m_activation.latched ? "locked" : "unlocked", - (unsigned long long)engine->groupId()); - engine->setHotkeyTalking(m_activation.talking); + bool doublePress = m_activation.observe(pressed, m_clock.elapsed(), m_doublePressEnabled ? QApplication::doubleClickInterval() : 0); + DEBUG_DEBUG(DEBUG_CATEGORY_RADIO, "PTT: shortcut pressed=%d double_press=%d latched=%d group=%016llx", + pressed, doublePress, engine->pttLatched(), (unsigned long long)engine->groupId()); + if (pressed && engine->pttLatched()) engine->setPttLatched(false, "shortcut-pressed"); + else if (doublePress) engine->setPttLatched(true, "shortcut-double-press"); + else engine->setHotkeyTalking(pressed); } /* Сброс не превращает ещё удерживаемую клавишу в новое нажатие после восстановления рации. */ void PttHotkeyManager::resetHotkey(const char* reason) { - if (m_activation.talking || m_activation.latched || m_activation.lastPress >= 0) - DEBUG_INFO(DEBUG_CATEGORY_RADIO, "PTT: shortcut reset reason=%s pressed=%d latched=%d talking=%d", - reason, m_activation.pressed, m_activation.latched, m_activation.talking); + if (m_activation.pressed || m_activation.lastPress >= 0) + DEBUG_INFO(DEBUG_CATEGORY_RADIO, "PTT: shortcut reset reason=%s pressed=%d double_press_pending=%d", + reason, m_activation.pressed, m_activation.lastPress >= 0); m_activation.reset(); - RadioAudioEngine::instance()->setHotkeyTalking(false); + auto* engine = RadioAudioEngine::instance(); + engine->setPttLatched(false, reason); + engine->setHotkeyTalking(false); } bool PttHotkeyManager::readKeys(PttKeyChord& held) const { diff --git a/tools/chatgui/src/ptt_hotkey_manager.h b/tools/chatgui/src/ptt_hotkey_manager.h index d6e6f9e3..a924092d 100644 --- a/tools/chatgui/src/ptt_hotkey_manager.h +++ b/tools/chatgui/src/ptt_hotkey_manager.h @@ -2,7 +2,7 @@ * Все методы/сигналы — GUI-поток, singleton принадлежит qApp; init вызывается после открытия БД. * DbManager заимствуется. Одно сочетание читается из ui_state radio_ptt_hotkey; сохранение выполняет вызывающий. * Удержание передаёт; radio_ptt_double_press (по умолчанию 1) разрешает фиксацию двойным нажатием до следующего нажатия. - * Wayland-комбинациями владеет портал. Менеджер меняет только hotkey-владельца PTT, не кнопку и не автоматическую передачу. */ + * Wayland-комбинациями владеет портал. Общим замком кнопки/хоткея владеет RadioAudioEngine; VAD от замка не зависит. */ #pragma once #include "ptt_key_chord.h" @@ -27,7 +27,7 @@ public: /* Применяет одно физическое сочетание в памяти, снимая удержание/фиксацию; невалидное значение отключает хоткей. * В режиме портала ничего не делает; БД не меняет. */ void setHotkey(const QString& chord); - /* Включает фиксацию двойным нажатием только для хоткея; изменение сбрасывает его текущее PTT, БД не меняет. */ + /* Включает двойное нажатие только для хоткея; изменение снимает общую фиксацию/удержание хоткея, БД не меняет. */ void setDoublePressEnabled(bool enabled); /* Загружает radio_ptt_hotkey и radio_ptt_double_press; без БД — пустое сочетание и разрешённая фиксация. */ void reload(); @@ -65,8 +65,8 @@ private: void pollKeys(); /* Обновляет hotkey-PTT/запись; ошибка чтения отключает PTT и останавливает таймер. */ bool readKeys(PttKeyChord& held) const; /* Добавляет нажатые коды в изначально пустой held; false при отказе backend. */ QString keyName(int key) const; /* Имя кода ОС либо локализованный запасной вариант Key N. */ - void updatePressed(bool pressed); // Общие фронты Windows/X11/Wayland; меняет только hotkey-владельца PTT. - void resetHotkey(const char* reason); // Снимает передачу/фиксацию и историю кликов, логируя причину сброса. + void updatePressed(bool pressed); // Фронты Windows/X11/Wayland: удержание, установка/снятие общего замка. + void resetHotkey(const char* reason); // Снимает общую фиксацию, удержание хоткея и историю кликов с причиной в логе. #ifdef UTUN_PTT_PORTAL PttPortal* m_portal = nullptr; /* Дочерняя portal-сессия, создаётся только для выбранного Wayland-режима. */ @@ -78,7 +78,7 @@ private: PttKeyChord m_held; // Последний снимок ОС, не конфигурация сочетания. bool m_capturing = false; // Идёт ручная запись вместо вычисления hotkey-PTT. bool m_captureArmed = false; // Исходные клавиши полностью отпущены, можно выдавать keysChanged. - PttHotkeyState m_activation; // Общая для всех backend логика удержания/двойного нажатия. + PttHotkeyState m_activation; // Физическое состояние и история двойного нажатия; собственной фиксации нет. QElapsedTimer m_clock; // Монотонные мс для двойного нажатия, запускаются в конструкторе. bool m_doublePressEnabled = true; // radio_ptt_double_press: кнопка PTT от этой настройки не зависит. }; diff --git a/tools/chatgui/src/ptt_hotkey_state.h b/tools/chatgui/src/ptt_hotkey_state.h index ea590b80..bb749557 100644 --- a/tools/chatgui/src/ptt_hotkey_state.h +++ b/tools/chatgui/src/ptt_hotkey_state.h @@ -1,26 +1,23 @@ -/* Состояние одного PTT-хоткея: удержание передаёт, двойное нажатие фиксирует, следующее снимает фиксацию. +/* Распознавание двойного нажатия одного PTT-хоткея; общим замком владеет RadioAudioEngine. * observe получает полное состояние сочетания и монотонное время в мс; повторы одного состояния игнорируются. - * reset снимает передачу и историю двойного нажатия, требуя отпускания уже удерживаемого сочетания. */ + * reset снимает историю двойного нажатия, сохраняя физическое состояние сочетания. */ #pragma once #include struct PttHotkeyState { bool pressed = false; // Последнее физическое состояние, включая нажатие, снявшее фиксацию. - bool latched = false; // Фиксация до нового нажатия либо reset. - bool talking = false; // Запрос передачи этого хоткея, независимый от кнопки и автоматики. qint64 lastPress = -1; // Начало первого нажатия в мс, -1 — двойное нажатие не ожидается. - /* Обновляет состояние по фронтам; interval — системный интервал двойного клика в мс. */ - void observe(bool held, qint64 now, int interval) { - if (pressed == held) return; + /* true только на втором нажатии пары; interval — системный интервал в мс, 0 отключает распознавание. */ + bool observe(bool held, qint64 now, int interval) { + if (pressed == held) return false; pressed = held; - if (!held) { talking = latched; return; } - if (latched) { reset(); return; } - latched = lastPress >= 0 && now - lastPress < interval; - talking = true; - lastPress = latched ? -1 : now; + if (!held) return false; + bool doublePress = lastPress >= 0 && now - lastPress < interval; + lastPress = doublePress ? -1 : now; + return doublePress; } /* Сохраняет pressed, чтобы удерживаемый при сбросе хоткей не включил передачу снова. */ - void reset() { latched = talking = false; lastPress = -1; } + void reset() { lastPress = -1; } };