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-03-04 21:04:29 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/krdp6 (Old) and /work/SRC/openSUSE:Factory/.krdp6.new.561 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "krdp6" Wed Mar 4 21:04:29 2026 rev:35 rq:1336099 version:6.6.2 Changes: -------- --- /work/SRC/openSUSE:Factory/krdp6/krdp6.changes 2026-02-27 17:09:06.270112260 +0100 +++ /work/SRC/openSUSE:Factory/.krdp6.new.561/krdp6.changes 2026-03-04 21:04:33.702807314 +0100 @@ -1,0 +2,14 @@ +Tue Mar 3 12:26:45 UTC 2026 - Fabian Vogt <[email protected]> + +- Update to 6.6.2: + * New bugfix release + * For more details see https://kde.org/announcements/plasma/6/6.6.2 +- Changes since 6.6.1: + * Update version for new release 6.6.2 + * Fix thread safety issues in VideoStream + * Fix potential deadlock in RdpConnection destructor + * Fix PortalSession destructor crash and resource leak + * Handle GFX CapsAdvertise re-advertisement from Windows clients + * Fix scroll wheel for RDP clients sending high-resolution or zero-position events (kde#511029) + +------------------------------------------------------------------- Old: ---- krdp-6.6.1.tar.xz krdp-6.6.1.tar.xz.sig New: ---- krdp-6.6.2.tar.xz krdp-6.6.2.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ krdp6.spec ++++++ --- /var/tmp/diff_new_pack.bNX7Bq/_old 2026-03-04 21:04:34.350834089 +0100 +++ /var/tmp/diff_new_pack.bNX7Bq/_new 2026-03-04 21:04:34.350834089 +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.6.1 +Version: 6.6.2 Release: 0 Summary: RDP Server for Plasma License: LGPL-2.1-or-later ++++++ krdp-6.6.1.tar.xz -> krdp-6.6.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.6.1/CMakeLists.txt new/krdp-6.6.2/CMakeLists.txt --- old/krdp-6.6.1/CMakeLists.txt 2026-02-24 10:38:15.000000000 +0100 +++ new/krdp-6.6.2/CMakeLists.txt 2026-03-03 10:31:18.000000000 +0100 @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2023 Arjen Hiemstra <[email protected]> # SPDX-License-Identifier: BSD-2-Clause -set(PROJECT_VERSION "6.6.1") +set(PROJECT_VERSION "6.6.2") cmake_minimum_required(VERSION 3.16) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.6.1/po/nn/kcm_krdpserver.po new/krdp-6.6.2/po/nn/kcm_krdpserver.po --- old/krdp-6.6.1/po/nn/kcm_krdpserver.po 2026-02-24 10:38:15.000000000 +0100 +++ new/krdp-6.6.2/po/nn/kcm_krdpserver.po 2026-03-03 10:31:18.000000000 +0100 @@ -5,7 +5,7 @@ "Project-Id-Version: krdp\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2026-01-17 02:38+0000\n" -"PO-Revision-Date: 2024-08-08 22:05+0200\n" +"PO-Revision-Date: 2026-03-01 19:51+0100\n" "Last-Translator: Karl Ove Hufthammer <[email protected]>\n" "Language-Team: Norwegian Nynorsk <[email protected]>\n" "Language: nn\n" @@ -13,7 +13,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Lokalize 24.11.70\n" +"X-Generator: Lokalize 26.03.70\n" "X-Environment: kde\n" "X-Accelerator-Marker: &\n" "X-Text-Markup: kde4\n" @@ -58,7 +58,7 @@ #: krdpserversettings.kcfg:35 #, kde-format msgid "Whether the current user can log in via PAM" -msgstr "" +msgstr "Om gjeldande brukar kan logga inn via PAM" #. i18n: ectx: label, entry (Autostart), group (General) #: krdpserversettings.kcfg:39 @@ -165,7 +165,7 @@ #, kde-format msgctxt "@info:status" msgid "Systemd not found. krdpserver will require manual activation." -msgstr "" +msgstr "Fann ikkje Systemd. krdpserver krev manuell aktivering." #: ui/main.qml:129 #, kde-kuit-format @@ -273,23 +273,23 @@ msgstr "" #: ui/main_phone.qml:105 -#, fuzzy, kde-format +#, kde-format msgctxt "@option:check" msgid "Enable RDP Server" msgstr "Bruk RDP-tenar" #: ui/main_phone.qml:128 -#, fuzzy, kde-format +#, kde-format msgid "Hostname:" -msgstr "Brukarnamn:" +msgstr "Vertsnamn:" #: ui/main_phone.qml:134 -#, fuzzy, kde-format +#, kde-format msgid "Use any of the following addresses to connect to this device:" msgstr "Bruk ei av desse adressene til å kopla til eininga:" #: ui/main_phone.qml:172 -#, fuzzy, kde-format +#, kde-format msgctxt "@title:group" msgid "Server Settings" msgstr "Tenarinnstillingar" @@ -343,19 +343,19 @@ #, kde-format msgctxt "@title:group" msgid "System Users" -msgstr "" +msgstr "Systembrukarar" #: ui/UserListView.qml:49 #, kde-format msgctxt "@title:group" msgid "Other Users" -msgstr "" +msgstr "Andre brukarar" #: ui/UserListView.qml:67 #, kde-format msgctxt "@info:usagetip used as a subtitle for a title+subtitle list item" msgid "Login with your system password" -msgstr "" +msgstr "Logg inn med systempassord" #: ui/UserListView.qml:99 #, kde-format diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.6.1/src/InputHandler.cpp new/krdp-6.6.2/src/InputHandler.cpp --- old/krdp-6.6.1/src/InputHandler.cpp 2026-02-24 10:38:15.000000000 +0100 +++ new/krdp-6.6.2/src/InputHandler.cpp 2026-03-03 10:31:18.000000000 +0100 @@ -75,6 +75,7 @@ public: RdpConnection *session; rdpInput *input; + QPointF lastMousePosition; }; InputHandler::InputHandler(KRdp::RdpConnection *session) @@ -108,6 +109,12 @@ { QPointF position = QPointF(x, y); + // Track last known mouse position for wheel events from clients + // that send (0,0) as the position (e.g. Microsoft Remote Desktop) + if (flags & PTR_FLAGS_MOVE) { + d->lastMousePosition = position; + } + Qt::MouseButton button = Qt::NoButton; if (flags & PTR_FLAGS_BUTTON1) { button = Qt::LeftButton; @@ -118,19 +125,39 @@ } if (flags & PTR_FLAGS_WHEEL || flags & PTR_FLAGS_HWHEEL) { + // Use last known mouse position if the client sends (0,0) + if (position.isNull() && !d->lastMousePosition.isNull()) { + position = d->lastMousePosition; + } auto axis = flags & WheelRotationMask; if (axis & PTR_FLAGS_WHEEL_NEGATIVE) { axis = (~axis & WheelRotationMask) + 1; } axis *= flags & PTR_FLAGS_WHEEL_NEGATIVE ? 1 : -1; + // The RDP protocol uses 120 units per standard wheel notch + // (15 degrees of rotation). Dividing by 8 converts to degrees, + // which we store in pixelDelta as a continuous scroll value. + auto degrees = axis / 8.0; if (flags & PTR_FLAGS_WHEEL) { - auto event = - std::make_shared<QWheelEvent>(position, QPointF{}, QPoint{}, QPoint{0, axis}, Qt::NoButton, Qt::KeyboardModifiers{}, Qt::NoScrollPhase, false); + auto event = std::make_shared<QWheelEvent>(position, + QPointF{}, + QPoint{0, qRound(degrees)}, + QPoint{0, axis}, + Qt::NoButton, + Qt::KeyboardModifiers{}, + Qt::NoScrollPhase, + false); Q_EMIT inputEvent(event); } if (flags & PTR_FLAGS_HWHEEL) { - auto event = - std::make_shared<QWheelEvent>(position, QPointF{}, QPoint{}, QPoint{-axis, 0}, Qt::NoButton, Qt::KeyboardModifiers{}, Qt::NoScrollPhase, false); + auto event = std::make_shared<QWheelEvent>(position, + QPointF{}, + QPoint{qRound(-degrees), 0}, + QPoint{-axis, 0}, + Qt::NoButton, + Qt::KeyboardModifiers{}, + Qt::NoScrollPhase, + false); Q_EMIT inputEvent(event); } return true; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.6.1/src/PlasmaScreencastV1Session.cpp new/krdp-6.6.2/src/PlasmaScreencastV1Session.cpp --- old/krdp-6.6.1/src/PlasmaScreencastV1Session.cpp 2026-02-24 10:38:15.000000000 +0100 +++ new/krdp-6.6.2/src/PlasmaScreencastV1Session.cpp 2026-03-03 10:31:18.000000000 +0100 @@ -229,12 +229,16 @@ } case QEvent::Wheel: { auto we = std::static_pointer_cast<QWheelEvent>(event); - auto delta = we->angleDelta(); + 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, delta.y() / 120); + 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, delta.x() / 120); + d->remoteInterface->axis(WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_double(delta.x())); } break; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.6.1/src/PortalSession.cpp new/krdp-6.6.2/src/PortalSession.cpp --- old/krdp-6.6.1/src/PortalSession.cpp 2026-02-24 10:38:15.000000000 +0100 +++ new/krdp-6.6.2/src/PortalSession.cpp 2026-03-03 10:31:18.000000000 +0100 @@ -127,6 +127,11 @@ PortalSession::~PortalSession() { + if (d->sessionPath.path().isEmpty()) { + qCDebug(KRDP) << "No portal session to close (session was never created)"; + return; + } + // Make sure to clear any modifier keys that were pressed when the session closed, otherwise // we risk those keys getting stuck and the original session becoming unusable. for (auto keycode : {KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA}) { @@ -186,13 +191,12 @@ } case QEvent::Wheel: { auto we = std::static_pointer_cast<QWheelEvent>(event); - auto delta = we->angleDelta(); - if (delta.y() != 0) { - d->remoteInterface->NotifyPointerAxisDiscrete(d->sessionPath, QVariantMap{}, 0 /* Vertical */, delta.y() / 120); - } - if (delta.x() != 0) { - d->remoteInterface->NotifyPointerAxisDiscrete(d->sessionPath, QVariantMap{}, 1 /* Horizontal */, delta.x() / 120); - } + auto delta = we->pixelDelta(); + // pixelDelta contains the scroll value already converted to + // degrees by InputHandler. The vertical axis is negated to + // account for the sign convention in + // xdg-desktop-portal-kde's requestPointerAxis. + d->remoteInterface->NotifyPointerAxis(d->sessionPath, QVariantMap{}, delta.x(), -delta.y()); break; } case QEvent::KeyPress: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.6.1/src/RdpConnection.cpp new/krdp-6.6.2/src/RdpConnection.cpp --- old/krdp-6.6.1/src/RdpConnection.cpp 2026-02-24 10:38:15.000000000 +0100 +++ new/krdp-6.6.2/src/RdpConnection.cpp 2026-03-03 10:31:18.000000000 +0100 @@ -237,7 +237,11 @@ RdpConnection::~RdpConnection() { - if (d->state == State::Streaming) { + // Close the peer first to unblock WaitForMultipleObjects in the + // run thread. Without this, thread.join() can deadlock when the + // session is not in the Streaming state but the thread is still + // blocked waiting for socket events. + if (d->peer) { d->peer->Close(d->peer); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/krdp-6.6.1/src/VideoStream.cpp new/krdp-6.6.2/src/VideoStream.cpp --- old/krdp-6.6.1/src/VideoStream.cpp 2026-02-24 10:38:15.000000000 +0100 +++ new/krdp-6.6.2/src/VideoStream.cpp 2026-03-03 10:31:18.000000000 +0100 @@ -128,8 +128,10 @@ QQueue<VideoFrame> frameQueue; QSet<uint32_t> pendingFrames; + std::mutex pendingFramesMutex; + int maximumFrameRate = 120; - int requestedFrameRate = 60; + std::atomic_int requestedFrameRate = 60; QQueue<FrameRateEstimate> frameRateEstimates; clk::system_clock::time_point lastFrameRateEstimation; @@ -146,6 +148,7 @@ VideoStream::~VideoStream() { + close(); } bool VideoStream::initialize() @@ -179,6 +182,14 @@ d->frameSubmissionThread = std::jthread([this](std::stop_token token) { while (!token.stop_requested()) { + // Don't dequeue frames until the GFX channel is ready. + // This preserves the I-frame (keyframe) in the queue until + // CapsAdvertise has completed and we can actually send it. + if (!d->gfxContext || !d->capsConfirmed) { + std::this_thread::sleep_for(std::chrono::milliseconds(16)); + continue; + } + VideoFrame nextFrame; { std::unique_lock lock(d->frameQueueMutex); @@ -187,7 +198,7 @@ } } if (nextFrame.size.isEmpty()) { - std::this_thread::sleep_for(std::chrono::milliseconds(1000) / d->requestedFrameRate); + std::this_thread::sleep_for(std::chrono::milliseconds(1000) / d->requestedFrameRate.load()); continue; } sendFrame(nextFrame); @@ -205,13 +216,25 @@ return; } - d->gfxContext->Close(d->gfxContext.get()); - + // Stop the frame submission thread first to prevent + // use-after-free on gfxContext during close. if (d->frameSubmissionThread.joinable()) { d->frameSubmissionThread.request_stop(); d->frameSubmissionThread.join(); } + { + std::lock_guard lock(d->pendingFramesMutex); + d->pendingFrames.clear(); + } + { + std::lock_guard lock(d->frameQueueMutex); + d->frameQueue.clear(); + } + + d->gfxContext->Close(d->gfxContext.get()); + d->gfxContext.reset(); + Q_EMIT closed(); } @@ -259,6 +282,18 @@ uint32_t VideoStream::onCapsAdvertise(const RDPGFX_CAPS_ADVERTISE_PDU *capsAdvertise) { + // Windows clients (mstsc) send CapsAdvertise twice: once during + // initial setup and again after confirming. If we already confirmed + // caps, this is a GFX channel reset — clear surface state so + // surfaces get re-created on the next frame. + if (d->capsConfirmed) { + qCDebug(KRDP) << "GFX channel reset (re-advertisement), resetting surface state"; + d->capsConfirmed = false; + d->pendingReset = true; + d->surface = Surface{}; + d->pendingFrames.clear(); + } + auto capsSets = capsAdvertise->capsSets; auto count = capsAdvertise->capsSetCount; @@ -332,6 +367,8 @@ { auto id = frameAcknowledge->frameId; + std::lock_guard lock(d->pendingFramesMutex); + auto itr = d->pendingFrames.constFind(id); if (itr == d->pendingFrames.cend()) { qCWarning(KRDP) << "Got frame acknowledge for an unknown frame"; @@ -405,7 +442,10 @@ d->encodedFrames++; - d->pendingFrames.insert(frameId); + { + std::lock_guard lock(d->pendingFramesMutex); + d->pendingFrames.insert(frameId); + } RDPGFX_START_FRAME_PDU startFramePdu; RDPGFX_END_FRAME_PDU endFramePdu;
