Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package krdp6 for openSUSE:Factory checked in at 2026-10-29 16:05:49 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/krdp6 (Old) and /work/SRC/openSUSE:Factory/.krdp6.new.1255 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "krdp6" Thu Oct 29 16:05:49 2026 rev:47 rq:1393314 version:6.8.2 Changes: -------- --- /work/SRC/openSUSE:Factory/krdp6/krdp6.changes 2026-10-22 15:50:49.964452393 +0200 +++ /work/SRC/openSUSE:Factory/.krdp6.new.1255/krdp6.changes 2026-10-29 16:09:28.039929808 +0100 @@ -1,0 +2,14 @@ +Tue Oct 27 18:09:58 UTC 2026 - Christophe Marin <[email protected]> + +- Update to 6.8.2 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/plasma/6/6.8.2 +- Changes since 6.8.1: + * Update version for new release 6.8.2 + * VideoStream: Don't track progressive frames while acknowledgements are suspended + * PortalSession: listen for the portal response before making the request (kde#527555) + * PlasmaScreencastSession: Port to libei + * VideoStream: Start from the client's requested desktop size + +------------------------------------------------------------------- Old: ---- krdp-6.8.1.tar.xz krdp-6.8.1.tar.xz.sig New: ---- krdp-6.8.2.tar.xz krdp-6.8.2.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ krdp6.spec ++++++ --- /var/tmp/diff_new_pack.kMnT1K/_old 2026-10-29 16:09:28.666956103 +0100 +++ /var/tmp/diff_new_pack.kMnT1K/_new 2026-10-29 16:09:28.668956187 +0100 @@ -27,7 +27,7 @@ %{!?_plasma6_version: %define _plasma6_version %(echo %{_plasma6_bugfix} | awk -F. '{print $1"."$2}')} %bcond_without released Name: krdp6 -Version: 6.8.1 +Version: 6.8.2 Release: 0 Summary: RDP Server for Plasma License: LGPL-2.1-or-later ++++++ krdp-6.8.1.tar.xz -> krdp-6.8.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.8.1/CMakeLists.txt new/krdp-6.8.2/CMakeLists.txt --- old/krdp-6.8.1/CMakeLists.txt 2026-10-20 11:04:25.000000000 +0200 +++ new/krdp-6.8.2/CMakeLists.txt 2026-10-27 12:26:32.000000000 +0100 @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2023 Arjen Hiemstra <[email protected]> # SPDX-License-Identifier: BSD-2-Clause -set(PROJECT_VERSION "6.8.1") +set(PROJECT_VERSION "6.8.2") cmake_minimum_required(VERSION 3.16) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.8.1/src/PlasmaScreencastV1Session.cpp new/krdp-6.8.2/src/PlasmaScreencastV1Session.cpp --- old/krdp-6.8.1/src/PlasmaScreencastV1Session.cpp 2026-10-20 11:04:25.000000000 +0200 +++ new/krdp-6.8.2/src/PlasmaScreencastV1Session.cpp 2026-10-27 12:26:32.000000000 +0100 @@ -4,148 +4,20 @@ #include "PlasmaScreencastV1Session.h" -#include <QGuiApplication> -#include <QMouseEvent> -#include <QQueue> -#include <QWaylandClientExtensionTemplate> -#include <qpa/qplatformnativeinterface.h> - -#include <linux/input-event-codes.h> -#include <sys/mman.h> -#include <unistd.h> -#include <xkbcommon/xkbcommon.h> - +#include "EiConnection.h" #include "krdp_logging.h" -#include "qwayland-fake-input.h" -#include "qwayland-wayland.h" #include "screencasting_p.h" +#include <QDBusConnection> +#include <QDBusMessage> +#include <QDBusPendingCallWatcher> +#include <QDBusPendingReply> +#include <QDBusUnixFileDescriptor> +#include <QGuiApplication> -namespace KRdp -{ +using namespace Qt::StringLiterals; -class FakeInput : public QWaylandClientExtensionTemplate<FakeInput>, public QtWayland::org_kde_kwin_fake_input -{ -public: - FakeInput() - : QWaylandClientExtensionTemplate<FakeInput>(4) - { - initialize(); - Q_ASSERT(isActive()); - } -}; - -namespace -{ -struct XKBStateDeleter { - void operator()(struct xkb_state *state) const - { - xkb_state_unref(state); - } -}; -struct XKBKeymapDeleter { - void operator()(struct xkb_keymap *keymap) const - { - xkb_keymap_unref(keymap); - } -}; -struct XKBContextDeleter { - void operator()(struct xkb_context *context) const - { - xkb_context_unref(context); - } -}; -using ScopedXKBState = std::unique_ptr<struct xkb_state, XKBStateDeleter>; -using ScopedXKBKeymap = std::unique_ptr<struct xkb_keymap, XKBKeymapDeleter>; -using ScopedXKBContext = std::unique_ptr<struct xkb_context, XKBContextDeleter>; -} -class Xkb : public QtWayland::wl_keyboard +namespace KRdp { -public: - struct Code { - const uint32_t level; - const uint32_t code; - }; - std::optional<Code> keycodeFromKeysym(xkb_keysym_t keysym) - { - /* The offset between KEY_* numbering, and keycodes in the XKB evdev - * dataset. */ - static const uint EVDEV_OFFSET = 8; - - auto layout = xkb_state_serialize_layout(m_state.get(), XKB_STATE_LAYOUT_EFFECTIVE); - const xkb_keycode_t max = xkb_keymap_max_keycode(m_keymap.get()); - for (xkb_keycode_t keycode = xkb_keymap_min_keycode(m_keymap.get()); keycode < max; keycode++) { - uint levelCount = xkb_keymap_num_levels_for_key(m_keymap.get(), keycode, layout); - for (uint currentLevel = 0; currentLevel < levelCount; currentLevel++) { - const xkb_keysym_t *syms; - uint num_syms = xkb_keymap_key_get_syms_by_level(m_keymap.get(), keycode, layout, currentLevel, &syms); - for (uint sym = 0; sym < num_syms; sym++) { - if (syms[sym] == keysym) { - return Code{currentLevel, keycode - EVDEV_OFFSET}; - } - } - } - } - return {}; - } - - static Xkb *self() - { - static Xkb self; - return &self; - } - -private: - Xkb() - { - m_ctx.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); - if (!m_ctx) { - qCWarning(KRDP) << "Failed to create xkb context"; - return; - } - m_keymap.reset(xkb_keymap_new_from_names(m_ctx.get(), nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS)); - if (!m_keymap) { - qCWarning(KRDP) << "Failed to create the keymap"; - return; - } - m_state.reset(xkb_state_new(m_keymap.get())); - if (!m_state) { - qCWarning(KRDP) << "Failed to create the xkb state"; - return; - } - - QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); - auto seat = static_cast<wl_seat *>(nativeInterface->nativeResourceForIntegration("wl_seat")); - init(wl_seat_get_keyboard(seat)); - } - - void keyboard_keymap(uint32_t format, int32_t fd, uint32_t size) override - { - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - qCWarning(KRDP) << "unknown keymap format:" << format; - close(fd); - return; - } - - char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0)); - if (map_str == MAP_FAILED) { - close(fd); - return; - } - - m_keymap.reset(xkb_keymap_new_from_string(m_ctx.get(), map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS)); - munmap(map_str, size); - close(fd); - - if (m_keymap) - m_state.reset(xkb_state_new(m_keymap.get())); - else - m_state.reset(nullptr); - } - - ScopedXKBContext m_ctx; - ScopedXKBKeymap m_keymap; - ScopedXKBState m_state; -}; class KRDP_NO_EXPORT PlasmaScreencastV1Session::Private { @@ -154,19 +26,46 @@ Screencasting m_screencasting; std::unique_ptr<ScreencastingStream> request; - std::unique_ptr<FakeInput> remoteInterface; + std::unique_ptr<EiConnection> eiConnection; + int eisCookie = 0; }; PlasmaScreencastV1Session::PlasmaScreencastV1Session() : AbstractSession() , d(std::make_unique<Private>()) { - d->remoteInterface = std::make_unique<FakeInput>(); - d->remoteInterface->authenticate(QStringLiteral("krdpserver"), QString()); + constexpr auto kwinService = "org.kde.KWin"_L1; + constexpr auto kwinRemoteDesktopPath = "/org/kde/KWin/EIS/RemoteDesktop"_L1; + constexpr auto kwinRemoteDesktopInterface = "org.kde.KWin.EIS.RemoteDesktop"_L1; + auto msg = QDBusMessage::createMethodCall(kwinService, kwinRemoteDesktopPath, kwinRemoteDesktopInterface, "connectToEIS"_L1); + msg.setArguments({7}); // Keyboard | Pointer | TouchScreen + auto *watcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall(msg), this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + + QDBusPendingReply<QDBusUnixFileDescriptor, int> reply = *watcher; + if (reply.isError()) { + qCWarning(KRDP) << "Failed to connect to EIS:" << reply.error(); + return; + } + d->eisCookie = reply.argumentAt<1>(); + d->eiConnection = std::make_unique<EiConnection>(reply.argumentAt<0>().takeFileDescriptor(), this); + if (!d->eiConnection->isValid()) { + qCWarning(KRDP) << "Failed to create EIS connection"; + } + }); } PlasmaScreencastV1Session::~PlasmaScreencastV1Session() { + if (d->eisCookie) { + constexpr auto kwinService = "org.kde.KWin"_L1; + constexpr auto kwinRemoteDesktopPath = "/org/kde/KWin/EIS/RemoteDesktop"_L1; + constexpr auto kwinRemoteDesktopInterface = "org.kde.KWin.EIS.RemoteDesktop"_L1; + auto msg = QDBusMessage::createMethodCall(kwinService, kwinRemoteDesktopPath, kwinRemoteDesktopInterface, QStringLiteral("disconnect")); + msg.setArguments({d->eisCookie}); + QDBusConnection::sessionBus().send(msg); + } qCDebug(KRDP) << "Closing Plasma Remote Session"; } @@ -200,87 +99,8 @@ void PlasmaScreencastV1Session::sendEvent(const std::shared_ptr<QEvent> &event) { - if (!isStarted()) { - return; - } - - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: { - auto me = std::static_pointer_cast<QMouseEvent>(event); - int button = 0; - if (me->button() == Qt::LeftButton) { - button = BTN_LEFT; - } else if (me->button() == Qt::MiddleButton) { - button = BTN_MIDDLE; - } else if (me->button() == Qt::RightButton) { - button = BTN_RIGHT; - } else { - qCWarning(KRDP) << "Unsupported mouse button" << me->button(); - return; - } - uint state = me->type() == QEvent::MouseButtonPress ? 1 : 0; - d->remoteInterface->button(button, state); - break; - } - case QEvent::MouseMove: { - auto me = std::static_pointer_cast<QMouseEvent>(event); - auto position = me->position(); - auto logicalPosition = QPointF{(position.x() / size().width()) * logicalSize().width(), (position.y() / size().height()) * logicalSize().height()}; - d->remoteInterface->pointer_motion_absolute(wl_fixed_from_double(logicalPosition.x()), wl_fixed_from_double(logicalPosition.y())); - break; - } - case QEvent::Wheel: { - auto we = std::static_pointer_cast<QWheelEvent>(event); - auto delta = we->pixelDelta(); - // pixelDelta contains the scroll value already converted to - // degrees by InputHandler, preserving sub-notch precision - // from clients that send high-resolution wheel values - // (e.g. Microsoft Remote Desktop). - if (delta.y() != 0) { - d->remoteInterface->axis(WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_from_double(delta.y())); - } - if (delta.x() != 0) { - d->remoteInterface->axis(WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_double(delta.x())); - } - break; - } - case QEvent::KeyPress: - case QEvent::KeyRelease: { - auto ke = std::static_pointer_cast<QKeyEvent>(event); - auto state = ke->type() == QEvent::KeyPress ? 1 : 0; - - if (ke->nativeScanCode()) { - d->remoteInterface->keyboard_key(ke->nativeScanCode(), state); - } else { - auto keycode = Xkb::self()->keycodeFromKeysym(ke->nativeVirtualKey()); - if (!keycode) { - qCWarning(KRDP) << "Failed to convert keysym into keycode" << ke->nativeVirtualKey(); - return; - } - - auto sendKey = [this, state](int keycode) { - d->remoteInterface->keyboard_key(keycode, state); - }; - switch (keycode->level) { - case 0: - break; - case 1: - sendKey(KEY_LEFTSHIFT); - break; - case 2: - sendKey(KEY_RIGHTALT); - break; - default: - qCWarning(KRDP) << "Unsupported key level" << keycode->level; - break; - } - sendKey(keycode->code); - } - break; - } - default: - break; + if (isStarted() && d->eiConnection) { + d->eiConnection->sendEvent(event, size(), {}); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.8.1/src/PlasmaScreencastV1Session.h new/krdp-6.8.2/src/PlasmaScreencastV1Session.h --- old/krdp-6.8.1/src/PlasmaScreencastV1Session.h 2026-10-20 11:04:25.000000000 +0200 +++ new/krdp-6.8.2/src/PlasmaScreencastV1Session.h 2026-10-27 12:26:32.000000000 +0100 @@ -9,7 +9,6 @@ #include <optional> #include <type_traits> -#include <QDBusPendingCallWatcher> #include <QPoint> #include <QPointer> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.8.1/src/PortalSession.cpp new/krdp-6.8.2/src/PortalSession.cpp --- old/krdp-6.8.1/src/PortalSession.cpp 2026-10-20 11:04:25.000000000 +0200 +++ new/krdp-6.8.2/src/PortalSession.cpp 2026-10-27 12:26:32.000000000 +0100 @@ -47,15 +47,37 @@ return arg; } -void PortalRequest::onStarted(QDBusPendingCallWatcher *watcher) +QString PortalRequest::requestPath(const QString &handleToken) { - QDBusPendingReply<QDBusObjectPath> reply = *watcher; - if (!reply.isError()) { - QDBusConnection::sessionBus().connect(QString{}, reply.value().path(), dbusRequestInterface, dbusResponse, this, SLOT(onFinished(uint, QVariantMap))); - } else { + // The portal derives the request path from the caller's unique name, e.g. ":1.42" -> "1_42" + QString sender = QDBusConnection::sessionBus().baseService().mid(1); + sender.replace(u'.', u'_'); + return QStringLiteral("/org/freedesktop/portal/desktop/request/%1/%2").arg(sender, handleToken); +} + +void PortalRequest::listen(const QString &path) +{ + if (!m_path.isEmpty()) { + QDBusConnection::sessionBus().disconnect(QString{}, m_path, dbusRequestInterface, dbusResponse, this, SLOT(onFinished(uint, QVariantMap))); + } + m_path = path; + QDBusConnection::sessionBus().connect(QString{}, m_path, dbusRequestInterface, dbusResponse, this, SLOT(onFinished(uint, QVariantMap))); +} + +void PortalRequest::start(const QDBusPendingCall &call) +{ + QDBusPendingReply<QDBusObjectPath> reply = call; + reply.waitForFinished(); + if (reply.isError()) { m_callback(-1, {{QStringLiteral("errorMessage"), reply.error().message()}}); + deleteLater(); + return; + } + + // Portals older than 0.9 don't derive the request path from the handle token + if (reply.value().path() != m_path) { + listen(reply.value().path()); } - watcher->deleteLater(); } void PortalRequest::onFinished(uint code, const QVariantMap &result) @@ -115,11 +137,13 @@ { qCDebug(KRDP) << "Initializing Freedesktop Portal Session"; + const QString handleToken = createHandleToken(); auto parameters = QVariantMap{ - {QStringLiteral("handle_token"), createHandleToken()}, + {QStringLiteral("handle_token"), handleToken}, {QStringLiteral("session_handle_token"), createHandleToken()}, }; - new PortalRequest(d->remoteInterface->CreateSession(parameters), this, &PortalSession::onCreateSession); + auto request = new PortalRequest(handleToken, this, &PortalSession::onCreateSession); + request->start(d->remoteInterface->CreateSession(parameters)); } void PortalSession::sendEvent(const std::shared_ptr<QEvent> &event) @@ -141,9 +165,10 @@ static const uint PermissionsPersistUntilExplicitlyRevoked = 2; + const QString handleToken = createHandleToken(); auto parameters = QVariantMap{ {QStringLiteral("types"), 7u}, - {QStringLiteral("handle_token"), createHandleToken()}, + {QStringLiteral("handle_token"), handleToken}, {QStringLiteral("persist_mode"), PermissionsPersistUntilExplicitlyRevoked}, }; // name is set explicitly as this is also used by the KCM @@ -161,7 +186,8 @@ parameters[QStringLiteral("restore_token")] = restoreToken; } - new PortalRequest(d->remoteInterface->SelectDevices(d->sessionPath, parameters), this, &PortalSession::onDevicesSelected); + auto request = new PortalRequest(handleToken, this, &PortalSession::onDevicesSelected); + request->start(d->remoteInterface->SelectDevices(d->sessionPath, parameters)); } void PortalSession::onDevicesSelected(uint code, const QVariantMap & /*result*/) @@ -181,7 +207,10 @@ } parameters[QStringLiteral("cursor_mode")] = 4u; // Metadata - new PortalRequest(d->screencastInterface->SelectSources(d->sessionPath, parameters), this, &PortalSession::onSourcesSelected); + const QString handleToken = createHandleToken(); + parameters[QStringLiteral("handle_token")] = handleToken; + auto request = new PortalRequest(handleToken, this, &PortalSession::onSourcesSelected); + request->start(d->screencastInterface->SelectSources(d->sessionPath, parameters)); } void PortalSession::onSourcesSelected(uint code, const QVariantMap & /*result*/) @@ -192,10 +221,12 @@ return; } + const QString handleToken = createHandleToken(); auto parameters = QVariantMap{ - {QStringLiteral("handle_token"), createHandleToken()}, + {QStringLiteral("handle_token"), handleToken}, }; - new PortalRequest(d->remoteInterface->Start(d->sessionPath, QString{}, parameters), this, &PortalSession::onSessionStarted); + auto request = new PortalRequest(handleToken, this, &PortalSession::onSessionStarted); + request->start(d->remoteInterface->Start(d->sessionPath, QString{}, parameters)); } void KRdp::PortalSession::onSessionStarted(uint code, const QVariantMap &result) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.8.1/src/PortalSession_p.h new/krdp-6.8.2/src/PortalSession_p.h --- old/krdp-6.8.1/src/PortalSession_p.h 2026-10-20 11:04:25.000000000 +0200 +++ new/krdp-6.8.2/src/PortalSession_p.h 2026-10-27 12:26:32.000000000 +0100 @@ -25,13 +25,17 @@ * A request to the portal requires first calling a method on the portal, which * will return an object path that should be listened to for a signal. The * signal provides the actual proper data for the original method call. + * + * The portal can send that signal before the method call returns, so the + * request listens on the path derived from the handle token before the call + * is made: create the request with the token, then pass the call to start(). */ class PortalRequest : public QObject { Q_OBJECT public: template<typename ContextType, typename Callback> - PortalRequest(const QDBusPendingCall &call, ContextType *context, Callback callback) + PortalRequest(const QString &handleToken, ContextType *context, Callback callback) : m_context(context) { if constexpr (std::is_member_function_pointer<Callback>::value) { @@ -40,17 +44,21 @@ m_callback = callback; } - auto watcher = new QDBusPendingCallWatcher(call); - // We need to wait here because otherwise we risk the signal arriving - // before we know what path to listen on. - watcher->waitForFinished(); - onStarted(watcher); + listen(requestPath(handleToken)); } + /** + * Start the request with a portal method call that was given the same + * handle token as the constructor. + */ + void start(const QDBusPendingCall &call); + private: - void onStarted(QDBusPendingCallWatcher *watcher); + static QString requestPath(const QString &handleToken); + void listen(const QString &path); Q_SLOT void onFinished(uint code, const QVariantMap &result); + QString m_path; QPointer<QObject> m_context; std::function<void(uint, const QVariantMap &)> m_callback; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.8.1/src/VideoStream.cpp new/krdp-6.8.2/src/VideoStream.cpp --- old/krdp-6.8.1/src/VideoStream.cpp 2026-10-20 11:04:25.000000000 +0200 +++ new/krdp-6.8.2/src/VideoStream.cpp 2026-10-27 12:26:32.000000000 +0100 @@ -324,6 +324,16 @@ return false; } + // Clients like mstsc only send a monitor layout when their window changes, so start + // from the desktop size the client asked for when connecting. + if (!d->surface->requestedSize.isValid()) { + auto settings = d->session->rdpPeerContext()->settings; + const QSize clientSize(freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)); + if (!clientSize.isEmpty()) { + setRequestedSize(clientSize); + } + } + d->initialized = true; connect(d->session->networkDetection(), &NetworkDetection::rttChanged, this, &VideoStream::updateInFlightWindow); @@ -1038,7 +1048,9 @@ { std::lock_guard lock(d->pendingFramesMutex); - d->pendingFrames.insert(frameId); + if (!d->frameAcknowledgementSuspended) { + d->pendingFrames.insert(frameId); + } } RDPGFX_START_FRAME_PDU startFramePdu;
