diff --git a/data/kde.portal b/data/kde.portal
index 2036d64..cc2dac4 100644
--- a/data/kde.portal
+++ b/data/kde.portal
@@ -1,4 +1,4 @@
[portal]
DBusName=org.freedesktop.impl.portal.desktop.kde
-Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Email;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Inhibit;org.freedesktop.impl.portal.Notification;org.freedesktop.impl.portal.Print;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.Screenshot
+Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Email;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Inhibit;org.freedesktop.impl.portal.Notification;org.freedesktop.impl.portal.Print;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.RemoteDesktop
UseIn=KDE
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 084b148..a08ead7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,63 +1,66 @@
add_definitions(-DTRANSLATION_DOMAIN="xdg-desktop-portal-kde")
include_directories(${Qt5PrintSupport_PRIVATE_INCLUDE_DIRS})
set(xdg_desktop_portal_kde_SRCS
xdg-desktop-portal-kde.cpp
access.cpp
accessdialog.cpp
appchooser.cpp
appchooserdialog.cpp
appchooserdialogitem.cpp
desktopportal.cpp
email.cpp
filechooser.cpp
inhibit.cpp
notification.cpp
print.cpp
request.cpp
session.cpp
screenshot.cpp
screenshotdialog.cpp
)
if (SCREENCAST_ENABLED)
set (xdg_desktop_portal_kde_SRCS
${xdg_desktop_portal_kde_SRCS}
screencast.cpp
screencaststream.cpp
- screenchooserdialog.cpp)
+ screenchooserdialog.cpp
+ remotedesktop.cpp
+ remotedesktopdialog.cpp)
ki18n_wrap_ui(xdg_desktop_portal_kde_SRCS
- screenchooserdialog.ui)
+ screenchooserdialog.ui
+ remotedesktopdialog.ui)
endif()
ki18n_wrap_ui(xdg_desktop_portal_kde_SRCS
accessdialog.ui
screenshotdialog.ui
)
add_executable(xdg-desktop-portal-kde ${xdg_desktop_portal_kde_SRCS})
target_link_libraries(xdg-desktop-portal-kde
Qt5::Core
Qt5::DBus
Qt5::Concurrent
Qt5::PrintSupport
Qt5::Widgets
KF5::CoreAddons
KF5::I18n
KF5::Notifications
KF5::WaylandClient
KF5::WidgetsAddons
)
if (SCREENCAST_ENABLED)
target_link_libraries(xdg-desktop-portal-kde
PipeWire::PipeWire
GLIB2::GLIB2
${Epoxy_LIBRARIES}
GBM::GBM)
endif()
install(TARGETS xdg-desktop-portal-kde DESTINATION ${KDE_INSTALL_LIBEXECDIR})
diff --git a/src/desktopportal.cpp b/src/desktopportal.cpp
index 388e6ff..8c9aa1e 100644
--- a/src/desktopportal.cpp
+++ b/src/desktopportal.cpp
@@ -1,49 +1,50 @@
/*
* Copyright © 2016 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Jan Grulich
*/
#include "desktopportal.h"
#include
#include
#include
#include
#include
Q_LOGGING_CATEGORY(XdgDesktopPortalKdeDesktopPortal, "xdp-kde-desktop-portal")
DesktopPortal::DesktopPortal(QObject *parent)
: QObject(parent)
, m_access(new AccessPortal(this))
, m_appChooser(new AppChooserPortal(this))
, m_email(new EmailPortal(this))
, m_fileChooser(new FileChooserPortal(this))
, m_inhibit(new InhibitPortal(this))
, m_notification(new NotificationPortal(this))
, m_print(new PrintPortal(this))
#if SCREENCAST_ENABLED
, m_screenCast(new ScreenCastPortal(this))
+ , m_remoteDesktop(new RemoteDesktopPortal(this))
#endif
, m_screenshot(new ScreenshotPortal(this))
{
}
DesktopPortal::~DesktopPortal()
{
}
diff --git a/src/desktopportal.h b/src/desktopportal.h
index 3b77777..d65d0a1 100644
--- a/src/desktopportal.h
+++ b/src/desktopportal.h
@@ -1,61 +1,63 @@
/*
* Copyright © 2016 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Jan Grulich
*/
#ifndef XDG_DESKTOP_PORTAL_KDE_DESKTOP_PORTAL_H
#define XDG_DESKTOP_PORTAL_KDE_DESKTOP_PORTAL_H
#include
#include
#include "access.h"
#include "appchooser.h"
#include "email.h"
#include "filechooser.h"
#include "inhibit.h"
#include "notification.h"
#include "print.h"
#if SCREENCAST_ENABLED
#include "screencast.h"
+#include "remotedesktop.h"
#endif
#include "screenshot.h"
class DesktopPortal : public QObject
{
Q_OBJECT
public:
explicit DesktopPortal(QObject *parent = nullptr);
~DesktopPortal();
private:
AccessPortal *m_access;
AppChooserPortal *m_appChooser;
EmailPortal *m_email;
FileChooserPortal *m_fileChooser;
InhibitPortal *m_inhibit;
NotificationPortal *m_notification;
PrintPortal *m_print;
#if SCREENCAST_ENABLED
ScreenCastPortal *m_screenCast;
+ RemoteDesktopPortal *m_remoteDesktop;
#endif
ScreenshotPortal *m_screenshot;
};
#endif // XDG_DESKTOP_PORTAL_KDE_DESKTOP_PORTAL_H
diff --git a/src/remotedesktop.cpp b/src/remotedesktop.cpp
new file mode 100644
index 0000000..2cd04b3
--- /dev/null
+++ b/src/remotedesktop.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright © 2018 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors:
+ * Jan Grulich
+ */
+
+#include "remotedesktop.h"
+#include "session.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+Q_LOGGING_CATEGORY(XdgDesktopPortalKdeRemoteDesktop, "xdp-kde-remotedesktop")
+
+RemoteDesktopPortal::RemoteDesktopPortal(QObject *parent)
+ : QDBusAbstractAdaptor(parent)
+{
+}
+
+RemoteDesktopPortal::~RemoteDesktopPortal()
+{
+}
+
+uint RemoteDesktopPortal::CreateSession(const QDBusObjectPath &handle,
+ const QDBusObjectPath &session_handle,
+ const QString &app_id,
+ const QVariantMap &options,
+ QVariantMap &results)
+{
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "CreateSession called with parameters:";
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path();
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path();
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id;
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options;
+
+ Session *session = Session::createSession(this, Session::RemoteDesktop, app_id, session_handle.path());
+
+ if (!session) {
+ return 2;
+ }
+
+ connect(session, &Session::closed, [this] () {
+ // TODO
+ });
+
+ return 0;
+}
+
+uint RemoteDesktopPortal::SelectDevices(const QDBusObjectPath &handle,
+ const QDBusObjectPath &session_handle,
+ const QString &app_id,
+ const QVariantMap &options,
+ QVariantMap &results)
+{
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "SelectDevices called with parameters:";
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path();
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path();
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id;
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options;
+
+ RemoteDesktopPortal::DeviceTypes types = RemoteDesktopPortal::All;
+
+ RemoteDesktopSession *session = qobject_cast(Session::getSession(session_handle.path()));
+
+ if (!session) {
+ qCWarning(XdgDesktopPortalKdeRemoteDesktop) << "Tried to select sources on non-existing session " << session_handle.path();
+ return 2;
+ }
+
+ if (options.contains(QLatin1String("types"))) {
+ types = (DeviceTypes)(options.value(QLatin1String("types")).toUInt());
+ }
+ session->setDeviceTypes(types);
+
+ return 0;
+}
+
+uint RemoteDesktopPortal::Start(const QDBusObjectPath &handle,
+ const QDBusObjectPath &session_handle,
+ const QString &app_id,
+ const QString &parent_window,
+ const QVariantMap &options,
+ QVariantMap &results)
+{
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << "Start called with parameters:";
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " handle: " << handle.path();
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " session_handle: " << session_handle.path();
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " app_id: " << app_id;
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " parent_window: " << parent_window;
+ qCDebug(XdgDesktopPortalKdeRemoteDesktop) << " options: " << options;
+
+ return 0;
+}
+
+void RemoteDesktopPortal::NotifyPointerMotion(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ double dx,
+ double dy)
+{
+}
+
+void RemoteDesktopPortal::NotifyPointerMotionAbsolute(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint stream,
+ double dx,
+ double dy)
+{
+}
+
+void RemoteDesktopPortal::NotifyPointerButton(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ int button,
+ uint state)
+{
+}
+
+void RemoteDesktopPortal::NotifyPointerAxis(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ double dx,
+ double dy)
+{
+}
+
+void RemoteDesktopPortal::NotifyPointerAxisDiscrete(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint axis,
+ int steps)
+{
+}
+
+void RemoteDesktopPortal::NotifyKeyboardKeysym(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ int keysym,
+ uint state)
+{
+}
+
+void RemoteDesktopPortal::NotifyKeyboardKeycode(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ int keycode,
+ uint state)
+{
+}
+
+void RemoteDesktopPortal::NotifyTouchDown(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint stream,
+ uint slot,
+ int x,
+ int y)
+{
+}
+
+void RemoteDesktopPortal::NotifyTouchMotion(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint stream,
+ uint slot,
+ int x,
+ int y)
+{
+}
+
+void RemoteDesktopPortal::NotifyTouchUp(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint slot)
+{
+}
diff --git a/src/remotedesktop.h b/src/remotedesktop.h
new file mode 100644
index 0000000..7be33b0
--- /dev/null
+++ b/src/remotedesktop.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2018 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors:
+ * Jan Grulich
+ */
+
+#ifndef XDG_DESKTOP_PORTAL_KDE_REMOTEDESKTOP_H
+#define XDG_DESKTOP_PORTAL_KDE_REMOTEDESKTOP_H
+
+#include
+#include
+
+class Session;
+
+class RemoteDesktopPortal : public QDBusAbstractAdaptor
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.impl.portal.RemoteDesktop")
+public:
+ explicit RemoteDesktopPortal(QObject *parent);
+ ~RemoteDesktopPortal();
+
+ enum DeviceType {
+ None = 0x0,
+ Keyboard = 0x1,
+ Pointer = 0x2,
+ TouchScreen = 0x4,
+ All = (Keyboard | Pointer | TouchScreen)
+ };
+ Q_DECLARE_FLAGS(DeviceTypes, DeviceType)
+
+ uint version() const { return 1; }
+ uint AvailableDeviceTypes() const { return All; };
+
+public Q_SLOTS:
+ uint CreateSession(const QDBusObjectPath &handle,
+ const QDBusObjectPath &session_handle,
+ const QString &app_id,
+ const QVariantMap &options,
+ QVariantMap &results);
+
+ uint SelectDevices(const QDBusObjectPath &handle,
+ const QDBusObjectPath &session_handle,
+ const QString &app_id,
+ const QVariantMap &options,
+ QVariantMap &results);
+
+ uint Start(const QDBusObjectPath &handle,
+ const QDBusObjectPath &session_handle,
+ const QString &app_id,
+ const QString &parent_window,
+ const QVariantMap &options,
+ QVariantMap &results);
+
+ void NotifyPointerMotion(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ double dx,
+ double dy);
+
+ void NotifyPointerMotionAbsolute(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint stream,
+ double dx,
+ double dy);
+
+ void NotifyPointerButton(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ int button,
+ uint state);
+
+ void NotifyPointerAxis(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ double dx,
+ double dy);
+
+ void NotifyPointerAxisDiscrete(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint axis,
+ int steps);
+
+ void NotifyKeyboardKeycode(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ int keycode,
+ uint state);
+
+ void NotifyKeyboardKeysym(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ int keysym,
+ uint state);
+
+ void NotifyTouchDown(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint stream,
+ uint slot,
+ int x,
+ int y);
+
+ void NotifyTouchMotion(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint stream,
+ uint slot,
+ int x,
+ int y);
+
+ void NotifyTouchUp(const QDBusObjectPath &session_handle,
+ const QVariantMap &options,
+ uint slot);
+
+private:
+
+
+};
+
+#endif // XDG_DESKTOP_PORTAL_KDE_REMOTEDESKTOP_H
+
diff --git a/src/screencast.cpp b/src/screencast.cpp
index 0c92997..87a4aec 100644
--- a/src/screencast.cpp
+++ b/src/screencast.cpp
@@ -1,579 +1,571 @@
/*
* Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Jan Grulich
*/
#include "screencast.h"
#include "session.h"
#include "screencaststream.h"
#include "screenchooserdialog.h"
#include
#include
#include
#include
#include
#include
#include
#include
// KWayland
#include
#include
#include
#include
#include
// system
#include
#include
Q_LOGGING_CATEGORY(XdgDesktopPortalKdeScreenCast, "xdp-kde-screencast")
Q_DECLARE_METATYPE(ScreenCastPortal::Stream);
Q_DECLARE_METATYPE(ScreenCastPortal::Streams);
const QDBusArgument &operator >> (const QDBusArgument &arg, ScreenCastPortal::Stream &stream)
{
arg.beginStructure();
arg >> stream.nodeId;
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;
}
const QDBusArgument &operator << (QDBusArgument &arg, const ScreenCastPortal::Stream &stream)
{
arg.beginStructure();
arg << stream.nodeId;
arg << stream.map;
arg.endStructure();
return arg;
}
static const char * formatGLError(GLenum err)
{
switch(err) {
case GL_NO_ERROR:
return "GL_NO_ERROR";
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
default:
return (QLatin1String("0x") + QString::number(err, 16)).toLocal8Bit().constData();
}
}
// Thank you kscreen
void ScreenCastPortalOutput::setOutputType(const QString &type)
{
const auto embedded = { QLatin1String("LVDS"),
QLatin1String("IDP"),
QLatin1String("EDP"),
QLatin1String("LCD") };
for (const QLatin1String &pre : embedded) {
if (type.toUpper().startsWith(pre)) {
outputType = OutputType::Laptop;
return;
}
}
if (type.contains("VGA") || type.contains("DVI") || type.contains("HDMI") || type.contains("Panel") ||
type.contains("DisplayPort") || type.startsWith("DP") || type.contains("unknown")) {
outputType = OutputType::Monitor;
} else if (type.contains("TV")) {
outputType = OutputType::Television;
} else {
outputType = OutputType::Monitor;
}
}
ScreenCastPortal::ScreenCastPortal(QObject *parent)
: QDBusAbstractAdaptor(parent)
, m_registryInitialized(false)
, m_streamingEnabled(false)
, m_connection(nullptr)
, m_queue(nullptr)
, m_registry(nullptr)
, m_remoteAccessManager(nullptr)
{
initDrm();
initEGL();
initWayland();
qDBusRegisterMetaType();
qDBusRegisterMetaType();
}
ScreenCastPortal::~ScreenCastPortal()
{
if (m_remoteAccessManager) {
m_remoteAccessManager->destroy();
}
if (m_drmFd) {
gbm_device_destroy(m_gbmDevice);
}
m_stream->deleteLater();
}
void ScreenCastPortal::createPipeWireStream(const QSize &resolution)
{
m_stream = new ScreenCastStream(resolution);
m_stream->init();
connect(m_stream, &ScreenCastStream::streamReady, this, [] (uint nodeId) {
qCDebug(XdgDesktopPortalKdeScreenCast) << "Pipewire stream is ready: " << nodeId;
});
connect(m_stream, &ScreenCastStream::startStreaming, this, [this] {
qCDebug(XdgDesktopPortalKdeScreenCast) << "Start streaming";
m_streamingEnabled = true;
if (!m_registryInitialized) {
qCWarning(XdgDesktopPortalKdeScreenCast) << "Cannot start stream because registry is not initialized yet";
return;
}
if (m_registry->hasInterface(KWayland::Client::Registry::Interface::RemoteAccessManager)) {
KWayland::Client::Registry::AnnouncedInterface interface = m_registry->interface(KWayland::Client::Registry::Interface::RemoteAccessManager);
if (!interface.name && !interface.version) {
qCWarning(XdgDesktopPortalKdeScreenCast) << "Cannot start stream because remote access interface is not initialized yet";
return;
}
m_remoteAccessManager = m_registry->createRemoteAccessManager(interface.name, interface.version);
connect(m_remoteAccessManager, &KWayland::Client::RemoteAccessManager::bufferReady, this, [this] (const void *output, const KWayland::Client::RemoteBuffer * rbuf) {
Q_UNUSED(output);
connect(rbuf, &KWayland::Client::RemoteBuffer::parametersObtained, this, [this, rbuf] {
processBuffer(rbuf);
});
});
}
});
connect(m_stream, &ScreenCastStream::stopStreaming, this, &ScreenCastPortal::stopStreaming);
}
void ScreenCastPortal::initDrm()
{
m_drmFd = open("/dev/dri/renderD128", O_RDWR);
m_gbmDevice = gbm_create_device(m_drmFd);
if (!m_gbmDevice) {
qFatal("Cannot create GBM device: %s", strerror(errno));
}
}
void ScreenCastPortal::initEGL()
{
// Get the list of client extensions
const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString));
if (clientExtensionsString.isEmpty()) {
// If eglQueryString() returned NULL, the implementation doesn't support
// EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
qFatal("No client extensions defined! %s", formatGLError(eglGetError()));
}
m_egl.extensions = clientExtensionsString.split(' ');
// Use eglGetPlatformDisplayEXT() to get the display pointer
// if the implementation supports it.
if (!m_egl.extensions.contains(QByteArrayLiteral("EGL_EXT_platform_base")) ||
!m_egl.extensions.contains(QByteArrayLiteral("EGL_MESA_platform_gbm"))) {
qFatal("One of required EGL extensions is missing");
}
m_egl.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, m_gbmDevice, nullptr);
if (m_egl.display == EGL_NO_DISPLAY) {
qFatal("Error during obtaining EGL display: %s", formatGLError(eglGetError()));
}
EGLint major, minor;
if (eglInitialize(m_egl.display, &major, &minor) == EGL_FALSE) {
qFatal("Error during eglInitialize: %s", formatGLError(eglGetError()));
}
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
qFatal("bind OpenGL API failed");
}
m_egl.context = eglCreateContext(m_egl.display, nullptr, EGL_NO_CONTEXT, nullptr);
if (m_egl.context == EGL_NO_CONTEXT) {
qFatal("Couldn't create EGL context: %s", formatGLError(eglGetError()));
}
qCDebug(XdgDesktopPortalKdeScreenCast) << "Egl initialization succeeded";
qCDebug(XdgDesktopPortalKdeScreenCast) << QString("EGL version: %1.%2").arg(major).arg(minor);
}
void ScreenCastPortal::initWayland()
{
m_thread = new QThread(this);
m_connection = new KWayland::Client::ConnectionThread;
connect(m_connection, &KWayland::Client::ConnectionThread::connected, this, &ScreenCastPortal::setupRegistry, Qt::QueuedConnection);
connect(m_connection, &KWayland::Client::ConnectionThread::connectionDied, this, [this] {
if (m_queue) {
delete m_queue;
m_queue = nullptr;
}
m_connection->deleteLater();
m_connection = nullptr;
if (m_thread) {
m_thread->quit();
if (!m_thread->wait(3000)) {
m_thread->terminate();
m_thread->wait();
}
delete m_thread;
m_thread = nullptr;
}
});
connect(m_connection, &KWayland::Client::ConnectionThread::failed, this, [this] {
m_thread->quit();
m_thread->wait();
});
m_thread->start();
m_connection->moveToThread(m_thread);
m_connection->initConnection();
}
uint ScreenCastPortal::CreateSession(const QDBusObjectPath &handle,
const QDBusObjectPath &session_handle,
const QString &app_id,
const QVariantMap &options,
QVariantMap &results)
{
Q_UNUSED(results)
qCDebug(XdgDesktopPortalKdeScreenCast) << "CreateSession called with parameters:";
qCDebug(XdgDesktopPortalKdeScreenCast) << " handle: " << handle.path();
qCDebug(XdgDesktopPortalKdeScreenCast) << " session_handle: " << session_handle.path();
qCDebug(XdgDesktopPortalKdeScreenCast) << " app_id: " << app_id;
qCDebug(XdgDesktopPortalKdeScreenCast) << " options: " << options;
- QDBusConnection sessionBus = QDBusConnection::sessionBus();
- Session *session = new Session(this, app_id, session_handle.path());
- if (sessionBus.registerVirtualObject(session_handle.path(), session, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
- connect(session, &Session::closed, [this, session, session_handle] () {
- m_sessionList.remove(session_handle.path());
- QDBusConnection::sessionBus().unregisterObject(session_handle.path());
- session->deleteLater();
- stopStreaming();
- });
- m_sessionList.insert(session_handle.path(), session);
- return 0;
- } else {
- qCDebug(XdgDesktopPortalKdeScreenCast) << sessionBus.lastError().message();
- qCDebug(XdgDesktopPortalKdeScreenCast) << "Failed to register session object: " << session_handle.path();
- session->deleteLater();
+ Session *session = Session::createSession(this, Session::ScreenCast, app_id, session_handle.path());
+
+ if (!session) {
return 2;
}
+
+ connect(session, &Session::closed, [this] () {
+ stopStreaming();
+ });
+
+ return 0;
}
uint ScreenCastPortal::SelectSources(const QDBusObjectPath &handle,
const QDBusObjectPath &session_handle,
const QString &app_id,
const QVariantMap &options,
QVariantMap &results)
{
Q_UNUSED(results)
qCDebug(XdgDesktopPortalKdeScreenCast) << "SelectSource called with parameters:";
qCDebug(XdgDesktopPortalKdeScreenCast) << " handle: " << handle.path();
qCDebug(XdgDesktopPortalKdeScreenCast) << " session_handle: " << session_handle.path();
qCDebug(XdgDesktopPortalKdeScreenCast) << " app_id: " << app_id;
qCDebug(XdgDesktopPortalKdeScreenCast) << " options: " << options;
uint types = Monitor;
- Session *session = nullptr;
- session = m_sessionList.value(session_handle.path());
+ ScreenCastSession *session = qobject_cast(Session::getSession(session_handle.path()));
if (!session) {
qCWarning(XdgDesktopPortalKdeScreenCast) << "Tried to select sources on non-existing session " << session_handle.path();
return 2;
}
if (options.contains(QLatin1String("multiple"))) {
session->setMultipleSources(options.value(QLatin1String("multiple")).toBool());
}
if (options.contains(QLatin1String("types"))) {
types = (SourceType)(options.value(QLatin1String("types")).toUInt());
}
if (types == Window) {
qCWarning(XdgDesktopPortalKdeScreenCast) << "Screen cast of a window is not implemented";
return 2;
}
return 0;
}
uint ScreenCastPortal::Start(const QDBusObjectPath &handle,
const QDBusObjectPath &session_handle,
const QString &app_id,
const QString &parent_window,
const QVariantMap &options,
QVariantMap &results)
{
Q_UNUSED(results)
qCDebug(XdgDesktopPortalKdeScreenCast) << "Start called with parameters:";
qCDebug(XdgDesktopPortalKdeScreenCast) << " handle: " << handle.path();
qCDebug(XdgDesktopPortalKdeScreenCast) << " session_handle: " << session_handle.path();
qCDebug(XdgDesktopPortalKdeScreenCast) << " app_id: " << app_id;
qCDebug(XdgDesktopPortalKdeScreenCast) << " parent_window: " << parent_window;
qCDebug(XdgDesktopPortalKdeScreenCast) << " options: " << options;
- Session *session = nullptr;
- session = m_sessionList.value(session_handle.path());
+ ScreenCastSession *session = qobject_cast(Session::getSession(session_handle.path()));
if (!session) {
qCWarning(XdgDesktopPortalKdeScreenCast) << "Tried to select sources on non-existing session " << session_handle.path();
return 2;
}
// TODO check whether we got some outputs?
if (m_outputMap.isEmpty()) {
qCWarning(XdgDesktopPortalKdeScreenCast) << "Failed to show dialog as there is no screen to select";
return 2;
}
QScopedPointer screenDialog(new ScreenChooserDialog(m_outputMap, session->multipleSources()));
if (screenDialog->exec()) {
ScreenCastPortalOutput selectedOutput = m_outputMap.value(screenDialog->selectedScreens().first());
// Initialize PipeWire
createPipeWireStream(selectedOutput.resolution);
// HACK wait for stream to be ready
bool streamReady = false;
QEventLoop loop;
connect(m_stream, &ScreenCastStream::streamReady, this, [&loop, &streamReady] {
loop.quit();
streamReady = true;
});
QTimer::singleShot(3000, &loop, &QEventLoop::quit);
loop.exec();
disconnect(m_stream, &ScreenCastStream::streamReady, this, nullptr);
if (!streamReady) {
qCWarning(XdgDesktopPortalKdeScreenCast) << "Pipewire stream is not ready to be streamed";
return 2;
}
// TODO support multiple outputs
qCDebug(XdgDesktopPortalKdeScreenCast) << "Pipewire node id: " << m_stream->nodeId();
KWayland::Client::Output *output = new KWayland::Client::Output(this);
output->setup(m_registry->bindOutput(selectedOutput.waylandOutputName, selectedOutput.waylandOutputVersion));
m_bindOutputs << output;
Stream stream;
stream.nodeId = m_stream->nodeId();
stream.map = QVariantMap({{QLatin1String("size"), selectedOutput.resolution}});
results.insert(QLatin1String("streams"), QVariant::fromValue({stream}));
return 0;
}
return 0;
}
void ScreenCastPortal::addOutput(quint32 name, quint32 version)
{
KWayland::Client::Output *output = new KWayland::Client::Output(this);
output->setup(m_registry->bindOutput(name, version));
connect(output, &KWayland::Client::Output::changed, this, [this, name, version, output] () {
qCDebug(XdgDesktopPortalKdeScreenCast) << "Adding output:";
qCDebug(XdgDesktopPortalKdeScreenCast) << " manufacturer: " << output->manufacturer();
qCDebug(XdgDesktopPortalKdeScreenCast) << " model: " << output->model();
qCDebug(XdgDesktopPortalKdeScreenCast) << " resolution: " << output->pixelSize();
ScreenCastPortalOutput portalOutput;
portalOutput.manufacturer = output->manufacturer();
portalOutput.model = output->model();
portalOutput.resolution = output->pixelSize();
portalOutput.waylandOutputName = name;
portalOutput.waylandOutputVersion = version;
portalOutput.setOutputType(output->model());
m_outputMap.insert(name, portalOutput);
delete output;
});
}
void ScreenCastPortal::removeOutput(quint32 name)
{
ScreenCastPortalOutput output = m_outputMap.take(name);
qCDebug(XdgDesktopPortalKdeScreenCast) << "Removing output:";
qCDebug(XdgDesktopPortalKdeScreenCast) << " manufacturer: " << output.manufacturer;
qCDebug(XdgDesktopPortalKdeScreenCast) << " model: " << output.model;
}
void ScreenCastPortal::processBuffer(const KWayland::Client::RemoteBuffer* rbuf)
{
QScopedPointer guard(rbuf);
auto gbmHandle = rbuf->fd();
auto width = rbuf->width();
auto height = rbuf->height();
auto stride = rbuf->stride();
auto format = rbuf->format();
qCDebug(XdgDesktopPortalKdeScreenCast) << QString("Incoming GBM fd %1, %2x%3, stride %4, fourcc 0x%5").arg(gbmHandle).arg(width).arg(height).arg(stride).arg(QString::number(format, 16));
if (!m_streamingEnabled) {
qCDebug(XdgDesktopPortalKdeScreenCast) << "Streaming is disabled";
close(gbmHandle);
return;
}
if (!gbm_device_is_format_supported(m_gbmDevice, format, GBM_BO_USE_SCANOUT)) {
qCritical() << "GBM format is not supported by device!";
}
// import GBM buffer that was passed from KWin
gbm_import_fd_data importInfo = {gbmHandle, width, height, stride, format};
gbm_bo *imported = gbm_bo_import(m_gbmDevice, GBM_BO_IMPORT_FD, &importInfo, GBM_BO_USE_SCANOUT);
if (!imported) {
qCritical() << "Cannot import passed GBM fd:" << strerror(errno);
}
// bind context to render thread
eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context);
// create EGL image from imported BO
EGLImageKHR image = eglCreateImageKHR(m_egl.display, nullptr, EGL_NATIVE_PIXMAP_KHR, imported, nullptr);
if (image == EGL_NO_IMAGE_KHR) {
qCritical() << "Error creating EGLImageKHR" << formatGLError(glGetError());
return;
}
// create GL 2D texture for framebuffer
GLuint texture;
glGenTextures(1, &texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
// bind framebuffer to copy pixels from
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
qCritical() << "glCheckFramebufferStatus failed:" << formatGLError(glGetError());
glDeleteTextures(1, &texture);
glDeleteFramebuffers(1, &framebuffer);
eglDestroyImageKHR(m_egl.display, image);
return;
}
auto capture = new QImage(QSize(width, height), QImage::Format_RGBA8888);
glViewport(0, 0, width, height);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, capture->bits());
m_stream->recordFrame(capture->bits());
gbm_bo_destroy(imported);
glDeleteTextures(1, &texture);
glDeleteFramebuffers(1, &framebuffer);
eglDestroyImageKHR(m_egl.display, image);
delete capture;
close(gbmHandle);
}
void ScreenCastPortal::setupRegistry()
{
m_queue = new KWayland::Client::EventQueue(this);
m_queue->setup(m_connection);
m_registry = new KWayland::Client::Registry(this);
connect(m_registry, &KWayland::Client::Registry::outputAnnounced, this, &ScreenCastPortal::addOutput);
connect(m_registry, &KWayland::Client::Registry::outputRemoved, this, &ScreenCastPortal::removeOutput);
connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] {
m_registryInitialized = true;
qCDebug(XdgDesktopPortalKdeScreenCast) << "Registry initialized";
});
m_registry->create(m_connection);
m_registry->setEventQueue(m_queue);
m_registry->setup();
}
void ScreenCastPortal::stopStreaming()
{
if (m_streamingEnabled) {
qCDebug(XdgDesktopPortalKdeScreenCast) << "Stop streaming";
m_remoteAccessManager->release();
m_remoteAccessManager->destroy();
m_streamingEnabled = false;
qDeleteAll(m_bindOutputs);
m_bindOutputs.clear();
delete m_stream;
m_stream = nullptr;
}
}
diff --git a/src/screencast.h b/src/screencast.h
index 0865eff..3a19cf6 100644
--- a/src/screencast.h
+++ b/src/screencast.h
@@ -1,157 +1,155 @@
/*
* Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Jan Grulich
*/
#ifndef XDG_DESKTOP_PORTAL_KDE_SCREENCAST_H
#define XDG_DESKTOP_PORTAL_KDE_SCREENCAST_H
#include
#include
#include
#include
#include
#include
namespace KWayland {
namespace Client {
class ConnectionThread;
class EventQueue;
class OutputDevice;
class Registry;
class RemoteAccessManager;
class RemoteBuffer;
class Output;
}
}
-class Session;
class ScreenChooserDialog;
class ScreenCastStream;
class ScreenCastPortalOutput
{
enum OutputType {
Laptop,
Monitor,
Television
};
void setOutputType(const QString &type);
QString manufacturer;
QString model;
QSize resolution;
OutputType outputType;
// Needed for later output binding
int waylandOutputName;
int waylandOutputVersion;
friend class ScreenCastPortal;
friend class ScreenChooserDialog;
};
class ScreenCastPortal : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.freedesktop.impl.portal.ScreenCast")
Q_PROPERTY(uint version READ version)
Q_PROPERTY(uint AvailableSourceTypes READ AvailableSourceTypes)
public:
typedef struct {
uint nodeId;
QVariantMap map;
} Stream;
typedef QList Streams;
enum SourceType {
Any = 0,
Monitor,
Window
};
explicit ScreenCastPortal(QObject *parent);
~ScreenCastPortal();
uint version() const { return 1; }
uint AvailableSourceTypes() const { return Monitor; };
public Q_SLOTS:
uint CreateSession(const QDBusObjectPath &handle,
const QDBusObjectPath &session_handle,
const QString &app_id,
const QVariantMap &options,
QVariantMap &results);
uint SelectSources(const QDBusObjectPath &handle,
const QDBusObjectPath &session_handle,
const QString &app_id,
const QVariantMap &options,
QVariantMap &results);
uint Start(const QDBusObjectPath &handle,
const QDBusObjectPath &session_handle,
const QString &app_id,
const QString &parent_window,
const QVariantMap &options,
QVariantMap &results);
private Q_SLOTS:
void addOutput(quint32 name, quint32 version);
void removeOutput(quint32 name);
void processBuffer(const KWayland::Client::RemoteBuffer *rbuf);
void setupRegistry();
void stopStreaming();
private:
void createPipeWireStream(const QSize &resolution);
void initDrm();
void initEGL();
void initWayland();
bool m_registryInitialized;
bool m_streamingEnabled;
- QMap m_sessionList;
QMap m_outputMap;
QList m_bindOutputs;
QThread *m_thread;
ScreenCastStream *m_stream;
KWayland::Client::ConnectionThread *m_connection;
KWayland::Client::EventQueue *m_queue;
KWayland::Client::Registry *m_registry;
KWayland::Client::RemoteAccessManager *m_remoteAccessManager;
qint32 m_drmFd = 0; // for GBM buffer mmap
gbm_device *m_gbmDevice = nullptr; // for passed GBM buffer retrieval
struct {
QList extensions;
EGLDisplay display = EGL_NO_DISPLAY;
EGLContext context = EGL_NO_CONTEXT;
} m_egl;
};
#endif // XDG_DESKTOP_PORTAL_KDE_SCREENCAST_H
diff --git a/src/session.cpp b/src/session.cpp
index e790e88..7128cbb 100644
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -1,126 +1,186 @@
/*
* Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Jan Grulich
*/
#include "session.h"
#include "desktopportal.h"
#include
#include
#include
#include
#include
#include
#include
Q_LOGGING_CATEGORY(XdgSessionKdeSession, "xdp-kde-session")
+static QMap sessionList;
+
Session::Session(QObject *parent, const QString &appId, const QString &path)
: QDBusVirtualObject(parent)
- , m_multipleSources(false)
, m_appId(appId)
, m_path(path)
{
}
Session::~Session()
{
}
bool Session::handleMessage(const QDBusMessage &message, const QDBusConnection &connection)
{
Q_UNUSED(connection);
if (message.path() != m_path) {
return false;
}
/* Check to make sure we're getting properties on our interface */
if (message.type() != QDBusMessage::MessageType::MethodCallMessage) {
return false;
}
qCDebug(XdgSessionKdeSession) << message.interface();
qCDebug(XdgSessionKdeSession) << message.member();
qCDebug(XdgSessionKdeSession) << message.path();
if (message.interface() == QLatin1String("org.freedesktop.impl.portal.Session")) {
if (message.member() == QLatin1String("Close")) {
Q_EMIT closed();
QDBusMessage reply = message.createReply();
return connection.send(reply);
}
} else if (message.interface() == QLatin1String("org.freedesktop.DBus.Properties")) {
if (message.member() == QLatin1String("Get")) {
if (message.arguments().count() == 2) {
const QString interface = message.arguments().at(0).toString();
const QString property = message.arguments().at(1).toString();
if (interface == QLatin1String("org.freedesktop.impl.portal.Session") &&
property == QLatin1String("version")) {
QList arguments;
arguments << 1;
QDBusMessage reply = message.createReply();
reply.setArguments(arguments);
return connection.send(reply);
}
}
}
}
return false;
}
QString Session::introspect(const QString &path) const
{
QString nodes;
if (path.startsWith(QLatin1String("/org/freedesktop/portal/desktop/session/"))) {
nodes = QStringLiteral(
""
" "
" "
""
""
""
"");
}
qCDebug(XdgSessionKdeSession) << nodes;
return nodes;
}
-bool Session::multipleSources() const
+bool Session::close()
+{
+ QDBusMessage reply = QDBusMessage::createSignal(m_path, QLatin1String("org.freedesktop.impl.portal.Session"), QLatin1String("Closed"));
+ return QDBusConnection::sessionBus().send(reply);
+}
+
+Session * Session::createSession(QObject *parent, SessionType type, const QString &appId, const QString &path)
+{
+ QDBusConnection sessionBus = QDBusConnection::sessionBus();
+
+ Session *session = nullptr;
+ if (type == ScreenCast) {
+ session = new ScreenCastSession(parent, appId, path);
+ } else {
+ session = new RemoteDesktopSession(parent, appId, path);
+ }
+
+ if (sessionBus.registerVirtualObject(path, session, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
+ connect(session, &Session::closed, [session, path] () {
+ sessionList.remove(path);
+ QDBusConnection::sessionBus().unregisterObject(path);
+ session->deleteLater();
+ });
+ sessionList.insert(path, session);
+ return session;
+ } else {
+ qCDebug(XdgSessionKdeSession) << sessionBus.lastError().message();
+ qCDebug(XdgSessionKdeSession) << "Failed to register session object: " << path;
+ session->deleteLater();
+ return nullptr;
+ }
+}
+
+Session * Session::getSession(const QString &sessionHandle)
+{
+ return sessionList.value(sessionHandle);
+}
+
+ScreenCastSession::ScreenCastSession(QObject *parent, const QString &appId, const QString &path)
+ : Session(parent, appId, path)
+{
+}
+
+ScreenCastSession::~ScreenCastSession()
+{
+}
+
+bool ScreenCastSession::multipleSources() const
{
return m_multipleSources;
}
-void Session::setMultipleSources(bool multipleSources)
+void ScreenCastSession::setMultipleSources(bool multipleSources)
{
m_multipleSources = multipleSources;
}
-bool Session::close()
+RemoteDesktopSession::RemoteDesktopSession(QObject *parent, const QString &appId, const QString &path)
+ : ScreenCastSession(parent, appId, path)
{
- QDBusMessage reply = QDBusMessage::createSignal(m_path, QLatin1String("org.freedesktop.impl.portal.Session"), QLatin1String("Closed"));
- return QDBusConnection::sessionBus().send(reply);
}
+RemoteDesktopSession::~RemoteDesktopSession()
+{
+}
+
+RemoteDesktopPortal::DeviceTypes RemoteDesktopSession::deviceTypes() const
+{
+ return m_deviceTypes;
+}
+
+void RemoteDesktopSession::setDeviceTypes(RemoteDesktopPortal::DeviceTypes deviceTypes)
+{
+ m_deviceTypes = deviceTypes;
+}
diff --git a/src/session.h b/src/session.h
index 2e1fa04..39414ac 100644
--- a/src/session.h
+++ b/src/session.h
@@ -1,52 +1,86 @@
/*
* Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Jan Grulich
*/
#ifndef XDG_DESKTOP_PORTAL_KDE_SESSION_H
#define XDG_DESKTOP_PORTAL_KDE_SESSION_H
#include
#include
+#include "remotedesktop.h"
+
class Session : public QDBusVirtualObject
{
Q_OBJECT
public:
explicit Session(QObject *parent = nullptr, const QString &appId = QString(), const QString &path = QString());
~Session();
+ enum SessionType {
+ ScreenCast = 0,
+ RemoteDesktop = 1
+ };
+
bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) override;
QString introspect(const QString &path) const override;
bool close();
- bool multipleSources() const;
- void setMultipleSources(bool multipleSources);
+ static Session *createSession(QObject *parent, SessionType type, const QString &appId, const QString &path);
+ static Session *getSession(const QString &sessionHandle);
Q_SIGNALS:
void closed();
private:
- bool m_multipleSources;
const QString m_appId;
const QString m_path;
};
+class ScreenCastSession : public Session
+{
+ Q_OBJECT
+public:
+ explicit ScreenCastSession(QObject *parent = nullptr, const QString &appId = QString(), const QString &path = QString());
+ ~ScreenCastSession();
+
+ bool multipleSources() const;
+ void setMultipleSources(bool multipleSources);
+private:
+ bool m_multipleSources;
+ // TODO type
+};
+
+class RemoteDesktopSession : public ScreenCastSession
+{
+ Q_OBJECT
+public:
+ explicit RemoteDesktopSession(QObject *parent = nullptr, const QString &appId = QString(), const QString &path = QString());
+ ~RemoteDesktopSession();
+
+ RemoteDesktopPortal::DeviceTypes deviceTypes() const;
+ void setDeviceTypes(RemoteDesktopPortal::DeviceTypes deviceTypes);
+
+private:
+ RemoteDesktopPortal::DeviceTypes m_deviceTypes;
+};
+
#endif // XDG_DESKTOP_PORTAL_KDE_SESSION_H