diff --git a/CMakeLists.txt b/CMakeLists.txt
index 20ea1e3..5bed1e3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,86 +1,88 @@
cmake_minimum_required(VERSION 3.0)
project(xdg-desktop-portal-kde)
set(PROJECT_VERSION "5.18.80")
set(PROJECT_VERSION_MAJOR 5)
set(QT_MIN_VERSION "5.14.0")
set(KF5_MIN_VERSION "5.66.0")
################# set KDE specific information #################
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
include(KDEClangFormat)
include(FeatureSummary)
-find_package(PipeWire)
-set_package_properties(PipeWire PROPERTIES
- TYPE OPTIONAL
- PURPOSE "Required for screencast portal"
-)
-
-find_package(GBM)
-set_package_properties(GBM PROPERTIES
- TYPE OPTIONAL
- PURPOSE "Required for screencast portal"
-)
-
-find_package(Epoxy)
-set_package_properties(Epoxy PROPERTIES DESCRIPTION "libepoxy"
- URL "https://github.com/anholt/libepoxy"
- TYPE OPTIONAL
- PURPOSE "Required for screencast portal"
-)
-
-if (PipeWire_FOUND AND GBM_FOUND AND Epoxy_FOUND)
- set (SCREENCAST_ENABLED true)
+option(ENABLE_PIPEWIRE "Disable PipeWire support. PipeWire is needed for screen sharing and remote desktop" ON)
+if(ENABLE_PIPEWIRE)
+ set(HAVE_PIPEWIRE_SUPPORT 1)
else()
- set (SCREENCAST_ENABLED false)
+ message(STATUS "Disabling PipeWire support")
+ set(HAVE_PIPEWIRE_SUPPORT 0)
+endif()
+add_definitions(-DHAVE_PIPEWIRE_SUPPORT=${HAVE_PIPEWIRE_SUPPORT})
+
+if(HAVE_PIPEWIRE_SUPPORT)
+ find_package(PipeWire)
+ set_package_properties(PipeWire PROPERTIES
+ TYPE REQUIRED
+ PURPOSE "Required for screencast portal"
+ )
+
+ find_package(GBM)
+ set_package_properties(GBM PROPERTIES
+ TYPE REQUIRED
+ PURPOSE "Required for screencast portal"
+ )
+
+ find_package(Epoxy)
+ set_package_properties(Epoxy PROPERTIES DESCRIPTION "libepoxy"
+ URL "https://github.com/anholt/libepoxy"
+ TYPE REQUIRED
+ PURPOSE "Required for screencast portal"
+ )
endif()
-add_definitions(-DSCREENCAST_ENABLED=${SCREENCAST_ENABLED})
-
-add_feature_info ("Screencast portal" ${SCREENCAST_ENABLED} "Support for screen sharing")
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Core
Concurrent
DBus
PrintSupport
QuickWidgets
Widgets
)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED
CoreAddons
Config
I18n
Declarative
KIO
Kirigami2
Notifications
Plasma
Wayland
WidgetsAddons
WindowSystem
)
if (EXISTS "${CMAKE_SOURCE_DIR}/.git")
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000)
add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054200)
endif()
add_subdirectory(data)
add_subdirectory(src)
# add clang-format target for all our real source files
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
index cc80053..f7ef514 100644
--- a/data/CMakeLists.txt
+++ b/data/CMakeLists.txt
@@ -1,6 +1,10 @@
configure_file(org.freedesktop.impl.portal.desktop.kde.desktop.in org.freedesktop.impl.portal.desktop.kde.desktop @ONLY)
configure_file(org.freedesktop.impl.portal.desktop.kde.cmake.in org.freedesktop.impl.portal.desktop.kde.service @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.impl.portal.desktop.kde.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR})
-install(FILES kde.portal DESTINATION ${DATA_INSTALL_DIR}/xdg-desktop-portal/portals)
+if(HAVE_PIPEWIRE_SUPPORT)
+ install(FILES kde.portal DESTINATION ${DATA_INSTALL_DIR}/xdg-desktop-portal/portals)
+else()
+ install(FILES kde-no-pipewire.portal DESTINATION ${DATA_INSTALL_DIR}/xdg-desktop-portal/portals RENAME kde.portal)
+endif()
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.impl.portal.desktop.kde.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/data/kde-no-pipewire.portal b/data/kde-no-pipewire.portal
new file mode 100644
index 0000000..69a4db6
--- /dev/null
+++ b/data/kde-no-pipewire.portal
@@ -0,0 +1,4 @@
+[portal]
+DBusName=org.freedesktop.impl.portal.desktop.kde
+Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.Account;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Background;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.Screenshot;org.freedesktop.impl.portal.Settings
+UseIn=KDE
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cca3404..e59330d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,91 +1,91 @@
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
account.cpp
appchooser.cpp
appchooserdialog.cpp
background.cpp
desktopportal.cpp
email.cpp
filechooser.cpp
inhibit.cpp
notification.cpp
print.cpp
request.cpp
session.cpp
screenshot.cpp
screenshotdialog.cpp
settings.cpp
utils.cpp
userinfodialog.cpp
+ waylandintegration.cpp
)
-if (SCREENCAST_ENABLED)
+if(HAVE_PIPEWIRE_SUPPORT)
set (xdg_desktop_portal_kde_SRCS
${xdg_desktop_portal_kde_SRCS}
screencast.cpp
screencaststream.cpp
screencastwidget.cpp
screenchooserdialog.cpp
remotedesktop.cpp
- remotedesktopdialog.cpp
- waylandintegration.cpp)
+ remotedesktopdialog.cpp)
ki18n_wrap_ui(xdg_desktop_portal_kde_SRCS
screenchooserdialog.ui
remotedesktopdialog.ui)
endif()
ki18n_wrap_ui(xdg_desktop_portal_kde_SRCS
accessdialog.ui
appchooserdialog.ui
screenshotdialog.ui
userinfodialog.ui
)
set_source_files_properties(../data/org.freedesktop.Accounts.User.xml PROPERTIES NO_NAMESPACE TRUE)
qt5_add_dbus_interface(xdg_desktop_portal_kde_SRCS ../data/org.freedesktop.Accounts.User.xml user_interface)
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::QuickWidgets
Qt5::Widgets
KF5::CoreAddons
KF5::ConfigCore
KF5::Declarative
KF5::I18n
KF5::KIOFileWidgets
KF5::Notifications
KF5::WaylandClient
KF5::WidgetsAddons
KF5::WindowSystem
)
-if (SCREENCAST_ENABLED)
+if (HAVE_PIPEWIRE_SUPPORT)
target_link_libraries(xdg-desktop-portal-kde
PipeWire::PipeWire
${Epoxy_LIBRARIES}
GBM::GBM)
endif()
install(TARGETS xdg-desktop-portal-kde DESTINATION ${KDE_INSTALL_LIBEXECDIR})
install(FILES
qml/AppChooserDialog.qml
qml/UserInfoDialog.qml
DESTINATION ${KDE_INSTALL_DATADIR}/xdg-desktop-portal-kde/qml)
install(FILES
xdg-desktop-portal-kde.notifyrc
DESTINATION ${KNOTIFYRC_INSTALL_DIR})
\ No newline at end of file
diff --git a/src/desktopportal.cpp b/src/desktopportal.cpp
index 2bd751e..e8ec94d 100644
--- a/src/desktopportal.cpp
+++ b/src/desktopportal.cpp
@@ -1,53 +1,53 @@
/*
* 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
Q_LOGGING_CATEGORY(XdgDesktopPortalKdeDesktopPortal, "xdp-kde-desktop-portal")
DesktopPortal::DesktopPortal(QObject *parent)
: QObject(parent)
, m_access(new AccessPortal(this))
, m_account(new AccountPortal(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))
, m_settings(new SettingsPortal(this))
{
const QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP").toUpper();
if (xdgCurrentDesktop == "KDE") {
m_background = new BackgroundPortal(this);
- m_screenshot = new ScreenshotPortal(this);
-#if SCREENCAST_ENABLED
+#if HAVE_PIPEWIRE_SUPPORT
m_screenCast = new ScreenCastPortal(this);
m_remoteDesktop = new RemoteDesktopPortal(this);
#endif
+ m_screenshot = new ScreenshotPortal(this);
WaylandIntegration::init();
}
}
DesktopPortal::~DesktopPortal()
{
}
diff --git a/src/desktopportal.h b/src/desktopportal.h
index 49da246..b832558 100644
--- a/src/desktopportal.h
+++ b/src/desktopportal.h
@@ -1,70 +1,71 @@
/*
* 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 "account.h"
#include "appchooser.h"
#include "background.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"
#include "settings.h"
#include "waylandintegration.h"
+#if HAVE_PIPEWIRE_SUPPORT
+#include "screencast.h"
+#include "remotedesktop.h"
+#endif
+
class DesktopPortal : public QObject, public QDBusContext
{
Q_OBJECT
public:
explicit DesktopPortal(QObject *parent = nullptr);
~DesktopPortal();
private:
AccessPortal *m_access;
AccountPortal *m_account;
AppChooserPortal *m_appChooser;
BackgroundPortal *m_background;
EmailPortal *m_email;
FileChooserPortal *m_fileChooser;
InhibitPortal *m_inhibit;
NotificationPortal *m_notification;
PrintPortal *m_print;
-#if SCREENCAST_ENABLED
+ ScreenshotPortal *m_screenshot;
+ SettingsPortal *m_settings;
+#if HAVE_PIPEWIRE_SUPPORT
ScreenCastPortal *m_screenCast;
RemoteDesktopPortal *m_remoteDesktop;
#endif
- ScreenshotPortal *m_screenshot;
- SettingsPortal *m_settings;
};
#endif // XDG_DESKTOP_PORTAL_KDE_DESKTOP_PORTAL_H
diff --git a/src/waylandintegration.cpp b/src/waylandintegration.cpp
index 928bdd2..e06f83b 100644
--- a/src/waylandintegration.cpp
+++ b/src/waylandintegration.cpp
@@ -1,652 +1,669 @@
/*
* 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 "waylandintegration.h"
#include "waylandintegration_p.h"
-#include "screencaststream.h"
+
#include
#include
#include
#include
#include
#include
#include
#include
// KWayland
#include
#include
-#include
#include
-#include
-#include
#include
// system
#include
#include
+#if HAVE_PIPEWIRE_SUPPORT
+#include "screencaststream.h"
+
+#include
+#include
+#include
+#endif
+
Q_LOGGING_CATEGORY(XdgDesktopPortalKdeWaylandIntegration, "xdp-kde-wayland-integration")
Q_GLOBAL_STATIC(WaylandIntegration::WaylandIntegrationPrivate, globalWaylandIntegration)
-void WaylandIntegration::authenticate()
-{
- globalWaylandIntegration->authenticate();
-}
-
void WaylandIntegration::init()
{
-#if SCREENCAST_ENABLED
+#if HAVE_PIPEWIRE_SUPPORT
globalWaylandIntegration->initDrm();
#endif
globalWaylandIntegration->initWayland();
}
+#if HAVE_PIPEWIRE_SUPPORT
+void WaylandIntegration::authenticate()
+{
+ globalWaylandIntegration->authenticate();
+}
+
bool WaylandIntegration::isEGLInitialized()
{
return globalWaylandIntegration->isEGLInitialized();
}
bool WaylandIntegration::isStreamingEnabled()
{
return globalWaylandIntegration->isStreamingEnabled();
}
void WaylandIntegration::startStreamingInput()
{
globalWaylandIntegration->startStreamingInput();
}
bool WaylandIntegration::startStreaming(quint32 outputName)
{
return globalWaylandIntegration->startStreaming(outputName);
}
void WaylandIntegration::stopStreaming()
{
globalWaylandIntegration->stopStreaming();
}
void WaylandIntegration::requestPointerButtonPress(quint32 linuxButton)
{
globalWaylandIntegration->requestPointerButtonPress(linuxButton);
}
void WaylandIntegration::requestPointerButtonRelease(quint32 linuxButton)
{
globalWaylandIntegration->requestPointerButtonRelease(linuxButton);
}
void WaylandIntegration::requestPointerMotion(const QSizeF &delta)
{
globalWaylandIntegration->requestPointerMotion(delta);
}
void WaylandIntegration::requestPointerMotionAbsolute(const QPointF &pos)
{
globalWaylandIntegration->requestPointerMotionAbsolute(pos);
}
void WaylandIntegration::requestPointerAxisDiscrete(Qt::Orientation axis, qreal delta)
{
globalWaylandIntegration->requestPointerAxisDiscrete(axis, delta);
}
void WaylandIntegration::requestKeyboardKeycode(int keycode, bool state)
{
globalWaylandIntegration->requestKeyboardKeycode(keycode, state);
}
WaylandIntegration::EGLStruct WaylandIntegration::egl()
{
return globalWaylandIntegration->egl();
}
-KWayland::Client::PlasmaWindowManagement * WaylandIntegration::plasmaWindowManagement()
-{
- return globalWaylandIntegration->plasmaWindowManagement();
-}
-
QMap WaylandIntegration::screens()
{
return globalWaylandIntegration->screens();
}
QVariant WaylandIntegration::streams()
{
return globalWaylandIntegration->streams();
}
-WaylandIntegration::WaylandIntegration * WaylandIntegration::waylandIntegration()
-{
- return globalWaylandIntegration;
-}
-
const char * WaylandIntegration::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 WaylandIntegration::WaylandOutput::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)) {
m_outputType = OutputType::Laptop;
return;
}
}
if (type.contains(QLatin1String("VGA")) || type.contains(QLatin1String("DVI")) || type.contains(QLatin1String("HDMI")) || type.contains(QLatin1String("Panel")) ||
type.contains(QLatin1String("DisplayPort")) || type.startsWith(QLatin1String("DP")) || type.contains(QLatin1String("unknown"))) {
m_outputType = OutputType::Monitor;
} else if (type.contains(QLatin1String("TV"))) {
m_outputType = OutputType::Television;
} else {
m_outputType = OutputType::Monitor;
}
}
const QDBusArgument &operator >> (const QDBusArgument &arg, WaylandIntegration::WaylandIntegrationPrivate::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 WaylandIntegration::WaylandIntegrationPrivate::Stream &stream)
{
arg.beginStructure();
arg << stream.nodeId;
arg << stream.map;
arg.endStructure();
return arg;
}
Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Stream)
Q_DECLARE_METATYPE(WaylandIntegration::WaylandIntegrationPrivate::Streams)
+#endif
+
+KWayland::Client::PlasmaWindowManagement * WaylandIntegration::plasmaWindowManagement()
+{
+ return globalWaylandIntegration->plasmaWindowManagement();
+}
+
+WaylandIntegration::WaylandIntegration * WaylandIntegration::waylandIntegration()
+{
+ return globalWaylandIntegration;
+}
WaylandIntegration::WaylandIntegrationPrivate::WaylandIntegrationPrivate()
: WaylandIntegration()
- , m_eglInitialized(false)
, m_registryInitialized(false)
- , m_waylandAuthenticationRequested(false)
, m_connection(nullptr)
, m_queue(nullptr)
- , m_fakeInput(nullptr)
, m_registry(nullptr)
+#if HAVE_PIPEWIRE_SUPPORT
+ , m_fakeInput(nullptr)
, m_remoteAccessManager(nullptr)
+#endif
{
+#if HAVE_PIPEWIRE_SUPPORT
qDBusRegisterMetaType();
qDBusRegisterMetaType();
+#endif
}
WaylandIntegration::WaylandIntegrationPrivate::~WaylandIntegrationPrivate()
{
+#if HAVE_PIPEWIRE_SUPPORT
if (m_remoteAccessManager) {
m_remoteAccessManager->destroy();
}
if (m_gbmDevice) {
gbm_device_destroy(m_gbmDevice);
}
+#endif
}
+#if HAVE_PIPEWIRE_SUPPORT
bool WaylandIntegration::WaylandIntegrationPrivate::isEGLInitialized() const
{
return m_eglInitialized;
}
bool WaylandIntegration::WaylandIntegrationPrivate::isStreamingEnabled() const
{
return m_streamingEnabled;
}
void WaylandIntegration::WaylandIntegrationPrivate::bindOutput(int outputName, int outputVersion)
{
KWayland::Client::Output *output = new KWayland::Client::Output(this);
output->setup(m_registry->bindOutput(outputName, outputVersion));
m_bindOutputs << output;
}
void WaylandIntegration::WaylandIntegrationPrivate::startStreamingInput()
{
m_streamInput = true;
}
bool WaylandIntegration::WaylandIntegrationPrivate::startStreaming(quint32 outputName)
{
WaylandOutput output = m_outputMap.value(outputName);
m_streamedScreenPosition = output.globalPosition();
m_stream = new ScreenCastStream(output.resolution());
m_stream->init();
connect(m_stream, &ScreenCastStream::startStreaming, this, [this, output] {
m_streamingEnabled = true;
startStreamingInput();
bindOutput(output.waylandOutputName(), output.waylandOutputVersion());
});
connect(m_stream, &ScreenCastStream::stopStreaming, this, &WaylandIntegrationPrivate::stopStreaming);
bool streamReady = false;
QEventLoop loop;
connect(m_stream, &ScreenCastStream::streamReady, this, [&loop, &streamReady] {
loop.quit();
streamReady = true;
});
// HACK wait for stream to be ready
QTimer::singleShot(3000, &loop, &QEventLoop::quit);
loop.exec();
disconnect(m_stream, &ScreenCastStream::streamReady, this, nullptr);
if (!streamReady) {
if (m_stream) {
delete m_stream;
m_stream = nullptr;
}
return false;
}
// TODO support multiple outputs
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(XdgDesktopPortalKdeWaylandIntegration) << "Failed to start streaming: remote access manager interface is not initialized yet";
return false;
}
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);
});
});
m_output = output.waylandOutputName();
return true;
}
if (m_stream) {
delete m_stream;
m_stream = nullptr;
}
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to start streaming: no remote access manager interface";
return false;
}
void WaylandIntegration::WaylandIntegrationPrivate::stopStreaming()
{
m_streamInput = false;
if (m_streamingEnabled) {
m_streamingEnabled = false;
// First unbound outputs and destroy remote access manager so we no longer receive buffers
if (m_remoteAccessManager) {
m_remoteAccessManager->release();
m_remoteAccessManager->destroy();
}
qDeleteAll(m_bindOutputs);
m_bindOutputs.clear();
if (m_stream) {
delete m_stream;
m_stream = nullptr;
}
}
}
void WaylandIntegration::WaylandIntegrationPrivate::requestPointerButtonPress(quint32 linuxButton)
{
if (m_streamInput && m_fakeInput) {
m_fakeInput->requestPointerButtonPress(linuxButton);
}
}
void WaylandIntegration::WaylandIntegrationPrivate::requestPointerButtonRelease(quint32 linuxButton)
{
if (m_streamInput && m_fakeInput) {
m_fakeInput->requestPointerButtonRelease(linuxButton);
}
}
void WaylandIntegration::WaylandIntegrationPrivate::requestPointerMotion(const QSizeF &delta)
{
if (m_streamInput && m_fakeInput) {
m_fakeInput->requestPointerMove(delta);
}
}
void WaylandIntegration::WaylandIntegrationPrivate::requestPointerMotionAbsolute(const QPointF &pos)
{
if (m_streamInput && m_fakeInput) {
m_fakeInput->requestPointerMoveAbsolute(pos + m_streamedScreenPosition);
}
}
void WaylandIntegration::WaylandIntegrationPrivate::requestPointerAxisDiscrete(Qt::Orientation axis, qreal delta)
{
if (m_streamInput && m_fakeInput) {
m_fakeInput->requestPointerAxis(axis, delta);
}
}
void WaylandIntegration::WaylandIntegrationPrivate::requestKeyboardKeycode(int keycode, bool state)
{
if (m_streamInput && m_fakeInput) {
if (state) {
m_fakeInput->requestKeyboardKeyPress(keycode);
} else {
m_fakeInput->requestKeyboardKeyRelease(keycode);
}
}
}
WaylandIntegration::EGLStruct WaylandIntegration::WaylandIntegrationPrivate::egl()
{
return m_egl;
}
QMap WaylandIntegration::WaylandIntegrationPrivate::screens()
{
return m_outputMap;
}
-KWayland::Client::PlasmaWindowManagement * WaylandIntegration::WaylandIntegrationPrivate::plasmaWindowManagement()
-{
- return m_windowManagement;
-}
-
QVariant WaylandIntegration::WaylandIntegrationPrivate::streams()
{
Stream stream;
stream.nodeId = m_stream->nodeId();
stream.map = QVariantMap({{QLatin1String("size"), m_outputMap.value(m_output).resolution()}});
return QVariant::fromValue({stream});
}
-void WaylandIntegration::WaylandIntegrationPrivate::authenticate()
-{
- if (!m_waylandAuthenticationRequested) {
- m_fakeInput->authenticate(i18n("xdg-desktop-portals-kde"), i18n("Remote desktop"));
- m_waylandAuthenticationRequested = true;
- }
-}
-
void WaylandIntegration::WaylandIntegrationPrivate::initDrm()
{
m_drmFd = open("/dev/dri/renderD128", O_RDWR);
if (m_drmFd == -1) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Cannot open render node: " << strerror(errno);
return;
}
m_gbmDevice = gbm_create_device(m_drmFd);
if (!m_gbmDevice) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Cannot create GBM device: " << strerror(errno);
}
initEGL();
}
void WaylandIntegration::WaylandIntegrationPrivate::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.
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "No client extensions defined! " << formatGLError(eglGetError());
return;
}
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"))) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "One of required EGL extensions is missing";
return;
}
m_egl.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, m_gbmDevice, nullptr);
if (m_egl.display == EGL_NO_DISPLAY) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Error during obtaining EGL display: " << formatGLError(eglGetError());
return;
}
EGLint major, minor;
if (eglInitialize(m_egl.display, &major, &minor) == EGL_FALSE) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Error during eglInitialize: " << formatGLError(eglGetError());
return;
}
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "bind OpenGL API failed";
return;
}
m_egl.context = eglCreateContext(m_egl.display, nullptr, EGL_NO_CONTEXT, nullptr);
if (m_egl.context == EGL_NO_CONTEXT) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Couldn't create EGL context: " << formatGLError(eglGetError());
return;
}
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Egl initialization succeeded";
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << QStringLiteral("EGL version: %1.%2").arg(major).arg(minor);
m_eglInitialized = true;
}
+void WaylandIntegration::WaylandIntegrationPrivate::authenticate()
+{
+ if (!m_waylandAuthenticationRequested) {
+ m_fakeInput->authenticate(i18n("xdg-desktop-portals-kde"), i18n("Remote desktop"));
+ m_waylandAuthenticationRequested = true;
+ }
+}
+
+#endif
+
+KWayland::Client::PlasmaWindowManagement * WaylandIntegration::WaylandIntegrationPrivate::plasmaWindowManagement()
+{
+ return m_windowManagement;
+}
+
void WaylandIntegration::WaylandIntegrationPrivate::initWayland()
{
m_thread = new QThread(this);
m_connection = new KWayland::Client::ConnectionThread;
connect(m_connection, &KWayland::Client::ConnectionThread::connected, this, &WaylandIntegrationPrivate::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();
}
+#if HAVE_PIPEWIRE_SUPPORT
void WaylandIntegration::WaylandIntegrationPrivate::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(XdgDesktopPortalKdeWaylandIntegration) << "Adding output:";
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output->manufacturer();
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output->model();
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " resolution: " << output->pixelSize();
WaylandOutput portalOutput;
portalOutput.setManufacturer(output->manufacturer());
portalOutput.setModel(output->model());
portalOutput.setOutputType(output->model());
portalOutput.setGlobalPosition(output->globalPosition());
portalOutput.setResolution(output->pixelSize());
portalOutput.setWaylandOutputName(name);
portalOutput.setWaylandOutputVersion(version);
m_outputMap.insert(name, portalOutput);
delete output;
});
}
void WaylandIntegration::WaylandIntegrationPrivate::removeOutput(quint32 name)
{
WaylandOutput output = m_outputMap.take(name);
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Removing output:";
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " manufacturer: " << output.manufacturer();
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << " model: " << output.model();
}
void WaylandIntegration::WaylandIntegrationPrivate::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(XdgDesktopPortalKdeWaylandIntegration) << QStringLiteral("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(XdgDesktopPortalKdeWaylandIntegration) << "Streaming is disabled";
close(gbmHandle);
return;
}
if (m_lastFrameTime.isValid() &&
m_lastFrameTime.msecsTo(QDateTime::currentDateTime()) < (1000 / m_stream->framerate())) {
close(gbmHandle);
return;
}
if (!gbm_device_is_format_supported(m_gbmDevice, format, GBM_BO_USE_SCANOUT)) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to process buffer: GBM format is not supported by device!";
close(gbmHandle);
return;
}
// 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) {
qCWarning(XdgDesktopPortalKdeWaylandIntegration) << "Failed to process buffer: Cannot import passed GBM fd - " << strerror(errno);
close(gbmHandle);
return;
}
if (m_stream->recordFrame(imported, width, height, stride)) {
m_lastFrameTime = QDateTime::currentDateTime();
}
gbm_bo_destroy(imported);
close(gbmHandle);
}
+#endif
void WaylandIntegration::WaylandIntegrationPrivate::setupRegistry()
{
m_queue = new KWayland::Client::EventQueue(this);
m_queue->setup(m_connection);
m_registry = new KWayland::Client::Registry(this);
-#if SCREENCAST_ENABLED
+#if HAVE_PIPEWIRE_SUPPORT
connect(m_registry, &KWayland::Client::Registry::fakeInputAnnounced, this, [this] (quint32 name, quint32 version) {
m_fakeInput = m_registry->createFakeInput(name, version, this);
});
connect(m_registry, &KWayland::Client::Registry::outputAnnounced, this, &WaylandIntegrationPrivate::addOutput);
connect(m_registry, &KWayland::Client::Registry::outputRemoved, this, &WaylandIntegrationPrivate::removeOutput);
#endif
+
connect(m_registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, this, [this] (quint32 name, quint32 version) {
m_windowManagement = m_registry->createPlasmaWindowManagement(name, version, this);
Q_EMIT waylandIntegration()->plasmaWindowManagementInitialized();
});
connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] {
m_registryInitialized = true;
qCDebug(XdgDesktopPortalKdeWaylandIntegration) << "Registry initialized";
});
m_registry->create(m_connection);
m_registry->setEventQueue(m_queue);
m_registry->setup();
}
diff --git a/src/waylandintegration.h b/src/waylandintegration.h
index a6da42a..4888aee 100644
--- a/src/waylandintegration.h
+++ b/src/waylandintegration.h
@@ -1,128 +1,135 @@
/*
* 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_WAYLAND_INTEGRATION_H
#define XDG_DESKTOP_PORTAL_KDE_WAYLAND_INTEGRATION_H
#include
#include
#include
#include
+#if HAVE_PIPEWIRE_SUPPORT
#include
-
#include
#include
+#endif
namespace KWayland {
namespace Client {
class PlasmaWindowManagement;
}
}
namespace WaylandIntegration
{
-
+#if HAVE_PIPEWIRE_SUPPORT
struct EGLStruct {
QList extensions;
EGLDisplay display = EGL_NO_DISPLAY;
EGLContext context = EGL_NO_CONTEXT;
};
class WaylandOutput
{
public:
enum OutputType {
Laptop,
Monitor,
Television
};
void setManufacturer(const QString &manufacturer) { m_manufacturer = manufacturer; }
QString manufacturer() const { return m_manufacturer; }
void setModel(const QString &model) { m_model = model; }
QString model() const { return m_model; }
void setGlobalPosition(const QPoint &pos) { m_globalPosition = pos; }
QPoint globalPosition() const { return m_globalPosition; }
void setResolution(const QSize &resolution) { m_resolution = resolution; }
QSize resolution() const { return m_resolution; }
void setOutputType(const QString &type);
OutputType outputType() const { return m_outputType; }
void setWaylandOutputName(int outputName) { m_waylandOutputName = outputName; }
int waylandOutputName() const { return m_waylandOutputName; }
void setWaylandOutputVersion(int outputVersion) { m_waylandOutputVersion = outputVersion; }
int waylandOutputVersion() const { return m_waylandOutputVersion; }
private:
QString m_manufacturer;
QString m_model;
QPoint m_globalPosition;
QSize m_resolution;
OutputType m_outputType;
// Needed for later output binding
int m_waylandOutputName;
int m_waylandOutputVersion;
};
+#endif
class WaylandIntegration : public QObject
{
Q_OBJECT
Q_SIGNALS:
+#if HAVE_PIPEWIRE_SUPPORT
void newBuffer(uint8_t *screenData);
+#endif
void plasmaWindowManagementInitialized();
};
+
+#if HAVE_PIPEWIRE_SUPPORT
const char * formatGLError(GLenum err);
void authenticate();
- void init();
bool isEGLInitialized();
bool isStreamingEnabled();
void startStreamingInput();
bool startStreaming(quint32 outputName);
void stopStreaming();
void requestPointerButtonPress(quint32 linuxButton);
void requestPointerButtonRelease(quint32 linuxButton);
void requestPointerMotion(const QSizeF &delta);
void requestPointerMotionAbsolute(const QPointF &pos);
void requestPointerAxisDiscrete(Qt::Orientation axis, qreal delta);
void requestKeyboardKeycode(int keycode, bool state);
EGLStruct egl();
-
- KWayland::Client::PlasmaWindowManagement *plasmaWindowManagement();
QMap screens();
QVariant streams();
+#endif
+ void init();
+
+ KWayland::Client::PlasmaWindowManagement *plasmaWindowManagement();
WaylandIntegration *waylandIntegration();
}
#endif // XDG_DESKTOP_PORTAL_KDE_WAYLAND_INTEGRATION_H
diff --git a/src/waylandintegration_p.h b/src/waylandintegration_p.h
index 50f21e4..8b434a0 100644
--- a/src/waylandintegration_p.h
+++ b/src/waylandintegration_p.h
@@ -1,127 +1,141 @@
/*
* 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_WAYLAND_INTEGRATION_P_H
#define XDG_DESKTOP_PORTAL_KDE_WAYLAND_INTEGRATION_P_H
#include "waylandintegration.h"
#include
#include
+#if HAVE_PIPEWIRE_SUPPORT
class ScreenCastStream;
+#endif
namespace KWayland {
namespace Client {
class ConnectionThread;
class EventQueue;
- class FakeInput;
class Registry;
- class RemoteAccessManager;
- class RemoteBuffer;
- class Output;
class PlasmaWindow;
class PlasmaWindowManagement;
+#if HAVE_PIPEWIRE_SUPPORT
+ class FakeInput;
+ class RemoteBuffer;
+ class Output;
+ class RemoteAccessManager;
+#endif
}
}
namespace WaylandIntegration
{
class WaylandIntegrationPrivate : public WaylandIntegration::WaylandIntegration
{
Q_OBJECT
+public:
+ WaylandIntegrationPrivate();
+ ~WaylandIntegrationPrivate();
+
+ void initWayland();
+
+ KWayland::Client::PlasmaWindowManagement *plasmaWindowManagement();
+
+protected Q_SLOTS:
+ void setupRegistry();
+
+private:
+ bool m_registryInitialized = false;
+
+ QThread *m_thread = nullptr;
+ KWayland::Client::ConnectionThread *m_connection = nullptr;
+ KWayland::Client::EventQueue *m_queue = nullptr;
+ KWayland::Client::Registry *m_registry = nullptr;
+ KWayland::Client::PlasmaWindowManagement *m_windowManagement = nullptr;
+
+#if HAVE_PIPEWIRE_SUPPORT
public:
typedef struct {
uint nodeId;
QVariantMap map;
} Stream;
typedef QList Streams;
- WaylandIntegrationPrivate();
- ~WaylandIntegrationPrivate();
-
void authenticate();
+
void initDrm();
void initEGL();
- void initWayland();
bool isEGLInitialized() const;
bool isStreamingEnabled() const;
void bindOutput(int outputName, int outputVersion);
void startStreamingInput();
bool startStreaming(quint32 outputName);
void stopStreaming();
void requestPointerButtonPress(quint32 linuxButton);
void requestPointerButtonRelease(quint32 linuxButton);
void requestPointerMotion(const QSizeF &delta);
void requestPointerMotionAbsolute(const QPointF &pos);
void requestPointerAxisDiscrete(Qt::Orientation axis, qreal delta);
void requestKeyboardKeycode(int keycode, bool state);
EGLStruct egl();
- KWayland::Client::PlasmaWindowManagement *plasmaWindowManagement();
QMap screens();
QVariant streams();
protected Q_SLOTS:
void addOutput(quint32 name, quint32 version);
void removeOutput(quint32 name);
void processBuffer(const KWayland::Client::RemoteBuffer *rbuf);
- void setupRegistry();
private:
bool m_eglInitialized = false;
bool m_streamingEnabled = false;
bool m_streamInput = false;
- bool m_registryInitialized = false;
bool m_waylandAuthenticationRequested = false;
quint32 m_output;
QDateTime m_lastFrameTime;
ScreenCastStream *m_stream = nullptr;
- QThread *m_thread = nullptr;
-
QPoint m_streamedScreenPosition;
QMap m_outputMap;
QList m_bindOutputs;
- KWayland::Client::ConnectionThread *m_connection = nullptr;
- KWayland::Client::EventQueue *m_queue = nullptr;
KWayland::Client::FakeInput *m_fakeInput = nullptr;
- KWayland::Client::Registry *m_registry = nullptr;
KWayland::Client::RemoteAccessManager *m_remoteAccessManager = nullptr;
- KWayland::Client::PlasmaWindowManagement *m_windowManagement = nullptr;
qint32 m_drmFd = 0; // for GBM buffer mmap
gbm_device *m_gbmDevice = nullptr; // for passed GBM buffer retrieval
EGLStruct m_egl;
+#endif
};
}
#endif // XDG_DESKTOP_PORTAL_KDE_WAYLAND_INTEGRATION_P_H