From 1ba6a4d78d27181847bb1fd45838a70ae0c853e5 Mon Sep 17 00:00:00 2001 From: David Elsing Date: Sat, 11 Mar 2023 14:36:51 +0100 Subject: Support screen sharing with xdg-desktop-portal --- src/voip/ScreenCastPortal.cpp | 471 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 src/voip/ScreenCastPortal.cpp (limited to 'src/voip/ScreenCastPortal.cpp') diff --git a/src/voip/ScreenCastPortal.cpp b/src/voip/ScreenCastPortal.cpp new file mode 100644 index 00000000..321373d9 --- /dev/null +++ b/src/voip/ScreenCastPortal.cpp @@ -0,0 +1,471 @@ +#ifdef GSTREAMER_AVAILABLE + +#include "ScreenCastPortal.h" +#include "ChatPage.h" +#include "Logging.h" +#include "UserSettingsPage.h" + +#include +#include +#include +#include +#include +#include + +static QString +make_token() +{ + thread_local std::random_device rng; + std::uniform_int_distribution index_dist(0, 9); + + std::string token; + token.reserve(5 + 64); + token += "nheko"; + + for (uint8_t i = 0; i < 64; ++i) + token.push_back('0' + index_dist(rng)); + + return QString::fromStdString(std::move(token)); +} + +static QString +handle_path(QString handle_token) +{ + QString sender = QDBusConnection::sessionBus().baseService(); + if (sender[0] == ':') + sender.remove(0, 1); + sender.replace(".", "_"); + return QStringLiteral("/org/freedesktop/portal/desktop/request/") + sender + + QStringLiteral("/") + handle_token; +} + +void +ScreenCastPortal::init() +{ + switch (state) { + case State::Closed: + state = State::Starting; + createSession(); + break; + case State::Starting: + nhlog::ui()->warn("ScreenCastPortal already starting"); + break; + case State::Started: + close(true); + break; + case State::Closing: + nhlog::ui()->warn("ScreenCastPortal still closing"); + break; + } +} + +const ScreenCastPortal::Stream * +ScreenCastPortal::getStream() const +{ + if (state != State::Started) + return nullptr; + else + return &stream; +} + +bool +ScreenCastPortal::ready() const +{ + return state == State::Started; +} + +void +ScreenCastPortal::close(bool reinit) +{ + switch (state) { + case State::Closed: + if (reinit) + init(); + break; + case State::Starting: + if (!reinit) { + // Remaining handler will abort. + state = State::Closed; + } + break; + case State::Started: { + state = State::Closing; + emit readyChanged(); + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), + sessionHandle.path(), + QStringLiteral("org.freedesktop.portal.Session"), + QStringLiteral("Close")); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, + &QDBusPendingCallWatcher::finished, + this, + [this, reinit](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + if (!reply.isValid()) { + nhlog::ui()->warn("org.freedesktop.portal.ScreenCast (Close): {}", + reply.error().message().toStdString()); + } + state = State::Closed; + if (reinit) + init(); + }); + } break; + case State::Closing: + nhlog::ui()->warn("ScreenCastPortal already closing"); + break; + } +} + +void +ScreenCastPortal::closedHandler(uint response, const QVariantMap &) +{ + if (response != 0) { + nhlog::ui()->error("org.freedekstop.portal.ScreenCast (Closed): {}", response); + } + + nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: Connection closed"); + state = State::Closed; + emit readyChanged(); +} + +void +ScreenCastPortal::createSession() +{ + // Connect before sending the request to avoid missing the reply + QString handle_token = make_token(); + QDBusConnection::sessionBus().connect(QStringLiteral("org.freedesktop.portal.Desktop"), + handle_path(handle_token), + QStringLiteral("org.freedesktop.portal.Request"), + QStringLiteral("Response"), + this, + SLOT(createSessionHandler(uint, QVariantMap))); + + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), + QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.portal.ScreenCast"), + QStringLiteral("CreateSession")); + msg << QVariantMap{{QStringLiteral("handle_token"), handle_token}, + {QStringLiteral("session_handle_token"), make_token()}}; + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + connect( + watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + self->deleteLater(); + + if (!reply.isValid()) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (CreateSession): {}", + reply.error().message().toStdString()); + close(); + } + }); +} + +void +ScreenCastPortal::createSessionHandler(uint response, const QVariantMap &results) +{ + switch (state) { + case State::Closed: + nhlog::ui()->warn("ScreenCastPortal not starting"); + break; + case State::Starting: { + if (response != 0) { + nhlog::ui()->error("org.freedekstop.portal.ScreenCast (CreateSession Response): {}", + response); + close(); + return; + } + + sessionHandle = QDBusObjectPath(results.value(QStringLiteral("session_handle")).toString()); + + nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: sessionHandle = {}", + sessionHandle.path().toStdString()); + + getAvailableSourceTypes(); + } break; + case State::Started: + nhlog::ui()->warn("ScreenCastPortal already started"); + break; + case State::Closing: + break; + } +} + +void +ScreenCastPortal::getAvailableSourceTypes() +{ + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), + QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("Get")); + msg << QStringLiteral("org.freedesktop.portal.ScreenCast") + << QStringLiteral("AvailableSourceTypes"); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + connect( + watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + self->deleteLater(); + + if (!reply.isValid()) { + nhlog::ui()->error("org.freedesktop.DBus.Properties (Get AvailableSourceTypes): {}", + reply.error().message().toStdString()); + close(); + return; + } + + switch (state) { + case State::Closed: + nhlog::ui()->warn("ScreenCastPortal not starting"); + break; + case State::Starting: { + const auto &value = reply.value().variant(); + if (value.canConvert()) { + availableSourceTypes = value.value(); + } else { + nhlog::ui()->error("Invalid reply from org.freedesktop.DBus.Properties (Get " + "AvailableSourceTypes)"); + close(); + return; + } + + getAvailableCursorModes(); + } break; + case State::Started: + nhlog::ui()->warn("ScreenCastPortal already started"); + break; + case State::Closing: + break; + } + }); +} + +void +ScreenCastPortal::getAvailableCursorModes() +{ + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), + QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("Get")); + msg << QStringLiteral("org.freedesktop.portal.ScreenCast") + << QStringLiteral("AvailableCursorModes"); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + connect( + watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + self->deleteLater(); + + if (!reply.isValid()) { + nhlog::ui()->error("org.freedesktop.DBus.Properties (Get AvailableCursorModes): {}", + reply.error().message().toStdString()); + close(); + return; + } + + switch (state) { + case State::Closed: + nhlog::ui()->warn("ScreenCastPortal not starting"); + break; + case State::Starting: { + const auto &value = reply.value().variant(); + if (value.canConvert()) { + availableCursorModes = value.value(); + } else { + nhlog::ui()->error("Invalid reply from org.freedesktop.DBus.Properties (Get " + "AvailableCursorModes)"); + close(); + return; + } + + selectSources(); + } break; + case State::Started: + nhlog::ui()->warn("ScreenCastPortal already started"); + break; + case State::Closing: + break; + } + }); +} + +void +ScreenCastPortal::selectSources() +{ + // Connect before sending the request to avoid missing the reply + auto handle_token = make_token(); + QDBusConnection::sessionBus().connect(QString(), + handle_path(handle_token), + QStringLiteral("org.freedesktop.portal.Request"), + QStringLiteral("Response"), + this, + SLOT(selectSourcesHandler(uint, QVariantMap))); + + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), + QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.portal.ScreenCast"), + QStringLiteral("SelectSources")); + + QVariantMap options{{QStringLiteral("multiple"), false}, + {QStringLiteral("types"), availableSourceTypes}, + {QStringLiteral("handle_token"), handle_token}}; + + auto settings = ChatPage::instance()->userSettings(); + if (settings->screenShareHideCursor() && (availableCursorModes & (uint)1) != 0) { + options["cursor_mode"] = (uint)1; + } + + msg << QVariant::fromValue(sessionHandle) << options; + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect( + watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + if (!reply.isValid()) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (SelectSources): {}", + reply.error().message().toStdString()); + close(); + } + }); +} + +void +ScreenCastPortal::selectSourcesHandler(uint response, const QVariantMap &) +{ + switch (state) { + case State::Closed: + nhlog::ui()->warn("ScreenCastPortal not starting"); + break; + case State::Starting: { + if (response != 0) { + nhlog::ui()->error("org.freedekstop.portal.ScreenCast (SelectSources Response): {}", + response); + close(); + return; + } + start(); + } break; + case State::Started: + nhlog::ui()->warn("ScreenCastPortal already started"); + break; + case State::Closing: + break; + } +} + +void +ScreenCastPortal::start() +{ + // Connect before sending the request to avoid missing the reply + auto handle_token = make_token(); + QDBusConnection::sessionBus().connect(QString(), + handle_path(handle_token), + QStringLiteral("org.freedesktop.portal.Request"), + QStringLiteral("Response"), + this, + SLOT(startHandler(uint, QVariantMap))); + + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), + QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.portal.ScreenCast"), + QStringLiteral("Start")); + msg << QVariant::fromValue(sessionHandle) << QString() + << QVariantMap{{QStringLiteral("handle_token"), handle_token}}; + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect( + watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + if (!reply.isValid()) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (Start): {}", + reply.error().message().toStdString()); + } else { + } + }); +} + +struct PipeWireStream +{ + quint32 nodeId = 0; + QVariantMap map; +}; + +Q_DECLARE_METATYPE(PipeWireStream) + +const QDBusArgument & +operator>>(const QDBusArgument &argument, PipeWireStream &stream) +{ + argument.beginStructure(); + argument >> stream.nodeId; + argument.beginMap(); + while (!argument.atEnd()) { + QString key; + QVariant map; + argument.beginMapEntry(); + argument >> key >> map; + argument.endMapEntry(); + stream.map.insert(key, map); + } + argument.endMap(); + argument.endStructure(); + return argument; +} + +void +ScreenCastPortal::startHandler(uint response, const QVariantMap &results) +{ + if (response != 0) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (Start Response): {}", response); + close(); + return; + } + + QVector streams = + qdbus_cast>(results.value(QStringLiteral("streams"))); + if (streams.size() == 0) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast: No stream was returned"); + close(); + return; + } + + stream.nodeId = streams[0].nodeId; + nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: nodeId = {}", stream.nodeId); + openPipeWireRemote(); +} + +void +ScreenCastPortal::openPipeWireRemote() +{ + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), + QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.portal.ScreenCast"), + QStringLiteral("OpenPipeWireRemote")); + msg << QVariant::fromValue(sessionHandle) << QVariantMap{}; + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect( + watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + if (!reply.isValid()) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (OpenPipeWireRemote): {}", + reply.error().message().toStdString()); + close(); + } else { + stream.fd = reply.value().fileDescriptor(); + nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: fd = {}", stream.fd); + + state = State::Started; + emit readyChanged(); + } + }); +} + +#endif -- cgit 1.5.1 From 562a71a5f3e4e29067103c250c3f2e83d4937d82 Mon Sep 17 00:00:00 2001 From: David Elsing Date: Sun, 12 Mar 2023 11:47:05 +0100 Subject: Add missing license headers --- src/voip/ScreenCastPortal.cpp | 4 ++++ src/voip/ScreenCastPortal.h | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'src/voip/ScreenCastPortal.cpp') diff --git a/src/voip/ScreenCastPortal.cpp b/src/voip/ScreenCastPortal.cpp index 321373d9..41e6c0a2 100644 --- a/src/voip/ScreenCastPortal.cpp +++ b/src/voip/ScreenCastPortal.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #ifdef GSTREAMER_AVAILABLE #include "ScreenCastPortal.h" diff --git a/src/voip/ScreenCastPortal.h b/src/voip/ScreenCastPortal.h index 068dbddb..853ece04 100644 --- a/src/voip/ScreenCastPortal.h +++ b/src/voip/ScreenCastPortal.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #ifdef GSTREAMER_AVAILABLE -- cgit 1.5.1 From 7f98cd01330057ade7cb7f5703b8ca896ecc36ab Mon Sep 17 00:00:00 2001 From: David Elsing Date: Mon, 27 Mar 2023 16:42:29 +0200 Subject: Use mtx::client::utils::random_token for XDP session tokens --- src/voip/ScreenCastPortal.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'src/voip/ScreenCastPortal.cpp') diff --git a/src/voip/ScreenCastPortal.cpp b/src/voip/ScreenCastPortal.cpp index 41e6c0a2..cce5a375 100644 --- a/src/voip/ScreenCastPortal.cpp +++ b/src/voip/ScreenCastPortal.cpp @@ -14,22 +14,13 @@ #include #include #include +#include #include static QString make_token() { - thread_local std::random_device rng; - std::uniform_int_distribution index_dist(0, 9); - - std::string token; - token.reserve(5 + 64); - token += "nheko"; - - for (uint8_t i = 0; i < 64; ++i) - token.push_back('0' + index_dist(rng)); - - return QString::fromStdString(std::move(token)); + return QString::fromStdString("nheko" + mtx::client::utils::random_token(64, false)); } static QString -- cgit 1.5.1 From c3081ece401bec8b14ba962a5120fe92f4166577 Mon Sep 17 00:00:00 2001 From: David Elsing Date: Wed, 29 Mar 2023 23:01:53 +0200 Subject: Cleanup Qt D-Bus connections and watchers --- src/voip/CallManager.cpp | 2 +- src/voip/ScreenCastPortal.cpp | 270 +++++++++++++++++++++++++----------------- src/voip/ScreenCastPortal.h | 10 +- src/voip/WebRTCSession.cpp | 2 +- 4 files changed, 171 insertions(+), 113 deletions(-) (limited to 'src/voip/ScreenCastPortal.cpp') diff --git a/src/voip/CallManager.cpp b/src/voip/CallManager.cpp index e7bd45e4..d9866c2c 100644 --- a/src/voip/CallManager.cpp +++ b/src/voip/CallManager.cpp @@ -1061,7 +1061,7 @@ CallManager::previewWindow(unsigned int index) const return; } GstElement *pipewiresrc = gst_element_factory_make("pipewiresrc", nullptr); - g_object_set(pipewiresrc, "fd", (gint)stream->fd, nullptr); + g_object_set(pipewiresrc, "fd", (gint)stream->fd.fileDescriptor(), nullptr); std::string path = std::to_string(stream->nodeId); g_object_set(pipewiresrc, "path", path.c_str(), nullptr); g_object_set(pipewiresrc, "do-timestamp", (gboolean)1, nullptr); diff --git a/src/voip/ScreenCastPortal.cpp b/src/voip/ScreenCastPortal.cpp index cce5a375..31cddba0 100644 --- a/src/voip/ScreenCastPortal.cpp +++ b/src/voip/ScreenCastPortal.cpp @@ -34,6 +34,48 @@ handle_path(QString handle_token) QStringLiteral("/") + handle_token; } +bool +ScreenCastPortal::makeConnection(QString service, + QString path, + QString interface, + QString name, + const char *slot) +{ + if (QDBusConnection::sessionBus().connect(service, path, interface, name, this, slot)) { + last_connection = { + std::move(service), std::move(path), std::move(interface), std::move(name), slot}; + return true; + } + return false; +} + +void +ScreenCastPortal::disconnectClose() +{ + QDBusConnection::sessionBus().disconnect(QStringLiteral("org.freedesktop.portal.Desktop"), + sessionHandle.path(), + QStringLiteral("org.freedesktop.portal.Session"), + QStringLiteral("Closed"), + this, + SLOT(closedHandler(QVariantMap))); +} + +void +ScreenCastPortal::removeConnection() +{ + if (!last_connection.has_value()) + return; + + const auto &connection = *last_connection; + QDBusConnection::sessionBus().disconnect(connection[0], + connection[1], + connection[2], + connection[3], + this, + connection[4].toLocal8Bit().data()); + last_connection = std::nullopt; +} + void ScreenCastPortal::init() { @@ -79,13 +121,19 @@ ScreenCastPortal::close(bool reinit) break; case State::Starting: if (!reinit) { - // Remaining handler will abort. + disconnectClose(); + removeConnection(); state = State::Closed; } break; case State::Started: { state = State::Closing; + disconnectClose(); + // Close file descriptor if it was opened + stream = Stream{}; + emit readyChanged(); + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), sessionHandle.path(), QStringLiteral("org.freedesktop.portal.Session"), @@ -97,7 +145,9 @@ ScreenCastPortal::close(bool reinit) &QDBusPendingCallWatcher::finished, this, [this, reinit](QDBusPendingCallWatcher *self) { + self->deleteLater(); QDBusPendingReply reply = *self; + if (!reply.isValid()) { nhlog::ui()->warn("org.freedesktop.portal.ScreenCast (Close): {}", reply.error().message().toStdString()); @@ -116,8 +166,11 @@ ScreenCastPortal::close(bool reinit) void ScreenCastPortal::closedHandler(uint response, const QVariantMap &) { + removeConnection(); + disconnectClose(); + if (response != 0) { - nhlog::ui()->error("org.freedekstop.portal.ScreenCast (Closed): {}", response); + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (Closed): {}", response); } nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: Connection closed"); @@ -130,12 +183,16 @@ ScreenCastPortal::createSession() { // Connect before sending the request to avoid missing the reply QString handle_token = make_token(); - QDBusConnection::sessionBus().connect(QStringLiteral("org.freedesktop.portal.Desktop"), - handle_path(handle_token), - QStringLiteral("org.freedesktop.portal.Request"), - QStringLiteral("Response"), - this, - SLOT(createSessionHandler(uint, QVariantMap))); + if (!makeConnection(QStringLiteral("org.freedesktop.portal.Desktop"), + handle_path(handle_token), + QStringLiteral("org.freedesktop.portal.Request"), + QStringLiteral("Response"), + SLOT(createSessionHandler(uint, QVariantMap)))) { + nhlog::ui()->error( + "Connection to signal Response for org.freedesktop.portal.Request failed"); + close(); + return; + } auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), @@ -145,11 +202,11 @@ ScreenCastPortal::createSession() {QStringLiteral("session_handle_token"), make_token()}}; QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { - QDBusPendingReply reply = *self; self->deleteLater(); + QDBusPendingReply reply = *self; if (!reply.isValid()) { nhlog::ui()->error("org.freedesktop.portal.ScreenCast (CreateSession): {}", @@ -162,31 +219,32 @@ ScreenCastPortal::createSession() void ScreenCastPortal::createSessionHandler(uint response, const QVariantMap &results) { - switch (state) { - case State::Closed: + removeConnection(); + + if (state != State::Starting) { nhlog::ui()->warn("ScreenCastPortal not starting"); - break; - case State::Starting: { - if (response != 0) { - nhlog::ui()->error("org.freedekstop.portal.ScreenCast (CreateSession Response): {}", - response); - close(); - return; - } + return; + } + if (response != 0) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (CreateSession Response): {}", + response); + close(); + return; + } - sessionHandle = QDBusObjectPath(results.value(QStringLiteral("session_handle")).toString()); + sessionHandle = QDBusObjectPath(results.value(QStringLiteral("session_handle")).toString()); - nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: sessionHandle = {}", - sessionHandle.path().toStdString()); + nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: sessionHandle = {}", + sessionHandle.path().toStdString()); - getAvailableSourceTypes(); - } break; - case State::Started: - nhlog::ui()->warn("ScreenCastPortal already started"); - break; - case State::Closing: - break; - } + QDBusConnection::sessionBus().connect(QStringLiteral("org.freedesktop.portal.Desktop"), + sessionHandle.path(), + QStringLiteral("org.freedesktop.portal.Session"), + QStringLiteral("Closed"), + this, + SLOT(closedHandler(QVariantMap))); + + getAvailableSourceTypes(); } void @@ -200,11 +258,11 @@ ScreenCastPortal::getAvailableSourceTypes() << QStringLiteral("AvailableSourceTypes"); QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { - QDBusPendingReply reply = *self; self->deleteLater(); + QDBusPendingReply reply = *self; if (!reply.isValid()) { nhlog::ui()->error("org.freedesktop.DBus.Properties (Get AvailableSourceTypes): {}", @@ -213,29 +271,21 @@ ScreenCastPortal::getAvailableSourceTypes() return; } - switch (state) { - case State::Closed: + if (state != State::Starting) { nhlog::ui()->warn("ScreenCastPortal not starting"); - break; - case State::Starting: { - const auto &value = reply.value().variant(); - if (value.canConvert()) { - availableSourceTypes = value.value(); - } else { - nhlog::ui()->error("Invalid reply from org.freedesktop.DBus.Properties (Get " - "AvailableSourceTypes)"); - close(); - return; - } - - getAvailableCursorModes(); - } break; - case State::Started: - nhlog::ui()->warn("ScreenCastPortal already started"); - break; - case State::Closing: - break; + return; + } + const auto &value = reply.value().variant(); + if (value.canConvert()) { + availableSourceTypes = value.value(); + } else { + nhlog::ui()->error("Invalid reply from org.freedesktop.DBus.Properties (Get " + "AvailableSourceTypes)"); + close(); + return; } + + getAvailableCursorModes(); }); } @@ -250,11 +300,11 @@ ScreenCastPortal::getAvailableCursorModes() << QStringLiteral("AvailableCursorModes"); QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(msg); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { - QDBusPendingReply reply = *self; self->deleteLater(); + QDBusPendingReply reply = *self; if (!reply.isValid()) { nhlog::ui()->error("org.freedesktop.DBus.Properties (Get AvailableCursorModes): {}", @@ -263,29 +313,21 @@ ScreenCastPortal::getAvailableCursorModes() return; } - switch (state) { - case State::Closed: + if (state != State::Starting) { nhlog::ui()->warn("ScreenCastPortal not starting"); - break; - case State::Starting: { - const auto &value = reply.value().variant(); - if (value.canConvert()) { - availableCursorModes = value.value(); - } else { - nhlog::ui()->error("Invalid reply from org.freedesktop.DBus.Properties (Get " - "AvailableCursorModes)"); - close(); - return; - } - - selectSources(); - } break; - case State::Started: - nhlog::ui()->warn("ScreenCastPortal already started"); - break; - case State::Closing: - break; + return; + } + const auto &value = reply.value().variant(); + if (value.canConvert()) { + availableCursorModes = value.value(); + } else { + nhlog::ui()->error("Invalid reply from org.freedesktop.DBus.Properties (Get " + "AvailableCursorModes)"); + close(); + return; } + + selectSources(); }); } @@ -294,12 +336,16 @@ ScreenCastPortal::selectSources() { // Connect before sending the request to avoid missing the reply auto handle_token = make_token(); - QDBusConnection::sessionBus().connect(QString(), - handle_path(handle_token), - QStringLiteral("org.freedesktop.portal.Request"), - QStringLiteral("Response"), - this, - SLOT(selectSourcesHandler(uint, QVariantMap))); + if (!makeConnection(QString(), + handle_path(handle_token), + QStringLiteral("org.freedesktop.portal.Request"), + QStringLiteral("Response"), + SLOT(selectSourcesHandler(uint, QVariantMap)))) { + nhlog::ui()->error( + "Connection to signal Response for org.freedesktop.portal.Request failed"); + close(); + return; + } auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), @@ -321,7 +367,9 @@ ScreenCastPortal::selectSources() QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + self->deleteLater(); QDBusPendingReply reply = *self; + if (!reply.isValid()) { nhlog::ui()->error("org.freedesktop.portal.ScreenCast (SelectSources): {}", reply.error().message().toStdString()); @@ -333,25 +381,19 @@ ScreenCastPortal::selectSources() void ScreenCastPortal::selectSourcesHandler(uint response, const QVariantMap &) { - switch (state) { - case State::Closed: + removeConnection(); + + if (state != State::Starting) { nhlog::ui()->warn("ScreenCastPortal not starting"); - break; - case State::Starting: { - if (response != 0) { - nhlog::ui()->error("org.freedekstop.portal.ScreenCast (SelectSources Response): {}", - response); - close(); - return; - } - start(); - } break; - case State::Started: - nhlog::ui()->warn("ScreenCastPortal already started"); - break; - case State::Closing: - break; + return; + } + if (response != 0) { + nhlog::ui()->error("org.freedesktop.portal.ScreenCast (SelectSources Response): {}", + response); + close(); + return; } + start(); } void @@ -359,12 +401,15 @@ ScreenCastPortal::start() { // Connect before sending the request to avoid missing the reply auto handle_token = make_token(); - QDBusConnection::sessionBus().connect(QString(), - handle_path(handle_token), - QStringLiteral("org.freedesktop.portal.Request"), - QStringLiteral("Response"), - this, - SLOT(startHandler(uint, QVariantMap))); + if (!makeConnection(QString(), + handle_path(handle_token), + QStringLiteral("org.freedesktop.portal.Request"), + QStringLiteral("Response"), + SLOT(startHandler(uint, QVariantMap)))) { + nhlog::ui()->error("Connection to org.freedesktop.portal.Request Response failed"); + close(); + return; + } auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), @@ -377,11 +422,12 @@ ScreenCastPortal::start() QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + self->deleteLater(); QDBusPendingReply reply = *self; + if (!reply.isValid()) { nhlog::ui()->error("org.freedesktop.portal.ScreenCast (Start): {}", reply.error().message().toStdString()); - } else { } }); } @@ -416,6 +462,8 @@ operator>>(const QDBusArgument &argument, PipeWireStream &stream) void ScreenCastPortal::startHandler(uint response, const QVariantMap &results) { + removeConnection(); + if (response != 0) { nhlog::ui()->error("org.freedesktop.portal.ScreenCast (Start Response): {}", response); close(); @@ -448,15 +496,17 @@ ScreenCastPortal::openPipeWireRemote() QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { + self->deleteLater(); QDBusPendingReply reply = *self; + if (!reply.isValid()) { nhlog::ui()->error("org.freedesktop.portal.ScreenCast (OpenPipeWireRemote): {}", reply.error().message().toStdString()); close(); } else { - stream.fd = reply.value().fileDescriptor(); - nhlog::ui()->debug("org.freedesktop.portal.ScreenCast: fd = {}", stream.fd); - + stream.fd = std::move(reply.value()); + nhlog::ui()->error("org.freedesktop.portal.ScreenCast: fd = {}", + stream.fd.fileDescriptor()); state = State::Started; emit readyChanged(); } diff --git a/src/voip/ScreenCastPortal.h b/src/voip/ScreenCastPortal.h index 853ece04..bc1fd143 100644 --- a/src/voip/ScreenCastPortal.h +++ b/src/voip/ScreenCastPortal.h @@ -20,7 +20,7 @@ class ScreenCastPortal final : public QObject public: struct Stream { - int fd; + QDBusUnixFileDescriptor fd; quint32 nodeId; }; @@ -51,6 +51,13 @@ private: void selectSources(); void start(); void openPipeWireRemote(); + bool makeConnection(QString service, + QString path, + QString interface, + QString name, + const char *slot); + void removeConnection(); + void disconnectClose(); QDBusObjectPath sessionHandle; uint availableSourceTypes; uint availableCursorModes; @@ -65,6 +72,7 @@ private: Closing, }; State state = State::Closed; + std::optional> last_connection; }; #endif diff --git a/src/voip/WebRTCSession.cpp b/src/voip/WebRTCSession.cpp index ba75c744..c0cab4ac 100644 --- a/src/voip/WebRTCSession.cpp +++ b/src/voip/WebRTCSession.cpp @@ -1030,7 +1030,7 @@ WebRTCSession::addVideoPipeline(int vp8PayloadType) pipe_ = nullptr; return false; } - g_object_set(pipewiresrc, "fd", (gint)stream->fd, nullptr); + g_object_set(pipewiresrc, "fd", (gint)stream->fd.fileDescriptor(), nullptr); std::string path = std::to_string(stream->nodeId); g_object_set(pipewiresrc, "path", path.c_str(), nullptr); g_object_set(pipewiresrc, "do-timestamp", (gboolean)1, nullptr); -- cgit 1.5.1