#include #include #include #include #include #include #include #include #include #include #include // Global variable to store the session handle static QDBusObjectPath sessionHandle; // QDBus callback for xdg-desktop-portal SelectSources response void handleSelectSourcesResponse(QDBusPendingCallWatcher *watcher) { QDBusPendingCall reply = *watcher; watcher->deleteLater(); if (reply.isError()) { qCritical() << "D-Bus call to SelectSources failed:" << reply.error().message(); fflush(stderr); QCoreApplication::quit(); return; } QList arguments = reply.reply().arguments(); if (arguments.isEmpty()) { qCritical() << "SelectSources response has no arguments."; fflush(stderr); QCoreApplication::quit(); return; } bool success = arguments.at(0).toBool(); if (!success) { qCritical() << "SelectSources request denied or failed."; fflush(stderr); QCoreApplication::quit(); return; } QVariantList sources = arguments.at(1).toList(); if (sources.isEmpty()) { qCritical() << "No sources selected."; fflush(stderr); QCoreApplication::quit(); return; } // For POC, just take the first source QVariantMap sourceMap = sources.at(0).toMap(); if (!sourceMap.contains("pipewire_node_id")) { qCritical() << "Selected source missing pipewire_node_id."; fflush(stderr); QCoreApplication::quit(); return; } quint32 node_id = sourceMap.value("pipewire_node_id").toUInt(); qDebug() << "xdg-desktop-portal granted screen capture. PipeWire node ID:" << node_id; QCoreApplication::quit(); // Quit after getting the node ID for POC } // QDBus callback for xdg-desktop-portal CreateSession response // QDBus callback for xdg-desktop-portal CreateSession response void handleCreateSessionFinished(QDBusPendingCallWatcher *watcher) { QDBusPendingCall reply = *watcher; watcher->deleteLater(); if (reply.isError()) { qCritical() << "D-Bus call to CreateSession failed:" << reply.error().message(); fflush(stderr); QCoreApplication::quit(); return; } QList arguments = reply.reply().arguments(); if (arguments.isEmpty()) { qCritical() << "CreateSession response has no arguments."; fflush(stderr); QCoreApplication::quit(); return; } QVariant firstArg = arguments.at(0); if (!firstArg.canConvert()) { qCritical() << "CreateSession response first argument is not an object path."; fflush(stderr); QCoreApplication::quit(); return; } sessionHandle = qvariant_cast(firstArg); qDebug() << "Session created with handle:" << sessionHandle.path(); // Now call SelectSources QDBusConnection sessionBus = QDBusConnection::sessionBus(); QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.ScreenCast", "SelectSources" ); QVariantMap options; options.insert("multiple", false); // Request single source options.insert("types", (uint)3); // Monitor and Window options.insert("cursor_mode", (uint)4); // Metadata options.insert("persist_mode", (uint)2); // Persist for session (OBS value) options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Use QUuid without braces options.insert("restore_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Add restore_token (OBS value) message << options; QDBusPendingCall pendingCall = sessionBus.asyncCall(message); QDBusPendingCallWatcher *selectSourcesWatcher = new QDBusPendingCallWatcher(pendingCall); // Use a new watcher QObject::connect(selectSourcesWatcher, &QDBusPendingCallWatcher::finished, handleSelectSourcesResponse); } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); qDebug() << "Requesting screen capture via xdg-desktop-portal..."; QDBusConnection sessionBus = QDBusConnection::sessionBus(); if (!sessionBus.isConnected()) { qCritical() << "Failed to connect to D-Bus session bus."; fflush(stderr); return -1; } // Call CreateSession first QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.ScreenCast", "CreateSession" ); QVariantMap options; options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Add handle_token message << options; QDBusPendingCall pendingCall = sessionBus.asyncCall(message); QDBusPendingCallWatcher *mainWatcher = new QDBusPendingCallWatcher(pendingCall); QObject::connect(mainWatcher, &QDBusPendingCallWatcher::finished, handleCreateSessionFinished); return app.exec(); }