Show desktop radio calls and activate channel radio after acceptance

This commit is contained in:
evgeny committed 2026-10-19 13:58:06 +02:00
1 parent 427794fed8
commit 3f97dafbf7
2 files changed
+115

No files matched your search

+109
View File
@@ -51,6 +51,7 @@ extern "C" {
#include <QClipboard>
#include <QStyle>
#include <QMessageBox>
#include <QPushButton>
#include <QDesktopServices>
#include <QUrl>
#include <QDebug>
@@ -69,6 +70,7 @@ extern "C" {
extern "C" {
#include "chat/chat_core.h"
#include "call/call.h"
#include "radio/radio_call.h"
#include "dm/dm_core.h"
#include "../../lib/mem.h"
#include "../../lib/debug_config.h"
@@ -203,6 +205,8 @@ MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath,
}
MainWindow::~MainWindow() {
gui_bridge_set_radio_call_cb(nullptr);
if (m_radioCallWindow) { m_radioCallId = 0; delete m_radioCallWindow.data(); }
if (m_streamPublishWindow) delete m_streamPublishWindow.data();
delete m_streamPublisher; m_streamPublisher = nullptr;
if (m_streamPlayer) delete m_streamPlayer.data();
@@ -644,6 +648,10 @@ void MainWindow::setupBridgeCallbacks() {
}
void MainWindow::setupConnects() {
connect(m_accountList, &AccountList::radioCallRequested, this, &MainWindow::onRadioCallRequested);
gui_bridge_set_radio_call_cb([](const uint8_t* data, int len) {
if (s_mainWindow) s_mainWindow->onRadioCallEvent(data, len);
});
connect(m_messageList, &MessageList::streamPublishRequested, this, [this](quint64 group) {
if (!m_streamPublisher) {
m_streamPublisher = new StreamPublishEngine(this);
@@ -740,6 +748,107 @@ void MainWindow::showMemberFiles(const QString& channel, quint64 owner) {
m_fileBrowser->setChannel(channel, owner); m_fileBrowser->show(); m_fileBrowser->raise(); m_fileBrowser->activateWindow();
}
/* Все команды рации идут в поток ядра; GUI не читает живые таблицы BGP. */
static void postRadioCall(quint64 id, int action, quint64 group = 0, quint64 peer = 0) {
auto* inst = gui_bridge_get_inst();
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "desktop radio call without running core"); return; }
auto* req = static_cast<radio_call_request*>(u_calloc(1, sizeof(radio_call_request)));
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "desktop radio call request allocation failed"); return; }
req->inst = inst; req->id = id; req->group = group; req->peer = peer; req->action = action;
gui_bridge_post_uasync_fn(radio_call_trampoline, req);
}
void MainWindow::onRadioCallRequested(const QString& channel, quint64 peer) {
if (m_radioCallWindow) { m_radioCallWindow->show(); m_radioCallWindow->raise(); return; }
quint64 id = QRandomGenerator::global()->generate64();
postRadioCall(id ? id : 1, RADIO_CALL_START, channel.toULongLong(), peer);
}
/* Один диалог на незавершённое приглашение; принятая рация живёт своим обычным циклом. */
void MainWindow::onRadioCallEvent(const uint8_t* data, int len) {
if (!data || len != 27 || data[24] < RADIO_CALL_OUTGOING || data[24] > RADIO_CALL_FINISHED ||
data[25] > RADIO_CALL_AUDIO_ERROR || data[26] > 1) {
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "desktop invalid radio call event bytes=%d", len); return;
}
quint64 id, group, peer;
memcpy(&id, data, 8); memcpy(&group, data + 8, 8); memcpy(&peer, data + 16, 8);
int state = data[24], reason = data[25]; bool incoming = data[26];
const QString channel = QString::number(group);
if (state == RADIO_CALL_FINISHED) {
if (m_radioCallId == id) {
m_radioCallId = 0;
SoundManager::instance()->stopLoop();
if (m_radioCallWindow) m_radioCallWindow->close();
m_radioCallWindow = nullptr;
}
if (reason == RADIO_CALL_ACCEPTED) {
if (!incoming) {
m_channelList->selectChannel(channel);
if (m_currentChannelId != channel || !m_messageList->enableRadio()) reason = RADIO_CALL_AUDIO_ERROR;
}
if (reason == RADIO_CALL_ACCEPTED) return;
}
if ((incoming && reason != RADIO_CALL_AUDIO_ERROR) || reason == RADIO_CALL_CANCELLED) return;
const char* text = reason == RADIO_CALL_DECLINED ? "Radio call declined" :
reason == RADIO_CALL_BUSY ? "Subscriber is busy" : reason == RADIO_CALL_NO_ANSWER ? "No answer" :
reason == RADIO_CALL_AUDIO_ERROR ? "Could not start channel radio" : "Subscriber is unavailable";
auto* notice = new QMessageBox(QMessageBox::Information, QCoreApplication::translate("ChatGui", "Radio call"),
QCoreApplication::translate("ChatGui", text), QMessageBox::Ok, this);
notice->setAttribute(Qt::WA_DeleteOnClose); notice->show();
if (m_trayIcon && (!isVisible() || isMinimized()))
m_trayIcon->showMessage(QCoreApplication::translate("ChatGui", "Radio call"), QCoreApplication::translate("ChatGui", text));
return;
}
if (state == RADIO_CALL_RINGING) {
if (m_radioCallId == id && m_radioCallWindow) {
m_radioCallWindow->setInformativeText(QCoreApplication::translate("ChatGui", "Ringing…"));
SoundManager::instance()->playLoop("call_ringing");
}
return;
}
if (m_radioCallId == id) return;
if (m_radioCallId || !m_activeCalls.isEmpty()) { postRadioCall(id, RADIO_CALL_REJECT_BUSY); return; }
QString name = m_db->getDisplayName(peer);
if (name.isEmpty()) name = QStringLiteral("0x%1").arg(peer, 16, 16, QChar('0'));
QString channelName = channel;
for (const auto& row : m_db->getChannels()) if (row.id == channel) { channelName = row.name; break; }
auto* box = new QMessageBox(QMessageBox::Question, QCoreApplication::translate("ChatGui", "Radio call"),
QCoreApplication::translate("ChatGui", incoming ? "Incoming radio call from %1\nChannel: %2" : "Calling %1 by radio\nChannel: %2")
.arg(name, channelName), QMessageBox::NoButton, this);
box->setAttribute(Qt::WA_DeleteOnClose);
m_radioCallId = id; m_radioCallWindow = box; m_radioCallAnswered = false;
if (incoming) {
auto* accept = box->addButton(QCoreApplication::translate("ChatGui", "Accept"), QMessageBox::AcceptRole);
box->addButton(QCoreApplication::translate("ChatGui", "Decline"), QMessageBox::RejectRole);
connect(box, &QMessageBox::buttonClicked, this, [this, id, channel, accept](QAbstractButton* button) {
if (m_radioCallId != id) return;
m_radioCallAnswered = true;
if (button != accept) { postRadioCall(id, RADIO_CALL_DECLINE); return; }
SoundManager::instance()->stopLoop();
m_channelList->selectChannel(channel);
bool ready = m_currentChannelId == channel && m_messageList->enableRadio();
postRadioCall(id, ready ? RADIO_CALL_ACCEPT : RADIO_CALL_FAIL);
});
} else {
box->addButton(QMessageBox::Cancel);
box->setInformativeText(QCoreApplication::translate("ChatGui", "Checking subscriber availability…"));
}
connect(box, &QDialog::finished, this, [this, id, incoming] {
if (m_radioCallId == id && !m_radioCallAnswered) {
m_radioCallAnswered = true;
SoundManager::instance()->stopLoop();
postRadioCall(id, incoming ? RADIO_CALL_DECLINE : RADIO_CALL_CANCEL);
}
});
box->show(); box->raise(); box->activateWindow();
SoundManager::instance()->playLoop(incoming ? "call_incoming" : "call_connecting");
if (incoming) {
if (m_trayIcon && (!isVisible() || isMinimized()))
m_trayIcon->showMessage(QCoreApplication::translate("ChatGui", "Radio call"), box->text());
postRadioCall(id, RADIO_CALL_READY);
}
}
void MainWindow::onCallRequested(const QString& chId, quint64 nodeId, const QString& name) {
if (chId.startsWith(QStringLiteral("dm:")) && !m_activeCalls.isEmpty()) {
auto* w = m_activeCalls.constBegin().value();
+6
View File
@@ -29,6 +29,7 @@ class CallWindow;
class CallAudioEngine;
class MemberFileBrowser;
class MessageNotification;
class QMessageBox;
struct AutoPlayItem {
QString channelId;
@@ -84,6 +85,8 @@ public:
void onCallError(quint64 callId, uint8_t err);
void onCallPath(quint64 callId, const QString& path);
void closeCallWindow(quint64 callId);
void onRadioCallRequested(const QString& channel, quint64 peer);
void onRadioCallEvent(const uint8_t* data, int len);
/* DM (p2p чат) */
void onPmRequested(const QString& chId, quint64 nodeId, const QString& name);
@@ -141,4 +144,7 @@ private:
QSet<int64_t> m_autoPlayDownloading; /* msgIds with download in progress */
QHash<quint64, CallWindow*> m_activeCalls; /* call_id → окно звонка */
QPointer<QMessageBox> m_radioCallWindow;
quint64 m_radioCallId = 0;
bool m_radioCallAnswered = false;
};