Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package krfb for openSUSE:Factory checked in at 2023-04-23 22:46:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/krfb (Old) and /work/SRC/openSUSE:Factory/.krfb.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "krfb" Sun Apr 23 22:46:42 2023 rev:126 rq:1080672 version:23.04.0 Changes: -------- --- /work/SRC/openSUSE:Factory/krfb/krfb.changes 2023-03-03 22:29:53.623644001 +0100 +++ /work/SRC/openSUSE:Factory/.krfb.new.1533/krfb.changes 2023-04-23 22:49:33.003418801 +0200 @@ -1,0 +2,20 @@ +Sat Apr 15 08:46:14 UTC 2023 - Christophe Marin <[email protected]> + +- Update to 23.04.0 + * New feature release + * For more details please see: + * https://kde.org/announcements/gear/23.04.0/ + +------------------------------------------------------------------- +Fri Mar 31 16:42:23 UTC 2023 - Christophe Marin <[email protected]> + +- Update to 23.03.90 + * New feature release + +------------------------------------------------------------------- +Fri Mar 17 20:44:01 UTC 2023 - Christophe Marin <[email protected]> + +- Update to 23.03.80 + * New feature release + +------------------------------------------------------------------- Old: ---- krfb-22.12.3.tar.xz krfb-22.12.3.tar.xz.sig New: ---- krfb-23.04.0.tar.xz krfb-23.04.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ krfb.spec ++++++ --- /var/tmp/diff_new_pack.KMOzPO/_old 2023-04-23 22:49:33.615422259 +0200 +++ /var/tmp/diff_new_pack.KMOzPO/_new 2023-04-23 22:49:33.619422282 +0200 @@ -16,11 +16,9 @@ # -# Latest stable Applications (e.g. 17.08 in KA, but 17.11.80 in KUA) -%{!?_kapp_version: %define _kapp_version %(echo %{version}| awk -F. '{print $1"."$2}')} %bcond_without released Name: krfb -Version: 22.12.3 +Version: 23.04.0 Release: 0 Summary: Screen sharing using the VNC/RFB protocol License: GPL-2.0-or-later @@ -49,6 +47,7 @@ BuildRequires: cmake(KF5WidgetsAddons) BuildRequires: cmake(KF5WindowSystem) BuildRequires: cmake(KF5XmlGui) +BuildRequires: cmake(KPipeWire) BuildRequires: cmake(PlasmaWaylandProtocols) BuildRequires: cmake(Qt5Core) BuildRequires: cmake(Qt5DBus) @@ -58,6 +57,7 @@ BuildRequires: pkgconfig(gbm) BuildRequires: pkgconfig(libsystemd) BuildRequires: pkgconfig(xdamage) +BuildRequires: pkgconfig(epoxy) BuildRequires: pkgconfig(xt) BuildRequires: pkgconfig(xtst) ++++++ krfb-22.12.3.tar.xz -> krfb-23.04.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/.kde-ci.yml new/krfb-23.04.0/.kde-ci.yml --- old/krfb-22.12.3/.kde-ci.yml 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/.kde-ci.yml 2023-04-12 18:47:19.000000000 +0200 @@ -19,3 +19,4 @@ 'frameworks/kxmlgui': '@stable' 'frameworks/kwayland': '@stable' 'libraries/plasma-wayland-protocols': '@latest' # can be switched to @stable when 1.5.0 is released + 'plasma/kpipewire': '@latest' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/CMakeLists.txt new/krfb-23.04.0/CMakeLists.txt --- old/krfb-22.12.3/CMakeLists.txt 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/CMakeLists.txt 2023-04-12 18:47:19.000000000 +0200 @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.16) # KDE Application Version, managed by release script -set (RELEASE_SERVICE_VERSION_MAJOR "22") -set (RELEASE_SERVICE_VERSION_MINOR "12") -set (RELEASE_SERVICE_VERSION_MICRO "3") +set (RELEASE_SERVICE_VERSION_MAJOR "23") +set (RELEASE_SERVICE_VERSION_MINOR "04") +set (RELEASE_SERVICE_VERSION_MICRO "0") set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") project(krfb VERSION ${RELEASE_SERVICE_VERSION}) @@ -73,6 +73,7 @@ option(DISABLE_PIPEWIRE "Disable PipeWire support." OFF) if(NOT DISABLE_PIPEWIRE) + find_package(KPipeWire REQUIRED) pkg_check_modules(PipeWire IMPORTED_TARGET libpipewire-0.3) endif() add_feature_info(PipeWire PipeWire_FOUND "Required for pipewire screencast plugin") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/events/xdp/xdpevents.cpp new/krfb-23.04.0/events/xdp/xdpevents.cpp --- old/krfb-22.12.3/events/xdp/xdpevents.cpp 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/events/xdp/xdpevents.cpp 2023-04-12 18:47:19.000000000 +0200 @@ -61,10 +61,8 @@ void XdpEventHandler::handleKeyboard(bool down, rfbKeySym keySym) { - // TODO: implement button handling - // both in FakeInput interface and here - Q_UNUSED(down) - Q_UNUSED(keySym) + const QDBusObjectPath sessionHandle = frameBuffer()->customProperty(QStringLiteral("session_handle")).value<QDBusObjectPath>(); + data->dbusXdpRemoteDesktopService->NotifyKeyboardKeysym(sessionHandle, {}, keySym, down); } void XdpEventHandler::handlePointer(int buttonMask, int x, int y) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/pipewire/CMakeLists.txt new/krfb-23.04.0/framebuffers/pipewire/CMakeLists.txt --- old/krfb-22.12.3/framebuffers/pipewire/CMakeLists.txt 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/pipewire/CMakeLists.txt 2023-04-12 18:47:19.000000000 +0200 @@ -49,6 +49,8 @@ Wayland::Client krfbprivate PkgConfig::PipeWire + K::KPipeWire + K::KPipeWireDmaBuf ) if (HAVE_DMA_BUF) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/pipewire/pipewire.json new/krfb-23.04.0/framebuffers/pipewire/pipewire.json --- old/krfb-22.12.3/framebuffers/pipewire/pipewire.json 1970-01-01 01:00:00.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/pipewire/pipewire.json 2023-04-12 18:47:19.000000000 +0200 @@ -0,0 +1,5 @@ +{ + "X-KDE-OnlyShowOnQtPlatforms": [ + "wayland" + ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/pipewire/pw_framebuffer.cpp new/krfb-23.04.0/framebuffers/pipewire/pw_framebuffer.cpp --- old/krfb-22.12.3/framebuffers/pipewire/pw_framebuffer.cpp 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/pipewire/pw_framebuffer.cpp 2023-04-12 18:47:19.000000000 +0200 @@ -42,9 +42,10 @@ #include "xdp_dbus_remotedesktop_interface.h" #include "krfb_fb_pipewire_debug.h" #include "screencasting.h" +#include <KPipeWire/PipeWireSourceStream> +#include <KPipeWire/DmaBufHandler> #if HAVE_DMA_BUF -#include <fcntl.h> #include <unistd.h> #include <gbm.h> @@ -114,47 +115,21 @@ private: friend class PWFrameBuffer; - static void onCoreError(void *data, uint32_t id, int seq, int res, const char *message); - static void onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format); - static void onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message); - static void onStreamProcess(void *data); - void initDbus(); - void initPw(); // dbus handling - void handleSessionCreated(quint32 &code, QVariantMap &results); - void handleDevicesSelected(quint32 &code, QVariantMap &results); - void handleSourcesSelected(quint32 &code, QVariantMap &results); - void handleRemoteDesktopStarted(quint32 &code, QVariantMap &results); + void handleSessionCreated(quint32 code, const QVariantMap &results); + void handleDevicesSelected(quint32 code, const QVariantMap &results); + void handleSourcesSelected(quint32 code, const QVariantMap &results); + void handleRemoteDesktopStarted(quint32 code, const QVariantMap &results); + void setVideoSize(const QSize &size); // pw handling - pw_stream *createReceivingStream(); - void handleFrame(pw_buffer *pwBuffer); + void handleFrame(const PipeWireFrame &frame); // link to public interface PWFrameBuffer *q; - // pipewire stuff - struct pw_context *pwContext = nullptr; - struct pw_core *pwCore = nullptr; - struct pw_stream *pwStream = nullptr; - struct pw_thread_loop *pwMainLoop = nullptr; - - // wayland-like listeners - // ...of events that happen in pipewire server - spa_hook coreListener = {}; - spa_hook streamListener = {}; - - // event handlers - pw_core_events pwCoreEvents = {}; - pw_stream_events pwStreamEvents = {}; - - uint pwStreamNodeId = 0; - - // negotiated video format - spa_video_info_raw *videoFormat = nullptr; - // requests a session from XDG Desktop Portal // auto-generated and compiled from xdp_dbus_interface.xml file QScopedPointer<OrgFreedesktopPortalScreenCastInterface> dbusXdpScreenCastService; @@ -162,113 +137,24 @@ // XDP screencast session handle QDBusObjectPath sessionPath; - // Pipewire file descriptor - QDBusUnixFileDescriptor pipewireFd; // screen geometry holder - QSize streamSize; QSize videoSize; - // Allowed devices - uint devices = 0; - // sanity indicator bool isValid = true; - - QImage cursorTexture; - QPoint cursorPosition; - QPoint cursorHotspot; - -#if HAVE_DMA_BUF - struct EGLStruct { - QList<QByteArray> extensions; - EGLDisplay display = EGL_NO_DISPLAY; - EGLContext context = EGL_NO_CONTEXT; - }; - - bool m_eglInitialized = false; - qint32 m_drmFd = 0; // for GBM buffer mmap - gbm_device *m_gbmDevice = nullptr; // for passed GBM buffer retrieval - - EGLStruct m_egl; -#endif /* HAVE_DMA_BUF */ + std::unique_ptr<PipeWireSourceStream> stream; + std::optional<PipeWireCursor> cursor; + DmaBufHandler m_dmabufHandler; }; -PWFrameBuffer::Private::Private(PWFrameBuffer *q) : q(q) +PWFrameBuffer::Private::Private(PWFrameBuffer *q) + : q(q) + , stream(new PipeWireSourceStream(q)) { - pwCoreEvents.version = PW_VERSION_CORE_EVENTS; - pwCoreEvents.error = &onCoreError; - - pwStreamEvents.version = PW_VERSION_STREAM_EVENTS; - pwStreamEvents.state_changed = &onStreamStateChanged; - pwStreamEvents.param_changed = &onStreamParamChanged; - pwStreamEvents.process = &onStreamProcess; - -#if HAVE_DMA_BUF - m_drmFd = open("/dev/dri/renderD128", O_RDWR); - - if (m_drmFd < 0) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to open drm render node: " << strerror(errno); - return; - } - - m_gbmDevice = gbm_create_device(m_drmFd); - - if (!m_gbmDevice) { - qCWarning(KRFB_FB_PIPEWIRE) << "Cannot create GBM device: " << strerror(errno); - return; - } - - // 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(KRFB_FB_PIPEWIRE) << "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(KRFB_FB_PIPEWIRE) << "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(KRFB_FB_PIPEWIRE) << "Error during obtaining EGL display: " << formatGLError(eglGetError()); - return; - } - - EGLint major, minor; - if (eglInitialize(m_egl.display, &major, &minor) == EGL_FALSE) { - qCWarning(KRFB_FB_PIPEWIRE) << "Error during eglInitialize: " << formatGLError(eglGetError()); - return; - } - - if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { - qCWarning(KRFB_FB_PIPEWIRE) << "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(KRFB_FB_PIPEWIRE) << "Couldn't create EGL context: " << formatGLError(eglGetError()); - return; - } - - qCDebug(KRFB_FB_PIPEWIRE) << "Egl initialization succeeded"; - qCDebug(KRFB_FB_PIPEWIRE) << QStringLiteral("EGL version: %1.%2").arg(major).arg(minor); - - m_eglInitialized = true; -#endif /* HAVE_DMA_BUF */ + QObject::connect(stream.get(), &PipeWireSourceStream::frameReceived, q, [this] (const PipeWireFrame &frame) { + handleFrame(frame); + }); } /** @@ -314,7 +200,7 @@ SLOT(handleXdpSessionCreated(uint, QVariantMap))); } -void PWFrameBuffer::handleXdpSessionCreated(quint32 code, QVariantMap results) +void PWFrameBuffer::handleXdpSessionCreated(quint32 code, const QVariantMap &results) { d->handleSessionCreated(code, results); } @@ -326,7 +212,7 @@ * @param code return code for dbus call. Zero is success, non-zero means error * @param results map with results of call. */ -void PWFrameBuffer::Private::handleSessionCreated(quint32 &code, QVariantMap &results) +void PWFrameBuffer::Private::handleSessionCreated(quint32 code, const QVariantMap &results) { if (code != 0) { qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create session: " << code; @@ -357,7 +243,7 @@ SLOT(handleXdpDevicesSelected(uint, QVariantMap))); } -void PWFrameBuffer::handleXdpDevicesSelected(quint32 code, QVariantMap results) +void PWFrameBuffer::handleXdpDevicesSelected(quint32 code, const QVariantMap &results) { d->handleDevicesSelected(code, results); } @@ -368,7 +254,7 @@ * @param code return code for dbus call. Zero is success, non-zero means error * @param results map with results of call. */ -void PWFrameBuffer::Private::handleDevicesSelected(quint32 &code, QVariantMap &results) +void PWFrameBuffer::Private::handleDevicesSelected(quint32 code, const QVariantMap &results) { Q_UNUSED(results) if (code != 0) { @@ -398,7 +284,7 @@ SLOT(handleXdpSourcesSelected(uint, QVariantMap))); } -void PWFrameBuffer::handleXdpSourcesSelected(quint32 code, QVariantMap results) +void PWFrameBuffer::handleXdpSourcesSelected(quint32 code, const QVariantMap &results) { d->handleSourcesSelected(code, results); } @@ -411,7 +297,7 @@ * @param code return code for dbus call. Zero is success, non-zero means error * @param results map with results of call. */ -void PWFrameBuffer::Private::handleSourcesSelected(quint32 &code, QVariantMap &) +void PWFrameBuffer::Private::handleSourcesSelected(quint32 code, const QVariantMap &) { if (code != 0) { qCWarning(KRFB_FB_PIPEWIRE) << "Failed to select sources: " << code; @@ -434,7 +320,7 @@ } -void PWFrameBuffer::handleXdpRemoteDesktopStarted(quint32 code, QVariantMap results) +void PWFrameBuffer::handleXdpRemoteDesktopStarted(quint32 code, const QVariantMap &results) { d->handleRemoteDesktopStarted(code, results); } @@ -446,7 +332,7 @@ * @param code return code for dbus call. Zero is success, non-zero means error * @param results map with results of call. */ -void PWFrameBuffer::Private::handleRemoteDesktopStarted(quint32 &code, QVariantMap &results) +void PWFrameBuffer::Private::handleRemoteDesktopStarted(quint32 code, const QVariantMap &results) { if (code != 0) { qCWarning(KRFB_FB_PIPEWIRE) << "Failed to start screencast: " << code; @@ -454,8 +340,14 @@ return; } + if (results.value(QStringLiteral("devices")).toUInt() == 0) { + qCWarning(KRFB_FB_PIPEWIRE) << "No devices were granted" << results; + isValid = false; + return; + } + // there should be only one stream - Streams streams = qdbus_cast<Streams>(results.value(QStringLiteral("streams"))); + const Streams streams = qdbus_cast<Streams>(results.value(QStringLiteral("streams"))); if (streams.isEmpty()) { // maybe we should check deeper with qdbus_cast but this suffices for now qCWarning(KRFB_FB_PIPEWIRE) << "Failed to get screencast streams"; @@ -471,501 +363,85 @@ return; } - pipewireFd = streamReply.value(); + QDBusUnixFileDescriptor pipewireFd = streamReply.value(); if (!pipewireFd.isValid()) { qCWarning(KRFB_FB_PIPEWIRE) << "Couldn't get pipewire connection file descriptor"; isValid = false; return; } - devices = results.value(QStringLiteral("types")).toUInt(); - - pwStreamNodeId = streams.first().nodeId; - - initPw(); -} - -/** - * @brief PWFrameBuffer::Private::initPw - initialize Pipewire socket connectivity. - * pipewireFd should be pointing to existing file descriptor that was passed by D-Bus at this point. - */ -void PWFrameBuffer::Private::initPw() { - qInfo() << "Initializing Pipewire connectivity"; - - // init pipewire (required) - pw_init(nullptr, nullptr); // args are not used anyways - - pwMainLoop = pw_thread_loop_new("pipewire-main-loop", nullptr); - pw_thread_loop_lock(pwMainLoop); - - pwContext = pw_context_new(pw_thread_loop_get_loop(pwMainLoop), nullptr, 0); - if (!pwContext) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create PipeWire context"; - return; - } - - pwCore = pw_context_connect(pwContext, nullptr, 0); - if (!pwCore) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to connect PipeWire context"; - return; - } - - pw_core_add_listener(pwCore, &coreListener, &pwCoreEvents, this); - - pwStream = createReceivingStream(); - if (!pwStream) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create PipeWire stream"; - return; - } - - if (pw_thread_loop_start(pwMainLoop) < 0) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to start main PipeWire loop"; + if (!stream->createStream(streams.first().nodeId, pipewireFd.takeFileDescriptor())) { + qCWarning(KRFB_FB_PIPEWIRE) << "Couldn't create the pipewire stream"; isValid = false; - } - - pw_thread_loop_unlock(pwMainLoop); -} - -void PWFrameBuffer::Private::onCoreError(void *data, uint32_t id, int seq, int res, const char *message) -{ - Q_UNUSED(data); - Q_UNUSED(id); - Q_UNUSED(seq); - Q_UNUSED(res); - - qInfo() << "core error: " << message; -} - -/** - * @brief PWFrameBuffer::Private::onStreamStateChanged - called whenever stream state changes on pipewire server - * @param data pointer that you have set in pw_stream_add_listener call's last argument - * @param state new state that stream has changed to - * @param error_message optional error message, is set to non-null if state is error - */ -void PWFrameBuffer::Private::onStreamStateChanged(void *data, pw_stream_state /*old*/, pw_stream_state state, const char *error_message) -{ - Q_UNUSED(data); - - qInfo() << "Stream state changed: " << pw_stream_state_as_string(state); - - switch (state) { - case PW_STREAM_STATE_ERROR: - qCWarning(KRFB_FB_PIPEWIRE) << "pipewire stream error: " << error_message; - break; - case PW_STREAM_STATE_PAUSED: - case PW_STREAM_STATE_STREAMING: - case PW_STREAM_STATE_UNCONNECTED: - case PW_STREAM_STATE_CONNECTING: - break; - } -} - -#define CURSOR_BPP 4 -#define CURSOR_META_SIZE(w,h) (sizeof(struct spa_meta_cursor) + \ - sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP) - -/** - * @brief PWFrameBuffer::Private::onStreamFormatChanged - being executed after stream is set to active - * and after setup has been requested to connect to it. The actual video format is being negotiated here. - * @param data pointer that you have set in pw_stream_add_listener call's last argument - * @param format format that's being proposed - */ -void PWFrameBuffer::Private::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format) -{ - qInfo() << "Stream format changed"; - auto d = static_cast<PWFrameBuffer::Private *>(data); - - if (!format || id != SPA_PARAM_Format) { - return; - } - - d->videoFormat = new spa_video_info_raw(); - spa_format_video_raw_parse(format, d->videoFormat); - auto width = d->videoFormat->size.width; - auto height = d->videoFormat->size.height; - auto stride = SPA_ROUND_UP_N(width * BYTES_PER_PIXEL, 4); - auto size = height * stride; - d->streamSize = QSize(width, height); - - uint8_t buffer[1024]; - auto builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - - // setup buffers and meta header for new format - -#if HAVE_DMA_BUF - const auto bufferTypes = d->m_eglInitialized ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) : - (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); -#else - const auto bufferTypes = (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); -#endif /* HAVE_DMA_BUF */ - - QVector<const struct spa_pod *> params = { - reinterpret_cast<spa_pod *>(spa_pod_builder_add_object(&builder, - SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_align, SPA_POD_Int(16), - SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(bufferTypes))), - reinterpret_cast<spa_pod *>(spa_pod_builder_add_object(&builder, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)))), - reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(&builder, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, - SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size, - SPA_POD_Int(sizeof(struct spa_meta_region)))), - reinterpret_cast<spa_pod*>(spa_pod_builder_add_object ( &builder, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, - SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor), - SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int (CURSOR_META_SIZE (64, 64), - CURSOR_META_SIZE (1, 1), - CURSOR_META_SIZE (1024, 1024)))), - reinterpret_cast<spa_pod*>(spa_pod_builder_add_object ( &builder, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoDamage), - SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int( - sizeof(struct spa_meta_region) * 16, - sizeof(struct spa_meta_region) * 1, - sizeof(struct spa_meta_region) * 16))), - }; - pw_stream_update_params(d->pwStream, params.data(), params.size()); -} - -/** - * @brief PWFrameBuffer::Private::onNewBuffer - called when new buffer is available in pipewire stream - * @param data pointer that you have set in pw_stream_add_listener call's last argument - * @param id - */ -void PWFrameBuffer::Private::onStreamProcess(void *data) -{ - auto d = static_cast<PWFrameBuffer::Private *>(data); - - pw_buffer* next_buffer; - pw_buffer* buffer = nullptr; - - next_buffer = pw_stream_dequeue_buffer(d->pwStream); - while (next_buffer) { - buffer = next_buffer; - next_buffer = pw_stream_dequeue_buffer(d->pwStream); - - if (next_buffer) { - pw_stream_queue_buffer(d->pwStream, buffer); - } - } - - if (!buffer) { return; } - - d->handleFrame(buffer); - - pw_stream_queue_buffer(d->pwStream, buffer); -} - -static QImage::Format spaToQImageFormat(quint32 format) -{ - return format == SPA_VIDEO_FORMAT_BGR ? QImage::Format_BGR888 - : format == SPA_VIDEO_FORMAT_RGBx ? QImage::Format_RGBX8888 - : QImage::Format_RGB32; + setVideoSize(qdbus_cast<QSize>(streams.first().map[QStringLiteral("size")].value<QDBusArgument>())); } -void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) +void PWFrameBuffer::Private::handleFrame(const PipeWireFrame &frame) { - auto spaBuffer = pwBuffer->buffer; - uint8_t *src = nullptr; + cursor = frame.cursor; - // process cursor - { - struct spa_meta_cursor *cursor = static_cast<struct spa_meta_cursor*>(spa_buffer_find_meta_data (spaBuffer, SPA_META_Cursor, sizeof (*cursor))); - if (spa_meta_cursor_is_valid (cursor)) { - struct spa_meta_bitmap *bitmap = nullptr; - - if (cursor->bitmap_offset) - bitmap = SPA_MEMBER (cursor, cursor->bitmap_offset, struct spa_meta_bitmap); - - if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) { - const uint8_t *bitmap_data; - - bitmap_data = SPA_MEMBER (bitmap, bitmap->offset, uint8_t); - cursorHotspot = { cursor->hotspot.x, cursor->hotspot.y }; - cursorTexture = QImage(bitmap_data, bitmap->size.width, bitmap->size.height, bitmap->stride, spaToQImageFormat(bitmap->format)); - } - - cursorPosition = QPoint{ cursor->position.x, cursor->position.y }; - } - } - - if (spaBuffer->datas[0].chunk->size == 0) { + if (!frame.dmabuf && !frame.image) { qCDebug(KRFB_FB_PIPEWIRE) << "Got empty buffer. The buffer possibly carried only " "information about the mouse cursor."; return; } - std::function<void()> cleanup; - const qint64 srcStride = spaBuffer->datas[0].chunk->stride; - if (spaBuffer->datas->type == SPA_DATA_MemFd) { - uint8_t *map = static_cast<uint8_t*>(mmap( - nullptr, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset, - PROT_READ, MAP_PRIVATE, spaBuffer->datas->fd, 0)); - - if (map == MAP_FAILED) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to mmap the memory: " << strerror(errno); - return; - } - src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t); - - cleanup = [map, spaBuffer] { - munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset); - }; - } else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) { - src = static_cast<uint8_t*>(spaBuffer->datas[0].data); + if (frame.image) { + memcpy(q->fb, frame.image->constBits(), frame.image->sizeInBytes()); + setVideoSize(frame.image->size()); } #if HAVE_DMA_BUF - else if (spaBuffer->datas->type == SPA_DATA_DmaBuf) { - if (!m_eglInitialized) { - // Shouldn't reach this - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to process DMA buffer."; - return; - } - - gbm_import_fd_data importInfo = {static_cast<int>(spaBuffer->datas->fd), static_cast<uint32_t>(streamSize.width()), - static_cast<uint32_t>(streamSize.height()), static_cast<uint32_t>(spaBuffer->datas[0].chunk->stride), GBM_BO_FORMAT_ARGB8888}; - gbm_bo *imported = gbm_bo_import(m_gbmDevice, GBM_BO_IMPORT_FD, &importInfo, GBM_BO_USE_SCANOUT); - if (!imported) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to process buffer: Cannot import passed GBM fd - " << strerror(errno); - return; - } - - // 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) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to record frame: Error creating EGLImageKHR - " << formatGLError(glGetError()); - gbm_bo_destroy(imported); - 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); - - src = static_cast<uint8_t*>(malloc(srcStride * streamSize.height())); - - GLenum glFormat = GL_BGRA; - switch (videoFormat->format) { - case SPA_VIDEO_FORMAT_RGBx: - glFormat = GL_RGBA; - break; - case SPA_VIDEO_FORMAT_RGBA: - glFormat = GL_RGBA; - break; - case SPA_VIDEO_FORMAT_BGRx: - glFormat = GL_BGRA; - break; - case SPA_VIDEO_FORMAT_RGB: - glFormat = GL_RGB; - break; - case SPA_VIDEO_FORMAT_BGR: - glFormat = GL_BGR; - break; - default: - glFormat = GL_BGRA; - break; - } - glGetTexImage(GL_TEXTURE_2D, 0, glFormat, GL_UNSIGNED_BYTE, src); - - if (!src) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to get image from DMA buffer."; - gbm_bo_destroy(imported); + else if (frame.dmabuf) { + QImage src((uchar*) q->fb, videoSize.width(), videoSize.height(), QImage::Format_RGB32); + if (!m_dmabufHandler.downloadFrame(src, frame)) { + stream->renegotiateModifierFailed(frame.format, frame.dmabuf->modifier); + qCDebug(KRFB_FB_PIPEWIRE) << "Failed to download frame."; return; } - - cleanup = [src] { - free(src); - }; - - glDeleteTextures(1, &texture); - eglDestroyImageKHR(m_egl.display, image); - - gbm_bo_destroy(imported); + setVideoSize(src.size()); } #endif /* HAVE_DMA_BUF */ - - struct spa_meta_region* videoMetadata = - static_cast<struct spa_meta_region*>(spa_buffer_find_meta_data( - spaBuffer, SPA_META_VideoCrop, sizeof(*videoMetadata))); - - if (videoMetadata && (videoMetadata->region.size.width > static_cast<uint32_t>(streamSize.width()) || - videoMetadata->region.size.height > static_cast<uint32_t>(streamSize.height()))) { - qCWarning(KRFB_FB_PIPEWIRE) << "Stream metadata sizes are wrong!"; - return; - } - - // Use video metadata when video size from metadata is set and smaller than - // video stream size, so we need to adjust it. - bool videoFullWidth = true; - bool videoFullHeight = true; - if (videoMetadata && videoMetadata->region.size.width != 0 && - videoMetadata->region.size.height != 0) { - if (videoMetadata->region.size.width < static_cast<uint32_t>(streamSize.width())) { - videoFullWidth = false; - } else if (videoMetadata->region.size.height < static_cast<uint32_t>(streamSize.height())) { - videoFullHeight = false; - } + else { + qCDebug(KRFB_FB_PIPEWIRE) << "Unknown kind of frame"; } - QSize prevVideoSize = videoSize; - if (!videoFullHeight || !videoFullWidth) { - videoSize = QSize(videoMetadata->region.size.width, videoMetadata->region.size.height); - } else { - videoSize = streamSize; - } - - if (!q->fb || videoSize != prevVideoSize) { - if (q->fb) { - free(q->fb); - } - q->fb = static_cast<char*>(malloc(videoSize.width() * videoSize.height() * BYTES_PER_PIXEL)); - - if (!q->fb) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to allocate buffer"; - isValid = false; - return; - } - - Q_EMIT q->frameBufferChanged(); - } - - const qint32 dstStride = videoSize.width() * BYTES_PER_PIXEL; - Q_ASSERT(dstStride <= srcStride); - - if (!videoFullHeight && (videoMetadata->region.position.y + videoSize.height() <= streamSize.height())) { - src += srcStride * videoMetadata->region.position.y; - } - - const int xOffset = !videoFullWidth && (videoMetadata->region.position.x + videoSize.width() <= streamSize.width()) - ? videoMetadata->region.position.x * BYTES_PER_PIXEL : 0; - - char *dst = q->fb; - for (int i = 0; i < videoSize.height(); ++i) { - // Adjust source content based on crop video position if needed - src += xOffset; - std::memcpy(dst, src, dstStride); - - if (videoFormat->format == SPA_VIDEO_FORMAT_BGRA || videoFormat->format == SPA_VIDEO_FORMAT_BGRx) { - for (int j = 0; j < dstStride; j += 4) { - std::swap(dst[j], dst[j + 2]); - } - } - - src += srcStride - xOffset; - dst += dstStride; - } - - if (spaBuffer->datas->type == SPA_DATA_MemFd || - spaBuffer->datas->type == SPA_DATA_DmaBuf) { - cleanup(); - } - - if (videoFormat->format != SPA_VIDEO_FORMAT_RGB) { - QImage img((uchar*) q->fb, videoSize.width(), videoSize.height(), dstStride, spaToQImageFormat(videoFormat->format)); - img.convertTo(QImage::Format_RGB888); - } - - if (spa_meta* vdMeta = spa_buffer_find_meta(spaBuffer, SPA_META_VideoDamage)) { - struct spa_meta_region *r; - spa_meta_for_each(r, vdMeta) { - if (!spa_meta_region_is_valid(r)) - break; - - q->tiles.append(QRect(r->region.position.x, r->region.position.y, r->region.size.width, r->region.size.height)); + if (auto damage = frame.damage) { + for (const auto &rect : *damage) { + q->tiles.append(rect); } } else { q->tiles.append(QRect(0, 0, videoSize.width(), videoSize.height())); } } -/** - * @brief PWFrameBuffer::Private::createReceivingStream - create a stream that will consume Pipewire buffers - * and copy the framebuffer to the existing image that we track. The state of the stream and configuration - * are later handled by the corresponding listener. - */ -pw_stream *PWFrameBuffer::Private::createReceivingStream() +void PWFrameBuffer::Private::setVideoSize(const QSize &size) { - spa_rectangle pwMinScreenBounds = SPA_RECTANGLE(1, 1); - spa_rectangle pwMaxScreenBounds = SPA_RECTANGLE(UINT32_MAX, UINT32_MAX); - - spa_fraction pwFramerateMin = SPA_FRACTION(0, 1); - spa_fraction pwFramerateMax = SPA_FRACTION(60, 1); - - pw_properties* reuseProps = pw_properties_new_string("pipewire.client.reuse=1"); - - auto stream = pw_stream_new(pwCore, "krfb-fb-consume-stream", reuseProps); - - uint8_t buffer[1024] = {}; - const spa_pod *params[1]; - auto builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - - params[0] = reinterpret_cast<spa_pod *>(spa_pod_builder_add_object(&builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(6, - SPA_VIDEO_FORMAT_RGBx, SPA_VIDEO_FORMAT_RGBA, - SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA, - SPA_VIDEO_FORMAT_RGB, SPA_VIDEO_FORMAT_BGR), - SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pwMaxScreenBounds, &pwMinScreenBounds, &pwMaxScreenBounds), - SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&pwFramerateMin), - SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction(&pwFramerateMax, &pwFramerateMin, &pwFramerateMax))); - - pw_stream_add_listener(stream, &streamListener, &pwStreamEvents, this); + if (q->fb && videoSize == size) { + return; + } - if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pwStreamNodeId, PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) { + free(q->fb); + q->fb = static_cast<char*>(malloc(size.width() * size.height() * BYTES_PER_PIXEL)); + if (!q->fb) { + qCWarning(KRFB_FB_PIPEWIRE) << "Failed to allocate buffer"; isValid = false; + return; } + videoSize = size; - return stream; + Q_EMIT q->frameBufferChanged(); } PWFrameBuffer::Private::~Private() { - if (pwMainLoop) { - pw_thread_loop_stop(pwMainLoop); - } - - if (pwStream) { - pw_stream_destroy(pwStream); - } - - if (pwCore) { - pw_core_disconnect(pwCore); - } - - if (pwContext) { - pw_context_destroy(pwContext); - } - - if (pwMainLoop) { - pw_thread_loop_destroy(pwMainLoop); - } } PWFrameBuffer::PWFrameBuffer(QObject *parent) : FrameBuffer (parent), d(new Private(this)) { - fb = nullptr; } PWFrameBuffer::~PWFrameBuffer() @@ -998,8 +474,7 @@ auto screencasting = new Screencasting(registry, wlname, version, this); auto r = screencasting->createVirtualMonitorStream(name, resolution, dpr, Screencasting::Metadata); connect(r, &ScreencastingStream::created, this, [this] (quint32 nodeId) { - d->pwStreamNodeId = nodeId; - d->initPw(); + d->stream->createStream(nodeId, 0); }); }); registry->create(connection); @@ -1013,11 +488,17 @@ int PWFrameBuffer::height() { + if (!d->videoSize.isValid()) { + return 0; + } return d->videoSize.height(); } int PWFrameBuffer::width() { + if (!d->videoSize.isValid()) { + return 0; + } return d->videoSize.width(); } @@ -1047,7 +528,7 @@ QVariant PWFrameBuffer::customProperty(const QString &property) const { if (property == QLatin1String("stream_node_id")) { - return QVariant::fromValue<uint>(d->pwStreamNodeId); + return QVariant::fromValue<uint>(d->stream->nodeId()); } if (property == QLatin1String("session_handle")) { return QVariant::fromValue<QDBusObjectPath>(d->sessionPath); } @@ -1062,5 +543,5 @@ QPoint PWFrameBuffer::cursorPosition() { - return d->cursorPosition; + return d->cursor->position; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/pipewire/pw_framebuffer.h new/krfb-23.04.0/framebuffers/pipewire/pw_framebuffer.h --- old/krfb-22.12.3/framebuffers/pipewire/pw_framebuffer.h 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/pipewire/pw_framebuffer.h 2023-04-12 18:47:19.000000000 +0200 @@ -50,10 +50,10 @@ bool isValid() const; private Q_SLOTS: - void handleXdpSessionCreated(quint32 code, QVariantMap results); - void handleXdpDevicesSelected(quint32 code, QVariantMap results); - void handleXdpSourcesSelected(quint32 code, QVariantMap results); - void handleXdpRemoteDesktopStarted(quint32 code, QVariantMap results); + void handleXdpSessionCreated(quint32 code, const QVariantMap &results); + void handleXdpDevicesSelected(quint32 code, const QVariantMap &results); + void handleXdpSourcesSelected(quint32 code, const QVariantMap &results); + void handleXdpRemoteDesktopStarted(quint32 code, const QVariantMap &results); private: class Private; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/pipewire/pw_framebufferplugin.cpp new/krfb-23.04.0/framebuffers/pipewire/pw_framebufferplugin.cpp --- old/krfb-22.12.3/framebuffers/pipewire/pw_framebufferplugin.cpp 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/pipewire/pw_framebufferplugin.cpp 2023-04-12 18:47:19.000000000 +0200 @@ -22,7 +22,7 @@ #include "pw_framebuffer.h" #include <KPluginFactory> -K_PLUGIN_CLASS(PWFrameBufferPlugin) +K_PLUGIN_CLASS_WITH_JSON(PWFrameBufferPlugin, "pipewire.json") PWFrameBufferPlugin::PWFrameBufferPlugin(QObject *parent, const QVariantList &args) : FrameBufferPlugin(parent, args) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/qt/qt.json new/krfb-23.04.0/framebuffers/qt/qt.json --- old/krfb-22.12.3/framebuffers/qt/qt.json 1970-01-01 01:00:00.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/qt/qt.json 2023-04-12 18:47:19.000000000 +0200 @@ -0,0 +1,6 @@ +{ + "X-KDE-OnlyShowOnQtPlatforms": [ + "xcb" + ] +} + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/qt/qtframebufferplugin.cpp new/krfb-23.04.0/framebuffers/qt/qtframebufferplugin.cpp --- old/krfb-22.12.3/framebuffers/qt/qtframebufferplugin.cpp 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/qt/qtframebufferplugin.cpp 2023-04-12 18:47:19.000000000 +0200 @@ -24,7 +24,7 @@ #include <KPluginFactory> -K_PLUGIN_CLASS(QtFrameBufferPlugin) +K_PLUGIN_CLASS_WITH_JSON(QtFrameBufferPlugin, "qt.json") QtFrameBufferPlugin::QtFrameBufferPlugin(QObject *parent, const QVariantList &args) : FrameBufferPlugin(parent, args) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/xcb/xcb.json new/krfb-23.04.0/framebuffers/xcb/xcb.json --- old/krfb-22.12.3/framebuffers/xcb/xcb.json 1970-01-01 01:00:00.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/xcb/xcb.json 2023-04-12 18:47:19.000000000 +0200 @@ -0,0 +1,6 @@ +{ + "X-KDE-OnlyShowOnQtPlatforms": [ + "xcb" + ] +} + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/framebuffers/xcb/xcb_framebufferplugin.cpp new/krfb-23.04.0/framebuffers/xcb/xcb_framebufferplugin.cpp --- old/krfb-22.12.3/framebuffers/xcb/xcb_framebufferplugin.cpp 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/framebuffers/xcb/xcb_framebufferplugin.cpp 2023-04-12 18:47:19.000000000 +0200 @@ -22,7 +22,7 @@ #include "xcb_framebuffer.h" #include <KPluginFactory> -K_PLUGIN_CLASS(XCBFrameBufferPlugin) +K_PLUGIN_CLASS_WITH_JSON(XCBFrameBufferPlugin, "xcb.json") XCBFrameBufferPlugin::XCBFrameBufferPlugin(QObject *parent, const QVariantList &args) : FrameBufferPlugin(parent, args) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/krfb/framebuffermanager.cpp new/krfb-23.04.0/krfb/framebuffermanager.cpp --- old/krfb-22.12.3/krfb/framebuffermanager.cpp 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/krfb/framebuffermanager.cpp 2023-04-12 18:47:19.000000000 +0200 @@ -24,6 +24,7 @@ #include "krfbconfig.h" #include "krfbdebug.h" +#include <QGuiApplication> #include <QGlobalStatic> #include <KPluginFactory> @@ -40,7 +41,10 @@ FrameBufferManager::FrameBufferManager() { - const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("krfb/framebuffer"), {}, KPluginMetaData::AllowEmptyMetaData); + const auto platformFilter = [] (const KPluginMetaData &pluginData) { + return pluginData.value(QStringLiteral("X-KDE-OnlyShowOnQtPlatforms"), QStringList()).contains(QGuiApplication::platformName()); + }; + const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("krfb/framebuffer"), platformFilter, KPluginMetaData::AllowEmptyMetaData); for (const KPluginMetaData &data : plugins) { const KPluginFactory::Result<FrameBufferPlugin> result = KPluginFactory::instantiatePlugin<FrameBufferPlugin>(data); if (result.plugin) { @@ -76,17 +80,21 @@ } } - // We don't already have that frame buffer. - for (auto it = m_plugins.cbegin(); it != m_plugins.constEnd(); it++) { - if (it.key() == KrfbConfig::preferredFrameBufferPlugin()) { + if (auto preferredPlugin = m_plugins.value(KrfbConfig::preferredFrameBufferPlugin())) { + if (auto frameBuffer = QSharedPointer<FrameBuffer>(preferredPlugin->frameBuffer(args))) { qCDebug(KRFB) << "Using FrameBuffer:" << KrfbConfig::preferredFrameBufferPlugin(); + m_frameBuffers.insert(id, frameBuffer.toWeakRef()); + return frameBuffer; + } + } - QSharedPointer<FrameBuffer> frameBuffer(it.value()->frameBuffer(args)); - if (frameBuffer) { - m_frameBuffers.insert(id, frameBuffer.toWeakRef()); - - return frameBuffer; - } + // We don't already have that frame buffer. + for (auto it = m_plugins.cbegin(); it != m_plugins.constEnd(); it++) { + QSharedPointer<FrameBuffer> frameBuffer(it.value()->frameBuffer(args)); + if (frameBuffer) { + qCDebug(KRFB) << "Using FrameBuffer:" << it.key(); + m_frameBuffers.insert(id, frameBuffer.toWeakRef()); + return frameBuffer; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/krfb/org.kde.krfb.appdata.xml new/krfb-23.04.0/krfb/org.kde.krfb.appdata.xml --- old/krfb-22.12.3/krfb/org.kde.krfb.appdata.xml 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/krfb/org.kde.krfb.appdata.xml 2023-04-12 18:47:19.000000000 +0200 @@ -160,9 +160,9 @@ </provides> <project_group>KDE</project_group> <releases> + <release version="23.04.0" date="2023-04-20"/> <release version="22.12.3" date="2023-03-02"/> <release version="22.12.2" date="2023-02-02"/> <release version="22.12.1" date="2023-01-05"/> - <release version="22.12.0" date="2022-12-08"/> </releases> </component> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/po/ar/krfb.po new/krfb-23.04.0/po/ar/krfb.po --- old/krfb-22.12.3/po/ar/krfb.po 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/po/ar/krfb.po 2023-04-12 18:47:19.000000000 +0200 @@ -174,7 +174,7 @@ #: main-virtualmonitor.cpp:93 main.cpp:111 #, kde-format msgid "KDE4 porting" -msgstr "اÙÙÙ٠إÙÙ ÙÙØ¯Ù4" +msgstr "اÙÙÙ٠إÙÙ ÙÙÙØ¯ÙÙ4" #: main-virtualmonitor.cpp:94 main.cpp:112 #, kde-format @@ -544,7 +544,7 @@ "KDE Desktop Sharing allows you to grant permission to someone at a remote " "location for viewing and possibly controlling your desktop." msgstr "" -"ØªØ´Ø§Ø±Ù Ø³Ø·Ø Ø§ÙÙ ÙØªØ¨ ÙÙÙÙØ¯Ù ÙØ³Ù Ø ÙÙ Ø¨Ø¯Ø¹ÙØ© شخص Ø¨Ø¹ÙØ¯ ÙÙ Ø´Ø§ÙØ¯Ø©Ø£Ù Ø§ÙØªØÙÙ Ø¨Ø³Ø·Ø Ù ÙØªØ¨Ù . " +"ØªØ´Ø§Ø±Ù Ø³Ø·Ø Ø§ÙÙ ÙØªØ¨ ÙÙÙÙÙØ¯ÙÙ ÙØ³Ù Ø ÙÙ Ø¨Ø¯Ø¹ÙØ© شخص Ø¨Ø¹ÙØ¯ ÙÙ Ø´Ø§ÙØ¯Ø©Ø£Ù Ø§ÙØªØÙÙ Ø¨Ø³Ø·Ø Ù ÙØªØ¨Ù . " "<a href=\"whatsthis\">اÙÙ Ø²ÙØ¯ Ø¹ÙØ¯ Ø§ÙØ¯Ø¹Ùات...</a>" #. i18n: ectx: property (whatsThis), widget (QCheckBox, enableSharingCheckBox) @@ -699,7 +699,7 @@ #~ "watch and possibly control your desktop. <a href=\"whatsthis\">More about " #~ "invitations...</a>" #~ msgstr "" -#~ "ØªØ´Ø§Ø±Ù Ø³Ø·Ø Ø§ÙÙ ÙØªØ¨ ÙÙÙÙØ¯Ù ÙØ³Ù Ø ÙÙ Ø¨Ø¯Ø¹ÙØ© شخص Ø¨Ø¹ÙØ¯ ÙÙ Ø´Ø§ÙØ¯Ø©Ø£Ù Ø§ÙØªØÙÙ Ø¨Ø³Ø·Ø " +#~ "ØªØ´Ø§Ø±Ù Ø³Ø·Ø Ø§ÙÙ ÙØªØ¨ ÙÙÙÙÙØ¯ÙÙ ÙØ³Ù Ø ÙÙ Ø¨Ø¯Ø¹ÙØ© شخص Ø¨Ø¹ÙØ¯ ÙÙ Ø´Ø§ÙØ¯Ø©Ø£Ù Ø§ÙØªØÙÙ Ø¨Ø³Ø·Ø " #~ "Ù ÙØªØ¨Ù . <a href=\"whatsthis\">اÙÙ Ø²ÙØ¯ Ø¹ÙØ¯ Ø§ÙØ¯Ø¹Ùات...</a>" #~ msgid "" @@ -836,8 +836,8 @@ #~ "\n" #~ "For security reasons this invitation will expire at %5 (%6)." #~ msgstr "" -#~ "ÙÙØ¯ Ø¯Ø¹ÙØª Ø¥ÙÙ Ø¬ÙØ³Ø© VNC Ø Ø¥Ø°Ø§ ÙØ§Ù ÙØ¯ÙÙ Ø¨Ø±ÙØ§Ù ج ÙØ¯Ù ÙÙØ£Ø³Ø·Ø Ø§ÙØ¨Ø¹Ùدة٠ا عÙÙÙ Ø¥ÙØ§ " -#~ "أ٠تÙÙØ± عÙÙ Ø§ÙØ±Ø§Ø¨Ø· Ø§ÙØ°Ù Ø¨Ø§ÙØ£Ø³ÙÙ.\n" +#~ "ÙÙØ¯ Ø¯Ø¹ÙØª Ø¥ÙÙ Ø¬ÙØ³Ø© VNC Ø Ø¥Ø°Ø§ ÙØ§Ù ÙØ¯ÙÙ Ø¨Ø±ÙØ§Ù ج ÙÙÙØ¯ÙÙ ÙÙØ£Ø³Ø·Ø Ø§ÙØ¨Ø¹Ùدة٠ا عÙÙÙ " +#~ "Ø¥ÙØ§ أ٠تÙÙØ± عÙÙ Ø§ÙØ±Ø§Ø¨Ø· Ø§ÙØ°Ù Ø¨Ø§ÙØ£Ø³ÙÙ.\n" #~ "\n" #~ "%1\n" #~ "\n" @@ -869,7 +869,7 @@ #~ msgstr "" #~ "Ù Ø´Ø§Ø±ÙØ© Ø³Ø·Ø Ø§ÙÙ ÙØªØ¨ باستخدا٠برتÙÙÙÙ VNC . ÙÙ ÙÙ٠استع٠ا٠أ٠ع٠ÙÙ VNC " #~ "ÙÙØ§ØªØµØ§Ù.\n" -#~ "Ù٠اÙÙÙØ¯Ù ÙØ³Ù Ù Ø§ÙØ¹Ù Ù٠ب٠'Ø§ØªØµØ§Ù Ø¨Ø³Ø·Ø Ù ÙØªØ¨ Ø¨Ø¹ÙØ¯' . ادخ٠٠عÙÙ٠ات اÙ٠ضÙÙ\n" +#~ "Ù٠اÙÙÙÙØ¯ÙÙ ÙØ³Ù Ù Ø§ÙØ¹Ù Ù٠ب٠'Ø§ØªØµØ§Ù Ø¨Ø³Ø·Ø Ù ÙØªØ¨ Ø¨Ø¹ÙØ¯' . ادخ٠٠عÙÙ٠ات اÙ٠ضÙÙ\n" #~ "ÙÙ Ø§ÙØ¹Ù ÙÙ Ù Ù٠سÙÙÙÙ Ø¨Ø§ÙØ§ØªØµØ§Ù.." #, fuzzy @@ -901,7 +901,7 @@ #~ "font-weight:400; font-style:normal; text-decoration:none;\">\n" #~ "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-" #~ "right:0px; -qt-block-indent:0; text-indent:0px;\">ÙØ³Ù Ø Ø¹Ù ÙÙ Ù Ø´Ø§Ø±ÙØ© Ø³Ø·Ø " -#~ "اÙÙ ÙØªØ¨ ÙÙÙÙØ¯ÙØ¨Ø¯Ø¹ÙØ© شخص ÙÙ Ù ÙØ§Ù Ø¨Ø¹ÙØ¯ ÙÙ Ø´Ø§ÙØ¯Ø© Ø³Ø·Ø Ù ÙØªØ¨ Ù Ø¨Ø§ÙØ¥Ù ÙØ§Ù Ø§ÙØªØÙÙ " +#~ "اÙÙ ÙØªØ¨ ÙÙÙÙÙØ¯ÙÙØ¨Ø¯Ø¹ÙØ© شخص ÙÙ Ù ÙØ§Ù Ø¨Ø¹ÙØ¯ ÙÙ Ø´Ø§ÙØ¯Ø© Ø³Ø·Ø Ù ÙØªØ¨ Ù Ø¨Ø§ÙØ¥Ù ÙØ§Ù Ø§ÙØªØÙÙ " #~ "ب٠. <a href=\"whatsthis\">اÙÙ Ø²ÙØ¯ Ø¹Ù Ø§ÙØ¯Ø¹Ùات...</a></p></body></html>" #~ msgid "Creation Time" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/po/ka/krfb.po new/krfb-23.04.0/po/ka/krfb.po --- old/krfb-22.12.3/po/ka/krfb.po 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/po/ka/krfb.po 2023-04-12 18:47:19.000000000 +0200 @@ -8,7 +8,7 @@ "Project-Id-Version: krfb\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-07-18 00:45+0000\n" -"PO-Revision-Date: 2023-02-13 09:12+0100\n" +"PO-Revision-Date: 2023-02-12 06:06+0100\n" "Last-Translator: Temuri Doghonadze <[email protected]>\n" "Language-Team: Georgian <[email protected]>\n" "Language: ka\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/po/tr/krfb.po new/krfb-23.04.0/po/tr/krfb.po --- old/krfb-22.12.3/po/tr/krfb.po 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/po/tr/krfb.po 2023-04-12 18:47:19.000000000 +0200 @@ -370,7 +370,7 @@ "Bu alan aralarında iki nokta üst üste karakteri olacak Åekilde " "bilgisayarınızın adını ve kapı numarasını içerir.\n" "\n" -"Bu adres sadece bir ipucu niteliÄi taÅır. Bilgisayarınıza eriÅmek için " +"Bu adres yalnızca bir ipucu niteliÄi taÅır. Bilgisayarınıza eriÅmek için " "herhangi bir adresi kullanabilirsiniz.\n" "\n" "Masaüstü PaylaÅımı aÄ ayarlarınıza bakarak adresinizi tahmin etmeye çalıÅır. " @@ -542,7 +542,7 @@ msgstr "" "EÄer bu seçenek açık ise, uzaktan baÄlanan kullanıcı klavyeyi ve fareyi " "kullanabilir. Bu onlara bilgisayarınız üstünde tam kontrol verir, bu yüzden " -"dikkatli olun. Sadece seyredebilen kullanıcılar için bu seçenek kapalı " +"dikkatli olun. Yalnızca seyredebilen kullanıcılar için bu seçenek kapalı " "olmalıdır." #. i18n: ectx: property (text), widget (QCheckBox, cbAllowRemoteControl) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krfb-22.12.3/po/zh_CN/krfb.po new/krfb-23.04.0/po/zh_CN/krfb.po --- old/krfb-22.12.3/po/zh_CN/krfb.po 2023-02-25 06:13:14.000000000 +0100 +++ new/krfb-23.04.0/po/zh_CN/krfb.po 2023-04-12 18:47:19.000000000 +0200 @@ -3,7 +3,7 @@ "Project-Id-Version: kdeorg\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-07-18 00:45+0000\n" -"PO-Revision-Date: 2023-02-24 12:58\n" +"PO-Revision-Date: 2023-04-10 14:13\n" "Last-Translator: \n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n"
