Let the hotkey toggle the common PTT latch without a second latch state

This commit is contained in:
evgeny committed 2026-10-31 17:31:24 +01:00
1 parent 3d0f3213f8
commit d25d097287
3 files changed
+28 -30

No files matched your search

+13 -12
View File
@@ -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 {
+5 -5
View File
@@ -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 от этой настройки не зависит.
};
+10 -13
View File
@@ -1,26 +1,23 @@
/* Состояние одного PTT-хоткея: удержание передаёт, двойное нажатие фиксирует, следующее снимает фиксацию.
/* Распознавание двойного нажатия одного PTT-хоткея; общим замком владеет RadioAudioEngine.
* observe получает полное состояние сочетания и монотонное время в мс; повторы одного состояния игнорируются.
* reset снимает передачу и историю двойного нажатия, требуя отпускания уже удерживаемого сочетания. */
* reset снимает историю двойного нажатия, сохраняя физическое состояние сочетания. */
#pragma once
#include <QtGlobal>
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; }
};