[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [xdg-portal-test-kde] /: Add test for screencast portal
From:       Jan Grulich <null () kde ! org>
Date:       2018-02-01 18:26:47
Message-ID: E1ehJZP-0002nz-SO () code ! kde ! org
[Download RAW message or body]

Git commit 866a492f3d28330320921c61967b16b00bee0735 by Jan Grulich.
Committed on 01/02/2018 at 18:26.
Pushed by grulich into branch 'master'.

Add test for screencast portal

M  +4    -0    CMakeLists.txt
M  +3    -0    src/CMakeLists.txt
M  +167  -0    src/portaltest.cpp
M  +15   -0    src/portaltest.h
M  +38   -7    src/portaltest.ui

https://commits.kde.org/xdg-portal-test-kde/866a492f3d28330320921c61967b16b00bee0735

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8b883dd..ba2726b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,6 +16,10 @@ include(ECMPackageConfigHelpers)
 include(ECMOptionalAddSubdirectory)
 include(FeatureSummary)
 
+include(FindPkgConfig)
+
+pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
+
 find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
     Core
     DBus
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f5ba9c2..0a1d370 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -7,6 +7,8 @@ ki18n_wrap_ui(flatpak_portal_test_kde_SRCS
     portaltest.ui
 )
 
+include_directories(${GSTREAMER_INCLUDE_DIRS})
+
 add_executable(portal-test-kde ${flatpak_portal_test_kde_SRCS})
 
 target_link_libraries(portal-test-kde
@@ -16,6 +18,7 @@ target_link_libraries(portal-test-kde
     KF5::I18n
     KF5::KIOFileWidgets
     KF5::Notifications
+    ${GSTREAMER_LIBRARIES}
 )
 
 install(TARGETS portal-test-kde DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/src/portaltest.cpp b/src/portaltest.cpp
index e6cafb2..b38af89 100644
--- a/src/portaltest.cpp
+++ b/src/portaltest.cpp
@@ -37,11 +37,38 @@
 
 #include <KNotification>
 
+#include <gst/gst.h>
+
 Q_LOGGING_CATEGORY(PortalTestKde, "portal-test-kde")
 
+Q_DECLARE_METATYPE(PortalTest::Stream);
+Q_DECLARE_METATYPE(PortalTest::Streams);
+
+const QDBusArgument &operator >> (const QDBusArgument &arg, PortalTest::Stream \
&stream) +{
+    arg.beginStructure();
+    arg >> stream.node_id;
+
+    arg.beginMap();
+    while (!arg.atEnd()) {
+        QString key;
+        QVariant map;
+        arg.beginMapEntry();
+        arg >> key >> map;
+        arg.endMapEntry();
+        stream.map.insert(key, map);
+    }
+    arg.endMap();
+    arg.endStructure();
+
+    return arg;
+}
+
 PortalTest::PortalTest(QWidget *parent, Qt::WindowFlags f)
     : QMainWindow(parent, f)
     , m_mainWindow(new Ui::PortalTest)
+    , m_sessionTokenCounter(0)
+    , m_requestTokenCounter(0)
 {
     QLoggingCategory::setFilterRules(QStringLiteral("portal-test-kde.debug = \
true"));  
@@ -85,6 +112,9 @@ PortalTest::PortalTest(QWidget *parent, Qt::WindowFlags f)
     connect(m_mainWindow->notifyButton, &QPushButton::clicked, this, \
                &PortalTest::sendNotification);
     connect(m_mainWindow->printButton, &QPushButton::clicked, this, \
                &PortalTest::printDocument);
     connect(m_mainWindow->requestDeviceAccess, &QPushButton::clicked, this, \
&PortalTest::requestDeviceAccess); +    connect(m_mainWindow->screenShareButton, \
&QPushButton::clicked, this, &PortalTest::requestScreenSharing); +
+    gst_init(nullptr, nullptr);
 }
 
 PortalTest::~PortalTest()
@@ -342,6 +372,132 @@ void PortalTest::sendNotification()
     notify->sendEvent();
 }
 
+void PortalTest::requestScreenSharing()
+{
+    QDBusMessage message = \
QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), +     \
QLatin1String("/org/freedesktop/portal/desktop"), +                                   \
QLatin1String("org.freedesktop.portal.ScreenCast"), +                                 \
QLatin1String("CreateSession")); +
+    message << QVariantMap { { QLatin1String("session_handle_token"), \
getSessionToken() }, { QLatin1String("handle_token"), getRequestToken() } }; +
+    QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
+    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
+    connect(watcher, &QDBusPendingCallWatcher::finished, [this] \
(QDBusPendingCallWatcher *watcher) { +        QDBusPendingReply<QDBusObjectPath> \
reply = *watcher; +        if (reply.isError()) {
+            qWarning() << "Couldn't get reply";
+            qWarning() << "Error: " << reply.error().message();
+        } else {
+            QDBusConnection::sessionBus().connect(QString(),
+                                                reply.value().path(),
+                                                \
QLatin1String("org.freedesktop.portal.Request"), +                                    \
QLatin1String("Response"), +                                                this,
+                                                \
SLOT(gotCreateSessionResponse(uint,QVariantMap))); +        }
+    });
+}
+
+void PortalTest::gotCreateSessionResponse(uint response, const QVariantMap &results)
+{
+    if (response != 0) {
+        qWarning() << "Failed to create session: " << response;
+        return;
+    }
+
+    QDBusMessage message = \
QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), +     \
QLatin1String("/org/freedesktop/portal/desktop"), +                                   \
QLatin1String("org.freedesktop.portal.ScreenCast"), +                                 \
QLatin1String("SelectSources")); +
+    m_session = results.value(QLatin1String("session_handle")).toString();
+
+    message << QVariant::fromValue(QDBusObjectPath(m_session))
+            << QVariantMap { { QLatin1String("multiple"), false},
+                             { QLatin1String("type"), \
m_mainWindow->screenShareCombobox->currentIndex() + 1}, +                             \
{ QLatin1String("handle_token"), getRequestToken() } }; +
+    QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
+    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
+    connect(watcher, &QDBusPendingCallWatcher::finished, [this] \
(QDBusPendingCallWatcher *watcher) { +        QDBusPendingReply<QDBusObjectPath> \
reply = *watcher; +        if (reply.isError()) {
+            qWarning() << "Couldn't get reply";
+            qWarning() << "Error: " << reply.error().message();
+        } else {
+            QDBusConnection::sessionBus().connect(QString(),
+                                                reply.value().path(),
+                                                \
QLatin1String("org.freedesktop.portal.Request"), +                                    \
QLatin1String("Response"), +                                                this,
+                                                \
SLOT(gotSelectSourcesResponse(uint,QVariantMap))); +        }
+    });
+}
+
+void PortalTest::gotSelectSourcesResponse(uint response, const QVariantMap &results)
+{
+    if (response != 0) {
+        qWarning() << "Failed to select sources: " << response;
+        return;
+    }
+
+    QDBusMessage message = \
QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), +     \
QLatin1String("/org/freedesktop/portal/desktop"), +                                   \
QLatin1String("org.freedesktop.portal.ScreenCast"), +                                 \
QLatin1String("Start")); +
+    message << QVariant::fromValue(QDBusObjectPath(m_session))
+            << QString() // parent_window
+            << QVariantMap { { QLatin1String("handle_token"), getRequestToken() } };
+
+    QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
+    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
+    connect(watcher, &QDBusPendingCallWatcher::finished, [this] \
(QDBusPendingCallWatcher *watcher) { +        QDBusPendingReply<QDBusObjectPath> \
reply = *watcher; +        if (reply.isError()) {
+            qWarning() << "Couldn't get reply";
+            qWarning() << "Error: " << reply.error().message();
+        } else {
+            QDBusConnection::sessionBus().connect(QString(),
+                                                reply.value().path(),
+                                                \
QLatin1String("org.freedesktop.portal.Request"), +                                    \
QLatin1String("Response"), +                                                this,
+                                                \
SLOT(gotStartResponse(uint,QVariantMap))); +        }
+    });
+}
+
+void PortalTest::gotStartResponse(uint response, const QVariantMap &results)
+{
+    if (response != 0) {
+        qWarning() << "Failed to start: " << response;
+    }
+
+    Streams streams = qdbus_cast<Streams>(results.value(QLatin1String("streams")));
+    Q_FOREACH (Stream stream, streams) {
+        QDBusMessage message = \
QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), +     \
QLatin1String("/org/freedesktop/portal/desktop"), +                                   \
QLatin1String("org.freedesktop.portal.ScreenCast"), +                                 \
QLatin1String("OpenPipeWireRemote")); +
+        message << QVariant::fromValue(QDBusObjectPath(m_session)) << QVariantMap();
+
+        QDBusPendingCall pendingCall = \
QDBusConnection::sessionBus().asyncCall(message); +        \
pendingCall.waitForFinished(); +        QDBusPendingReply<QDBusUnixFileDescriptor> \
reply = pendingCall.reply(); +        if (reply.isError()) {
+            qWarning() << "Failed to get fd for node_id " << stream.node_id;
+        }
+
+        QString gstLaunch = QString("pipewiresrc fd=%1 path=%2 ! videoconvert ! \
xvimagesink").arg(reply.value().fileDescriptor()).arg(stream.node_id); +        \
GstElement *element = gst_parse_launch(gstLaunch.toUtf8(), nullptr); +        \
gst_element_set_state(element, GST_STATE_PLAYING); +    }
+}
+
 bool PortalTest::isRunningSandbox()
 {
     QString runtimeDir = qgetenv("XDG_RUNTIME_DIR");
@@ -355,3 +511,14 @@ bool PortalTest::isRunningSandbox()
     return file.exists();
 }
 
+QString PortalTest::getSessionToken()
+{
+    m_sessionTokenCounter += 1;
+    return QString("u%1").arg(m_sessionTokenCounter);
+}
+
+QString PortalTest::getRequestToken()
+{
+    m_requestTokenCounter += 1;
+    return QString("u%1").arg(m_requestTokenCounter);
+}
diff --git a/src/portaltest.h b/src/portaltest.h
index f9314b5..9c5c0a0 100644
--- a/src/portaltest.h
+++ b/src/portaltest.h
@@ -37,10 +37,19 @@ class PortalTest : public QMainWindow
 {
     Q_OBJECT
 public:
+    typedef struct {
+        uint node_id;
+        QVariantMap map;
+    } Stream;
+    typedef QList<Stream> Streams;
+
     PortalTest(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
     ~PortalTest();
 
 public Q_SLOTS:
+    void gotCreateSessionResponse(uint response, const QVariantMap &results);
+    void gotSelectSourcesResponse(uint response, const QVariantMap &results);
+    void gotStartResponse(uint response, const QVariantMap &results);
     void gotPrintResponse(uint response, const QVariantMap &results);
     void gotPreparePrintResponse(uint response, const QVariantMap &results);
     void inhibitRequested();
@@ -51,11 +60,17 @@ public Q_SLOTS:
     void requestDeviceAccess();
     void saveFileRequested();
     void sendNotification();
+    void requestScreenSharing();
 private:
     bool isRunningSandbox();
+    QString getSessionToken();
+    QString getRequestToken();
 
     QDBusObjectPath m_inhibitionRequest;
+    QString m_session;
     Ui::PortalTest * m_mainWindow;
+    uint m_sessionTokenCounter;
+    uint m_requestTokenCounter;
 };
 
 #endif // PORTAL_TEST_KDE_H
diff --git a/src/portaltest.ui b/src/portaltest.ui
index f9ae68a..01022c9 100644
--- a/src/portaltest.ui
+++ b/src/portaltest.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>640</width>
-    <height>482</height>
+    <width>622</width>
+    <height>510</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -274,15 +274,47 @@
       </item>
      </layout>
     </item>
-    <item row="13" column="0" colspan="2">
+    <item row="13" column="0">
+     <widget class="QLabel" name="label_13">
+      <property name="text">
+       <string>Screen sharing:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="13" column="1">
+     <layout class="QHBoxLayout" name="horizontalLayout_5">
+      <item>
+       <widget class="QComboBox" name="screenShareCombobox">
+        <item>
+         <property name="text">
+          <string>Monitor</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Window</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="screenShareButton">
+        <property name="text">
+         <string>Request</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item row="14" column="1">
      <spacer name="verticalSpacer">
       <property name="orientation">
        <enum>Qt::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
-        <width>28</width>
-        <height>382</height>
+        <width>25</width>
+        <height>13</height>
        </size>
       </property>
      </spacer>
@@ -308,8 +340,7 @@
    <zorder>label_10</zorder>
    <zorder>label_11</zorder>
    <zorder>label_12</zorder>
-   <zorder>deviceCombobox</zorder>
-   <zorder>requestDeviceAccess</zorder>
+   <zorder>label_13</zorder>
   </widget>
   <widget class="QStatusBar" name="statusbar"/>
  </widget>


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic