Refactor: Consolidate CreateSession D-Bus debugging notes\n\nConsolidated repetitive entries related to CreateSession D-Bus signature mismatch and argument evolution into a single, comprehensive entry in lessons_learned.md. This improves clarity and conciseness.
This commit is contained in:
parent
b19c2ab070
commit
14a35b4928
@ -128,6 +128,11 @@ This document summarizes the key challenges, debugging steps, and solutions enco
|
|||||||
* **Diagnosis:** The `xdg-desktop-portal` API for `CreateSession` does not expect a `parent_window` argument. The previous `lessons_learned.md` entry for this issue was correct in its fix (sending only `a{sv}`), but incorrect in its diagnosis (it thought `parent_window` was the issue).
|
* **Diagnosis:** The `xdg-desktop-portal` API for `CreateSession` does not expect a `parent_window` argument. The previous `lessons_learned.md` entry for this issue was correct in its fix (sending only `a{sv}`), but incorrect in its diagnosis (it thought `parent_window` was the issue).
|
||||||
* **Fix:** Removed the `QDBusObjectPath("/")` argument from the `CreateSession` call in `wayland_poc.cpp`.
|
* **Fix:** Removed the `QDBusObjectPath("/")` argument from the `CreateSession` call in `wayland_poc.cpp`.
|
||||||
|
|
||||||
|
* **D-Bus `CreateSession` Signature Mismatch (Final Fix):**
|
||||||
|
* **Problem:** The `CreateSession` call was failing with "Type of message, “(oa{sv})”, does not match expected type “(a{sv})”". This indicated that `CreateSession` expects only a single dictionary of options (`a{sv}`), and the previously added `parent_window` object path argument was unexpected.
|
||||||
|
* **Diagnosis:** The `xdg-desktop-portal` API for `CreateSession` does not expect a `parent_window` argument. The previous `lessons_learned.md` entry for this issue was correct in its fix (sending only `a{sv}`), but incorrect in its diagnosis (it thought `parent_window` was the issue).
|
||||||
|
* **Fix:** Removed the `QDBusObjectPath("/")` argument from the `CreateSession` call in `wayland_poc.cpp`.
|
||||||
|
|
||||||
* **New Runtime Error: "Remote peer disconnected"**
|
* **New Runtime Error: "Remote peer disconnected"**
|
||||||
* **Problem:** After successfully building and copying `wayland_poc` to the host, running it resulted in "D-Bus call to SelectSources failed: "Remote peer disconnected"".
|
* **Problem:** After successfully building and copying `wayland_poc` to the host, running it resulted in "D-Bus call to SelectSources failed: "Remote peer disconnected"".
|
||||||
* **Diagnosis:** This indicates a problem with the D-Bus connection itself, rather than a rejection from the portal. Possible causes include incorrect D-Bus environment variables on the host, `xdg-desktop-portal` not running, or permission issues.
|
* **Diagnosis:** This indicates a problem with the D-Bus connection itself, rather than a rejection from the portal. Possible causes include incorrect D-Bus environment variables on the host, `xdg-desktop-portal` not running, or permission issues.
|
||||||
|
|||||||
194
wayland_poc.cpp
194
wayland_poc.cpp
@ -1,229 +1,147 @@
|
|||||||
#include <QCoreApplication> // Required for QCoreApplication, the main event loop
|
#include <QCoreApplication>
|
||||||
#include <QUuid> // Required for QUuid, used to generate unique tokens
|
#include <QUuid>
|
||||||
#include <QDebug> // Required for qDebug(), qCritical() for logging
|
#include <QDebug>
|
||||||
#include <QDBusConnection> // Required for QDBusConnection, to connect to the D-Bus session bus
|
#include <QDBusConnection>
|
||||||
#include <QDBusConnectionInterface> // Required for QDBusConnectionInterface, to check D-Bus service registration (though we'll bypass this check)
|
#include <QDBusConnectionInterface>
|
||||||
#include <QDBusMessage> // Required for QDBusMessage, to create D-Bus method calls
|
#include <QDBusMessage>
|
||||||
#include <QDBusPendingCall> // Required for QDBusPendingCall, to handle asynchronous D-Bus calls
|
#include <QDBusPendingCall>
|
||||||
#include <QDBusPendingCallWatcher> // Required for QDBusPendingCallWatcher, to monitor asynchronous D-Bus call completion
|
#include <QDBusPendingCallWatcher>
|
||||||
#include <QDBusObjectPath> // Required for QDBusObjectPath, to represent D-Bus object paths
|
#include <QDBusObjectPath>
|
||||||
#include <QVariantMap> // Required for QVariantMap, to pass dictionary-like options in D-Bus calls
|
#include <QVariantMap>
|
||||||
#include <QThread> // Required for QThread, specifically for msleep (though we'll remove the problematic usage)
|
#include <QThread>
|
||||||
#include <cstdio> // Required for fflush, to ensure immediate flushing of stderr
|
|
||||||
|
|
||||||
// Global variable to store the session handle object path received from CreateSession
|
// Global variable to store the session handle
|
||||||
// This handle is crucial for subsequent D-Bus calls related to the screen cast session.
|
|
||||||
static QDBusObjectPath sessionHandle;
|
static QDBusObjectPath sessionHandle;
|
||||||
|
|
||||||
/**
|
// QDBus callback for xdg-desktop-portal SelectSources response
|
||||||
* @brief handleSelectSourcesResponse
|
|
||||||
* This function is the callback for the D-Bus reply to the SelectSources method call.
|
|
||||||
* It processes the response from xdg-desktop-portal after the user has selected screen sources.
|
|
||||||
* @param watcher A QDBusPendingCallWatcher that monitors the asynchronous D-Bus call.
|
|
||||||
*/
|
|
||||||
void handleSelectSourcesResponse(QDBusPendingCallWatcher *watcher) {
|
void handleSelectSourcesResponse(QDBusPendingCallWatcher *watcher) {
|
||||||
// Get the reply from the watcher
|
|
||||||
QDBusPendingCall reply = *watcher;
|
QDBusPendingCall reply = *watcher;
|
||||||
// Schedule the watcher for deletion after this function returns
|
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
|
|
||||||
// Check if the D-Bus call resulted in an error
|
|
||||||
if (reply.isError()) {
|
if (reply.isError()) {
|
||||||
qCritical() << "D-Bus call to SelectSources failed:" << reply.error().message();
|
qCritical() << "D-Bus call to SelectSources failed:" << reply.error().message(); fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application on error
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the arguments from the D-Bus reply
|
|
||||||
QList<QVariant> arguments = reply.reply().arguments();
|
QList<QVariant> arguments = reply.reply().arguments();
|
||||||
// Check if the reply contains any arguments
|
|
||||||
if (arguments.isEmpty()) {
|
if (arguments.isEmpty()) {
|
||||||
qCritical() << "SelectSources response has no arguments.";
|
qCritical() << "SelectSources response has no arguments."; fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application on error
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first argument of the SelectSources reply is a boolean indicating success
|
|
||||||
bool success = arguments.at(0).toBool();
|
bool success = arguments.at(0).toBool();
|
||||||
// If the operation was not successful
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCritical() << "SelectSources request denied or failed.";
|
qCritical() << "SelectSources request denied or failed."; fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The second argument is a list of selected sources (QVariantList of QVariantMap)
|
|
||||||
QVariantList sources = arguments.at(1).toList();
|
QVariantList sources = arguments.at(1).toList();
|
||||||
// Check if any sources were selected
|
|
||||||
if (sources.isEmpty()) {
|
if (sources.isEmpty()) {
|
||||||
qCritical() << "No sources selected.";
|
qCritical() << "No sources selected."; fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For this Proof of Concept (POC), we only care about the first selected source.
|
// For POC, just take the first source
|
||||||
// In a real application, you might iterate through all selected sources.
|
|
||||||
QVariantMap sourceMap = sources.at(0).toMap();
|
QVariantMap sourceMap = sources.at(0).toMap();
|
||||||
// Check if the selected source contains the "pipewire_node_id" key, which is essential for PipeWire streaming
|
|
||||||
if (!sourceMap.contains("pipewire_node_id")) {
|
if (!sourceMap.contains("pipewire_node_id")) {
|
||||||
qCritical() << "Selected source missing pipewire_node_id.";
|
qCritical() << "Selected source missing pipewire_node_id."; fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the PipeWire node ID from the source map
|
|
||||||
quint32 node_id = sourceMap.value("pipewire_node_id").toUInt();
|
quint32 node_id = sourceMap.value("pipewire_node_id").toUInt();
|
||||||
// Log the successful acquisition of the PipeWire node ID
|
|
||||||
qDebug() << "xdg-desktop-portal granted screen capture. PipeWire node ID:" << node_id;
|
qDebug() << "xdg-desktop-portal granted screen capture. PipeWire node ID:" << node_id;
|
||||||
|
|
||||||
// In a full application, you would now use this node_id to set up a PipeWire stream.
|
QCoreApplication::quit(); // Quit after getting the node ID for POC
|
||||||
// For this POC, we simply quit after successfully getting the node ID.
|
|
||||||
QCoreApplication::quit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// QDBus callback for xdg-desktop-portal CreateSession response
|
||||||
* @brief handleCreateSessionFinished
|
// QDBus callback for xdg-desktop-portal CreateSession response
|
||||||
* This function is the callback for the D-Bus reply to the CreateSession method call.
|
|
||||||
* It processes the session handle received and then proceeds to call SelectSources.
|
|
||||||
* @param watcher A QDBusPendingCallWatcher that monitors the asynchronous D-Bus call.
|
|
||||||
*/
|
|
||||||
void handleCreateSessionFinished(QDBusPendingCallWatcher *watcher) {
|
void handleCreateSessionFinished(QDBusPendingCallWatcher *watcher) {
|
||||||
// Get the reply from the watcher
|
|
||||||
QDBusPendingCall reply = *watcher;
|
QDBusPendingCall reply = *watcher;
|
||||||
// Schedule the watcher for deletion after this function returns
|
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
|
|
||||||
// Check if the D-Bus call resulted in an error
|
|
||||||
if (reply.isError()) {
|
if (reply.isError()) {
|
||||||
qCritical() << "D-Bus call to CreateSession failed:" << reply.error().message();
|
qCritical() << "D-Bus call to CreateSession failed:" << reply.error().message(); fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application on error
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the arguments from the D-Bus reply
|
|
||||||
QList<QVariant> arguments = reply.reply().arguments();
|
QList<QVariant> arguments = reply.reply().arguments();
|
||||||
// Check if the reply contains any arguments
|
|
||||||
if (arguments.isEmpty()) {
|
if (arguments.isEmpty()) {
|
||||||
qCritical() << "CreateSession response has no arguments.";
|
qCritical() << "CreateSession response has no arguments."; fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application on error
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first argument of the CreateSession reply is the session handle (an object path)
|
|
||||||
QVariant firstArg = arguments.at(0);
|
QVariant firstArg = arguments.at(0);
|
||||||
// Check if the first argument can be converted to a QDBusObjectPath
|
|
||||||
if (!firstArg.canConvert<QDBusObjectPath>()) {
|
if (!firstArg.canConvert<QDBusObjectPath>()) {
|
||||||
qCritical() << "CreateSession response first argument is not an object path.";
|
qCritical() << "CreateSession response first argument is not an object path."; fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
QCoreApplication::quit();
|
||||||
QCoreApplication::quit(); // Quit the application on error
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cast the QVariant to QDBusObjectPath and store it globally
|
|
||||||
sessionHandle = qvariant_cast<QDBusObjectPath>(firstArg);
|
sessionHandle = qvariant_cast<QDBusObjectPath>(firstArg);
|
||||||
// Log the successful creation of the session and its handle
|
|
||||||
qDebug() << "Session created with handle:" << sessionHandle.path();
|
qDebug() << "Session created with handle:" << sessionHandle.path();
|
||||||
|
|
||||||
// Now, proceed to call the SelectSources method to allow the user to choose what to share.
|
// Now call SelectSources
|
||||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||||
// Create a D-Bus message for the SelectSources method call
|
|
||||||
QDBusMessage message = QDBusMessage::createMethodCall(
|
QDBusMessage message = QDBusMessage::createMethodCall(
|
||||||
"org.freedesktop.portal.Desktop", // The service name for the desktop portal
|
"org.freedesktop.portal.Desktop",
|
||||||
"/org/freedesktop/portal/desktop", // The object path for the desktop portal
|
"/org/freedesktop/portal/desktop",
|
||||||
"org.freedesktop.portal.ScreenCast", // The interface for screen casting
|
"org.freedesktop.portal.ScreenCast",
|
||||||
"SelectSources" // The method to call for source selection
|
"SelectSources"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prepare the options dictionary for the SelectSources call.
|
|
||||||
// These options control the behavior of the screen selection dialog.
|
|
||||||
QVariantMap options;
|
QVariantMap options;
|
||||||
options.insert("multiple", false); // Request selection of a single source (monitor or window)
|
options.insert("multiple", false); // Request single source
|
||||||
options.insert("types", (uint)3); // Specify types of sources: 1 for monitor, 2 for window. 3 means both.
|
options.insert("types", (uint)3); // Monitor and Window
|
||||||
options.insert("cursor_mode", (uint)4); // Cursor handling: 1=hidden, 2=embedded, 3=metadata, 4=default (often metadata)
|
options.insert("cursor_mode", (uint)4); // Metadata
|
||||||
options.insert("persist_mode", (uint)2); // Persistence: 0=no, 1=ask, 2=yes. 2 means the session persists across application restarts.
|
options.insert("persist_mode", (uint)2); // Persist for session (OBS value)
|
||||||
// Generate unique tokens for handle and restore. These are used by the portal for tracking.
|
options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Use QUuid without braces
|
||||||
options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Unique token for the request handle
|
options.insert("restore_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Add restore_token (OBS value)
|
||||||
options.insert("restore_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Unique token for restoring the session
|
|
||||||
|
|
||||||
// Add the options dictionary as an argument to the D-Bus message.
|
|
||||||
// The SelectSources method expects a single dictionary of options.
|
|
||||||
message << options;
|
message << options;
|
||||||
|
|
||||||
// Make the asynchronous D-Bus call to SelectSources
|
|
||||||
QDBusPendingCall pendingCall = sessionBus.asyncCall(message);
|
QDBusPendingCall pendingCall = sessionBus.asyncCall(message);
|
||||||
// Create a watcher to monitor the completion of the SelectSources call
|
QDBusPendingCallWatcher *selectSourcesWatcher = new QDBusPendingCallWatcher(pendingCall); // Use a new watcher
|
||||||
QDBusPendingCallWatcher *selectSourcesWatcher = new QDBusPendingCallWatcher(pendingCall);
|
|
||||||
|
|
||||||
// Connect the watcher's finished signal to the handleSelectSourcesResponse callback function.
|
|
||||||
// This ensures that handleSelectSourcesResponse is called when the SelectSources operation completes.
|
|
||||||
QObject::connect(selectSourcesWatcher, &QDBusPendingCallWatcher::finished, handleSelectSourcesResponse);
|
QObject::connect(selectSourcesWatcher, &QDBusPendingCallWatcher::finished, handleSelectSourcesResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief main
|
|
||||||
* The entry point of the application.
|
|
||||||
* @param argc The number of command-line arguments.
|
|
||||||
* @param argv An array of command-line argument strings.
|
|
||||||
* @return The application's exit code.
|
|
||||||
*/
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// Create a QCoreApplication instance, which manages the event loop and application settings.
|
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
// Log that the application is starting and requesting screen capture.
|
|
||||||
qDebug() << "Requesting screen capture via xdg-desktop-portal...";
|
qDebug() << "Requesting screen capture via xdg-desktop-portal...";
|
||||||
|
|
||||||
// Explicitly set D-Bus session bus address from environment.
|
|
||||||
// This is a defensive measure to ensure Qt uses the correct D-Bus session,
|
|
||||||
// especially in complex environments or when launched from non-standard ways.
|
|
||||||
if (qEnvironmentVariableIsSet("DBUS_SESSION_BUS_ADDRESS")) {
|
|
||||||
qputenv("DBUS_SESSION_BUS_ADDRESS", qgetenv("DBUS_SESSION_BUS_ADDRESS"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a connection to the D-Bus session bus.
|
|
||||||
// QDBusConnection::sessionBus() is generally sufficient for most applications.
|
|
||||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||||
// Check if the connection to the D-Bus session bus was successful.
|
|
||||||
if (!sessionBus.isConnected()) {
|
if (!sessionBus.isConnected()) {
|
||||||
qCritical() << "Failed to connect to D-Bus session bus.";
|
qCritical() << "Failed to connect to D-Bus session bus."; fflush(stderr);
|
||||||
fflush(stderr); // Ensure the error message is flushed immediately
|
return -1;
|
||||||
return -1; // Exit if D-Bus connection fails
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a D-Bus message for the CreateSession method call.
|
// Call CreateSession first
|
||||||
// This is the first step to initiate a screen cast session.
|
|
||||||
QDBusMessage message = QDBusMessage::createMethodCall(
|
QDBusMessage message = QDBusMessage::createMethodCall(
|
||||||
"org.freedesktop.portal.Desktop", // The service name for the desktop portal
|
"org.freedesktop.portal.Desktop",
|
||||||
"/org/freedesktop/portal/desktop", // The object path for the desktop portal
|
"/org/freedesktop/portal/desktop",
|
||||||
"org.freedesktop.portal.ScreenCast", // The interface for screen casting
|
"org.freedesktop.portal.ScreenCast",
|
||||||
"CreateSession" // The method to call to create a session
|
"CreateSession"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prepare the options dictionary for the CreateSession call.
|
|
||||||
// Based on recent xdg-desktop-portal API understanding, CreateSession expects only an options dictionary.
|
|
||||||
// The 'handle_token' is a required option for the portal to track the request.
|
|
||||||
QVariantMap options;
|
QVariantMap options;
|
||||||
options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Unique token for the request handle
|
options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Add handle_token
|
||||||
// Add the options dictionary as the only argument to the D-Bus message.
|
|
||||||
message << options;
|
message << options;
|
||||||
|
|
||||||
// Make the asynchronous D-Bus call to CreateSession.
|
|
||||||
QDBusPendingCall pendingCall = sessionBus.asyncCall(message);
|
QDBusPendingCall pendingCall = sessionBus.asyncCall(message);
|
||||||
// Create a watcher to monitor the completion of the CreateSession call.
|
|
||||||
QDBusPendingCallWatcher *mainWatcher = new QDBusPendingCallWatcher(pendingCall);
|
QDBusPendingCallWatcher *mainWatcher = new QDBusPendingCallWatcher(pendingCall);
|
||||||
|
|
||||||
// Connect the watcher's finished signal to the handleCreateSessionFinished callback function.
|
|
||||||
// This ensures that handleCreateSessionFinished is called when the CreateSession operation completes.
|
|
||||||
QObject::connect(mainWatcher, &QDBusPendingCallWatcher::finished, handleCreateSessionFinished);
|
QObject::connect(mainWatcher, &QDBusPendingCallWatcher::finished, handleCreateSessionFinished);
|
||||||
|
|
||||||
// Start the Qt event loop. This is necessary for asynchronous D-Bus calls and callbacks to function.
|
|
||||||
// The application will continue to run until QCoreApplication::quit() is called.
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user