diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,33 +20,35 @@ 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 diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -2,5 +2,9 @@ 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 --- /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 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,18 +23,18 @@ 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 @@ -72,7 +72,7 @@ KF5::WindowSystem ) -if (SCREENCAST_ENABLED) +if (HAVE_PIPEWIRE_SUPPORT) target_link_libraries(xdg-desktop-portal-kde PipeWire::PipeWire ${Epoxy_LIBRARIES} diff --git a/src/desktopportal.h b/src/desktopportal.h --- a/src/desktopportal.h +++ b/src/desktopportal.h @@ -33,14 +33,15 @@ #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 @@ -58,12 +59,12 @@ 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/desktopportal.cpp b/src/desktopportal.cpp --- a/src/desktopportal.cpp +++ b/src/desktopportal.cpp @@ -39,11 +39,11 @@ 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(); } } diff --git a/src/waylandintegration.h b/src/waylandintegration.h --- a/src/waylandintegration.h +++ b/src/waylandintegration.h @@ -26,10 +26,11 @@ #include #include +#if HAVE_PIPEWIRE_SUPPORT #include - #include #include +#endif namespace KWayland { namespace Client { @@ -39,7 +40,7 @@ namespace WaylandIntegration { - +#if HAVE_PIPEWIRE_SUPPORT struct EGLStruct { QList extensions; EGLDisplay display = EGL_NO_DISPLAY; @@ -86,18 +87,22 @@ 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(); @@ -115,10 +120,12 @@ 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(); } diff --git a/src/waylandintegration.cpp b/src/waylandintegration.cpp --- a/src/waylandintegration.cpp +++ b/src/waylandintegration.cpp @@ -20,7 +20,7 @@ #include "waylandintegration.h" #include "waylandintegration_p.h" -#include "screencaststream.h" + #include #include @@ -37,33 +37,39 @@ // 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(); @@ -124,11 +130,6 @@ return globalWaylandIntegration->egl(); } -KWayland::Client::PlasmaWindowManagement * WaylandIntegration::plasmaWindowManagement() -{ - return globalWaylandIntegration->plasmaWindowManagement(); -} - QMap WaylandIntegration::screens() { return globalWaylandIntegration->screens(); @@ -139,11 +140,6 @@ return globalWaylandIntegration->streams(); } -WaylandIntegration::WaylandIntegration * WaylandIntegration::waylandIntegration() -{ - return globalWaylandIntegration; -} - const char * WaylandIntegration::formatGLError(GLenum err) { switch(err) { @@ -223,33 +219,49 @@ 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; @@ -414,27 +426,14 @@ 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); @@ -506,6 +505,21 @@ 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); @@ -541,6 +555,7 @@ m_connection->initConnection(); } +#if HAVE_PIPEWIRE_SUPPORT void WaylandIntegration::WaylandIntegrationPrivate::addOutput(quint32 name, quint32 version) { KWayland::Client::Output *output = new KWayland::Client::Output(this); @@ -621,21 +636,23 @@ 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(); diff --git a/src/waylandintegration_p.h b/src/waylandintegration_p.h --- a/src/waylandintegration_p.h +++ b/src/waylandintegration_p.h @@ -26,19 +26,23 @@ #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 } } @@ -48,20 +52,38 @@ 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; @@ -79,45 +101,37 @@ 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 }; }