--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: kwin@packages.debian.org, Debian Qt/KDE Maintainers <debian-qt-kde@lists.debian.org>
Control: affects -1 + src:kwin
User: release.debian.org@packages.debian.org
Usertags: unblock
Dear Release Team,
please unblock package kwin.
[ Reason ]
It contains the following changes:
* New upstream bugfix release (6.3.6).
- Backends/drm: never use DEGAMMA_LUT. (kde#505869)
- Workspace: Fix window activation on activity change. (kde#501393)
- Don't add deleted windows to the stacking order.
- Xwayland: don't forward left/middle/right mouse buttons to Xwayland.
(kcde#490057)
- Output management: add some safe guards for invalid brightness
overrides. (kde#506090)
- Wayland/tablet_v2: fix the tablet cursor hotspot with Xwayland scaling.
- Core/renderloop: fix subsurfaces vrr scheduling.
- Plugins/slideback: Scale smoothness proportionally to adjusted strength
from 6.3. (kde#503964)
- Plugins/systembell: Throttle visual bell.
- Backends/drm: disable direct scanout on secondary GPUs.
- Scene/surfaceitem_wayland: handle some missing initial properties.
- Plugins/translucency: Fix unsetting animations for minimized windows.
(kde#504687)
- Autotests/integration/outputchanges: wait for frame callbacks before committing.
- Autotests/outputchanges: add a test for evacuating windows from removed outputs.
- Scene/workspacescene: restrict frame callbacks based on the output
rather than geometry. (kde#479694, kde#498628, kde#505060)
- Backends/drm: reduce severity of pageflip failure logging. (kde#505028)
- Xwayland: Fix leaking normal key presses with keyboard layouts other
than English. (kde#500032)
- Backends/drm: Add missing null guard.
- Outputconfigurationstore: disable adaptive sync by default.
- Map xinerama index to Output by output name.
- Backends/drm: clear the test buffer with legacy modesetting.
(kde#504258)
- Wayland/xdgoutput: round the scaled output position.
- Don't leak lcms tone curves.
- Plugins/colorpicker: round the result. (kde#491633)
- Plugins/colorpicker: use BPC when converting to sRGB. (kde#491633)
- X11: Add an environment variable to disable _NET_WM_SYNC_REQUEST in X11Window.
- Wayland/colormanagement: compare primaries with the protocol's resolution.
- Wayland/colormanagement: also around max_fall and max_cll.
- Wayland/colormanagement: fix sending target luminance levels.
- Core/iccprofile: also estimate black point even if there's no luminance tag.
- Backends/drm: also guard DrmOutput::cursorLayer for nullptr pipeline.
(kde#504516)
* Drop backported patches now part of the upstreame release.
* Refresh patches.
* Backport upstream commits:
- Fix inactive inner window action "Activate, raise and pass click" not
raising the window. (kde#501457)
- Fix incorrect window sometimes receiving clicks when slideback effect in
use. [572fee09] (kde#455429)
- wayland: Send wl_data_source.cancelled if wl_data_device.start_drag is
rejected similar to what weston does. [48ddfb59]
- backends/drm: also handle GPU resets on secondary GPUs. [9580193a]
- Fix night light not tinting properly on wayland. [98b64e9b] (kde#505495)
- Implement org.freedesktop.a11y.KeyboardMonitor interface which allows
screen readers to intercept keys for their own shortcut handling.
[252c74c0, cfc05b0e, 5ba6279b, 889ffed2]
- Fix an issue where windows may be placed at a non-0,0 coordinate after a
maximize on fractionally scaled displays if the window was previously
resized from the top edge, potentially allowing mouse clicks on the top of
the display to reach windows underneath the maximized window. [070f5ba6]
- Fix for multiples instances of screens waking up just after going to
sleep. [8f331a26] (kde#493879, kde#506135, kde#505953)
- Fix some wayland clients crashing in some race conditions when
attempting to create an xdg_surface while mapping a window. [225e7360]
(kde#506412)
- backends/drm: Fix memory leak in DrmGpu::createNonMasterFd. [e4e144db]
- wayland: Fix resizing with fractional increments. [e8541cf2]
- wayland: close popups upon window activation, fixing GTK4 popups
preventing the kicker launcher getting focus. [51c0880f] (kde#497075)
- Fix panel not registering clicks when window behavior set to "activate
and raise", and a tooltip is visible. [267fa4ec] (kde#461414)
The complete debdiff is quite big due to translation and autotests update
so I’m attaching a trimmed down version for your conveninence:
diff -ur --exclude=po --exclude=patches --exclude=autotests kwin-6.3.[56]
[ Tests ]
- No regression noted running Plasma with these changes for several
days.
- The upstream test suite doesn’t regress in sbuild with the same tests
failing :
The following tests FAILED:
19 - kwin-testDontCrashGlxgears (Failed)
20 - kwin-testLockScreen (Failed)
21 - kwin-testBounceKeys (Failed)
22 - kwin-testButtonRebind (Failed)
23 - kwin-testDecorationInput (Failed)
25 - kwin-testTouchInput (Failed)
27 - kwin-testPointerInput (Failed)
31 - kwin-testDebugConsole (Failed)
34 - kwin-testXdgShellWindow (Failed)
44 - kwin-testDontCrashUseractionsMenu (Failed)
59 - kwin-testMoveResize (Failed)
63 - kwin-testPlasmaWindow (Failed)
67 - kwin-testX11Window (Failed)
68 - kwin-testQuickTiling (Failed)
75 - kwin-testStickyKeys (Failed)
82 - kwin-testXwaylandSelections (Failed)
89 - kwin-testInputCapture (Failed)
101 - kwin-testLibinputDevice (Failed)
146 - kwin-testDrm (SEGFAULT)
[ Risks ]
Contains the latest upstream point release for the 6.3 branch plus
backported fixes. More fixes can be backported or the changes reverted
if required.
[ Checklist ]
[x] all changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in testing
Thanks !
unblock kwin/4:6.3.6-1
diff -Nru kwin-6.3.5/autotests/integration/outputchanges_test.cpp kwin-6.3.6/autotests/integration/outputchanges_test.cpp
--- kwin-6.3.5/autotests/integration/outputchanges_test.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/autotests/integration/outputchanges_test.cpp 2025-07-08 13:45:07.000000000 +0200
@@ -131,6 +131,9 @@
void testSettingRestoration_data();
void testSettingRestoration();
void testSettingRestoration_initialParsingFailure();
+
+ void testEvacuateTiledWindowFromRemovedOutput_data();
+ void testEvacuateTiledWindowFromRemovedOutput();
};
void OutputChangesTest::initTestCase()
@@ -1900,6 +1903,115 @@
}
}
+void OutputChangesTest::testEvacuateTiledWindowFromRemovedOutput_data()
+{
+ QTest::addColumn<QuickTileFlag>("tileMode");
+
+ QTest::addRow("Not tiled") << QuickTileFlag::None;
+ QTest::addRow("Quick Left") << QuickTileFlag::Left;
+ QTest::addRow("Quick Right") << QuickTileFlag::Right;
+ QTest::addRow("Quick Top") << QuickTileFlag::Top;
+ QTest::addRow("Quick Bottom") << QuickTileFlag::Bottom;
+ // FIXME this case currently fails!
+ // QTest::addRow("Custom") << QuickTileFlag::Custom;
+}
+
+void OutputChangesTest::testEvacuateTiledWindowFromRemovedOutput()
+{
+ Test::setOutputConfig({
+ Test::OutputInfo{
+ .geometry = QRect(0, 0, 5120, 1440),
+ .internal = false,
+ },
+ Test::OutputInfo{
+ .geometry = QRect(1705, 1440, 1800, 1200),
+ .scale = 1.6,
+ .internal = true,
+ },
+ });
+
+ const auto outputs = kwinApp()->outputBackend()->outputs();
+ const auto external = outputs[0];
+ const auto internal = outputs[1];
+ QVERIFY(!external->isInternal());
+
+ // create a window on the external output
+ workspace()->setActiveOutput(external);
+ std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
+ std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
+ auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 300), Qt::blue);
+ QVERIFY(window);
+
+ // kwin will send a configure event with the active state.
+ QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
+ QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
+ QSignalSpy tileChangedSpy(window, &Window::tileChanged);
+ QSignalSpy frameCallback(surface.get(), &KWayland::Client::Surface::frameRendered);
+ QVERIFY(surfaceConfigureRequestedSpy.wait());
+
+ QVERIFY(external->geometryF().contains(window->frameGeometry()));
+
+ surface->setupFrameCallback();
+
+ // possibly tile it
+ QFETCH(QuickTileFlag, tileMode);
+ if (tileMode != QuickTileFlag::None) {
+ QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
+
+ window->setQuickTileModeAtCurrentPosition(tileMode);
+ QVERIFY(surfaceConfigureRequestedSpy.wait());
+ shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
+ Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
+
+ QVERIFY(frameGeometryChangedSpy.wait());
+ QVERIFY(external->geometryF().contains(window->frameGeometry()));
+ }
+
+ const QRectF originalGeometry = window->frameGeometry();
+
+ // now remove the external output
+ {
+ OutputConfiguration config;
+ config.changeSet(external)->enabled = false;
+ workspace()->applyOutputConfiguration(config);
+ }
+
+ if (tileMode != QuickTileFlag::None) {
+ // react to the configure event
+ QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
+ QVERIFY(surfaceConfigureRequestedSpy.wait());
+ shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
+
+ // before committing, wait for the frame callback
+ // like some real-world clients do (like Firefox)
+ QVERIFY(frameCallback.count() || frameCallback.wait(100));
+
+ Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
+ QVERIFY(frameGeometryChangedSpy.wait());
+ }
+
+ // the window should be moved to be completely in the internal output
+ QVERIFY(internal->geometryF().contains(window->frameGeometry()));
+
+ // when re-adding the output, the window should be back at its original spot
+ {
+ OutputConfiguration config;
+ config.changeSet(external)->enabled = true;
+ workspace()->applyOutputConfiguration(config);
+ }
+
+ if (tileMode != QuickTileFlag::None) {
+ // react to the configure event
+ QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
+ QVERIFY(surfaceConfigureRequestedSpy.wait());
+ shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
+ Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
+ QVERIFY(frameGeometryChangedSpy.wait());
+ }
+
+ QCOMPARE(window->frameGeometry(), originalGeometry);
+}
+
} // namespace KWin
WAYLANDTEST_MAIN(KWin::OutputChangesTest)
diff -Nru kwin-6.3.5/CMakeLists.txt kwin-6.3.6/CMakeLists.txt
--- kwin-6.3.5/CMakeLists.txt 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/CMakeLists.txt 2025-07-08 13:45:07.000000000 +0200
@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.16)
-set(PROJECT_VERSION "6.3.5") # Handled by release scripts
+set(PROJECT_VERSION "6.3.6") # Handled by release scripts
project(KWin VERSION ${PROJECT_VERSION})
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(PROJECT_DEP_VERSION "6.3.5")
+set(PROJECT_DEP_VERSION "6.3.6")
set(QT_MIN_VERSION "6.7.0")
set(KF6_MIN_VERSION "6.10.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
diff -Nru kwin-6.3.5/debian/changelog kwin-6.3.6/debian/changelog
--- kwin-6.3.5/debian/changelog 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/changelog 2025-07-18 11:21:52.000000000 +0200
@@ -1,3 +1,78 @@
+kwin (4:6.3.6-1) unstable; urgency=medium
+
+ [ Aurélien COUDERC ]
+ * New upstream bugfix release (6.3.6).
+ - Backends/drm: never use DEGAMMA_LUT. (kde#505869)
+ - Workspace: Fix window activation on activity change. (kde#501393)
+ - Don't add deleted windows to the stacking order.
+ - Xwayland: don't forward left/middle/right mouse buttons to Xwayland.
+ (kcde#490057)
+ - Output management: add some safe guards for invalid brightness
+ overrides. (kde#506090)
+ - Wayland/tablet_v2: fix the tablet cursor hotspot with Xwayland scaling.
+ - Core/renderloop: fix subsurfaces vrr scheduling.
+ - Plugins/slideback: Scale smoothness proportionally to adjusted strength
+ from 6.3. (kde#503964)
+ - Plugins/systembell: Throttle visual bell.
+ - Backends/drm: disable direct scanout on secondary GPUs.
+ - Scene/surfaceitem_wayland: handle some missing initial properties.
+ - Plugins/translucency: Fix unsetting animations for minimized windows.
+ (kde#504687)
+ - Autotests/integration/outputchanges: wait for frame callbacks before committing.
+ - Autotests/outputchanges: add a test for evacuating windows from removed outputs.
+ - Scene/workspacescene: restrict frame callbacks based on the output
+ rather than geometry. (kde#479694, kde#498628, kde#505060)
+ - Backends/drm: reduce severity of pageflip failure logging. (kde#505028)
+ - Xwayland: Fix leaking normal key presses with keyboard layouts other
+ than English. (kde#500032)
+ - Backends/drm: Add missing null guard.
+ - Outputconfigurationstore: disable adaptive sync by default.
+ - Map xinerama index to Output by output name.
+ - Backends/drm: clear the test buffer with legacy modesetting.
+ (kde#504258)
+ - Wayland/xdgoutput: round the scaled output position.
+ - Don't leak lcms tone curves.
+ - Plugins/colorpicker: round the result. (kde#491633)
+ - Plugins/colorpicker: use BPC when converting to sRGB. (kde#491633)
+ - X11: Add an environment variable to disable _NET_WM_SYNC_REQUEST in X11Window.
+ - Wayland/colormanagement: compare primaries with the protocol's resolution.
+ - Wayland/colormanagement: also around max_fall and max_cll.
+ - Wayland/colormanagement: fix sending target luminance levels.
+ - Core/iccprofile: also estimate black point even if there's no luminance tag.
+ - Backends/drm: also guard DrmOutput::cursorLayer for nullptr pipeline.
+ (kde#504516)
+ * Drop backported patches now part of the upstreame release.
+ * Refresh patches.
+ * Backport upstream commits:
+ - Fix inactive inner window action "Activate, raise and pass click" not
+ raising the window. (kde#501457)
+ - Fix incorrect window sometimes receiving clicks when slideback effect in
+ use. [572fee09] (kde#455429)
+ - wayland: Send wl_data_source.cancelled if wl_data_device.start_drag is
+ rejected similar to what weston does. [48ddfb59]
+ - backends/drm: also handle GPU resets on secondary GPUs. [9580193a]
+ - Fix night light not tinting properly on wayland. [98b64e9b] (kde#505495)
+ - Implement org.freedesktop.a11y.KeyboardMonitor interface which allows
+ screen readers to intercept keys for their own shortcut handling.
+ [252c74c0, cfc05b0e, 5ba6279b, 889ffed2]
+ - Fix an issue where windows may be placed at a non-0,0 coordinate after a
+ maximize on fractionally scaled displays if the window was previously
+ resized from the top edge, potentially allowing mouse clicks on the top of
+ the display to reach windows underneath the maximized window. [070f5ba6]
+ - Fix for multiples instances of screens waking up just after going to
+ sleep. [8f331a26] (kde#493879, kde#506135, kde#505953)
+ - Fix some wayland clients crashing in some race conditions when
+ attempting to create an xdg_surface while mapping a window. [225e7360]
+ (kde#506412)
+ - backends/drm: Fix memory leak in DrmGpu::createNonMasterFd. [e4e144db]
+ - wayland: Fix resizing with fractional increments. [e8541cf2]
+ - wayland: close popups upon window activation, fixing GTK4 popups
+ preventing the kicker launcher getting focus. [51c0880f] (kde#497075)
+ - Fix panel not registering clicks when window behavior set to "activate
+ and raise", and a tooltip is visible. [267fa4ec] (kde#461414)
+
+ -- Aurélien COUDERC <coucouf@debian.org> Fri, 18 Jul 2025 11:21:52 +0200
+
kwin (4:6.3.5-1) unstable; urgency=medium
[ Aurélien COUDERC ]
diff -Nru kwin-6.3.5/debian/patches/relax-interplasma-versioned-deps.patch kwin-6.3.6/debian/patches/relax-interplasma-versioned-deps.patch
--- kwin-6.3.5/debian/patches/relax-interplasma-versioned-deps.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/relax-interplasma-versioned-deps.patch 2025-07-18 11:21:52.000000000 +0200
@@ -4,7 +4,7 @@
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
--set(PROJECT_DEP_VERSION "6.3.5")
+-set(PROJECT_DEP_VERSION "6.3.6")
+set(PROJECT_DEP_VERSION "6.3.4")
set(QT_MIN_VERSION "6.7.0")
set(KF6_MIN_VERSION "6.10.0")
diff -Nru kwin-6.3.5/debian/patches/series kwin-6.3.6/debian/patches/series
--- kwin-6.3.5/debian/patches/series 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/series 2025-07-18 11:21:52.000000000 +0200
@@ -5,21 +5,21 @@
upstream_85eeafa0_backends-drm-dynamically-adjust-the-safety-margin-based-on-commit-time.patch
upstream_7b93a776_Implement-MouseKeys-on-Wayland.patch
upstream_801d31a9_plugins-stickykeys-Unlatch-keys-after-mouse-click.patch
-upstream_f1f25d2d_backends-drm-also-guard-DrmOutput-cursorLayer-for-nullptr-pipeline.patch
-upstream_1c071e40_core-iccprofile-also-estimate-black-point-even-if-there-s-no-luminance-tag.patch
-upstream_be55cfa2_wayland-colormanagement-fix-sending-target-luminance-levels.patch
-upstream_3c724b83_wayland-colormanagement-also-around-max-fall-and-max-cll.patch
-upstream_5cc72fe9_wayland-colormanagement-compare-primaries-with-the-protocol-s-resolution.patch
-upstream_473aebf0_x11-Add-an-environment-variable-to-disable-NET-WM-SYNC-REQUEST-in-X11Window.patch
-upstream_2d70fe62_plugins-colorpicker-use-BPC-when-converting-to-sRGB.patch
-upstream_cfd044a3_plugins-colorpicker-round-the-result.patch
-upstream_f82034f0_don-t-leak-lcms-tone-curves.patch
-upstream_8450d9a8_wayland-xdgoutput-round-the-scaled-output-position.patch
-upstream_04713f7b_Map-xinerama-index-to-Output-by-output-name.patch
-upstream_56760937_backends-drm-clear-the-test-buffer-with-legacy-modesetting.patch
-upstream_cc5d1256_outputconfigurationstore-disable-adaptive-sync-by-default.patch
-upstream_7f088c1b_backends-drm-Add-missing-null-guard.patch
-upstream_5c7d2de5_xwayland-Fix-leaking-normal-key-presses-with-keyboard-layouts-other-than-English.patch
-upstream_27d28c9c_backends-drm-reduce-severity-of-pageflip-failure-logging.patch
upstream_612ec743_backends-drm-also-generate-modes-for-the-native-refresh-rate-of-the-display.patch
upstream_1a6871de_wayland-Fix-focused-surface-check-in-wl-data-device-start-drag.patch
+upstream_fd0eef29_Fix-config-options-for-inactive-window-action.patch
+upstream_572fee09_effects-slideback-Also-check-activity-when-matching-windows.patch
+upstream_48ddfb59_wayland-Send-wl-data-source-cancelled-if-wl-data-device-start-drag-is-rejected.patch
+upstream_9580193a_backends-drm-also-handle-GPU-resets-on-secondary-GPUs.patch
+upstream_98b64e9b_backends-drm-allow-night-light-to-get-closer-to-the-edges-of-the-gamut.patch
+upstream_252c74c0_Implement-org-freedesktop-a11y-KeyboardMonitor-interface.patch
+upstream_cfc05b0e_a11ykeyboardmanager-Grab-keys-when-grabbed-modifier-is-pressed.patch
+upstream_5ba6279b_A11yKeyboardMonitor-Fix-sending-keycodes-to-AT.patch
+upstream_889ffed2_A11yKeyboardMonitor-Distinguish-modifier-and-other-key.patch
+upstream_070f5ba6_xdgshellwindow-Reset-gravity-on-interactive-resize-finish.patch
+upstream_8f331a26_backends-drm-don-t-use-UUID-to-identify-outputs.patch
+upstream_225e7360_wayland-Remove-buffer-checks-in-xdg-surface-and-layer-surface-factory-requests.patch
+upstream_e4e144db_backends-drm-Fix-memory-leak-in-DrmGpu-createNonMasterFd.patch
+upstream_e8541cf2_wayland-Fix-resizing-with-fractional-increments.patch
+upstream_51c0880f_wayland-close-popups-upon-window-activation.patch
+upstream_267fa4ec_Fix-activate-and-raise-action-with-panels.patch
diff -Nru kwin-6.3.5/debian/patches/upstream_04713f7b_Map-xinerama-index-to-Output-by-output-name.patch kwin-6.3.6/debian/patches/upstream_04713f7b_Map-xinerama-index-to-Output-by-output-name.patch
--- kwin-6.3.5/debian/patches/upstream_04713f7b_Map-xinerama-index-to-Output-by-output-name.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_04713f7b_Map-xinerama-index-to-Output-by-output-name.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,214 +0,0 @@
-From 04713f7b76bc07ed0f24e8f2603702ef57dd42a6 Mon Sep 17 00:00:00 2001
-From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
-Date: Fri, 23 May 2025 12:46:17 +0000
-Subject: [PATCH] Map xinerama index to Output by output name
-
-Currently, xinerama indices are mapped to outputs by comparing geometries.
-The problem with that approach is that geometries may mismatch due to
-scaling being applied.
-
-Since version 23.1.0, Xwayland uses provided output connector names, so
-we can use them to find the matching output. This approach should be less
-affected by rounding issues.
-
-
-(cherry picked from commit 2de458c9fa643dc56acdd03609d6dcd20598920f)
-
-Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
----
- src/utils/xcbutils.cpp | 15 ++++++++++++++
- src/utils/xcbutils.h | 2 ++
- src/workspace.cpp | 41 ++++++++++++++++++++------------------
- src/workspace.h | 1 +
- src/xwayland/drag_x.cpp | 2 +-
- src/xwayland/selection.cpp | 17 +---------------
- src/xwayland/selection.h | 1 -
- 7 files changed, 42 insertions(+), 37 deletions(-)
-
-diff --git a/src/utils/xcbutils.cpp b/src/utils/xcbutils.cpp
-index 171158ce57d..dbf55076e65 100644
---- a/src/utils/xcbutils.cpp
-+++ b/src/utils/xcbutils.cpp
-@@ -672,5 +672,20 @@ QRectF nativeFloor(const QRectF &rect)
- nativeFloor(outputRect.width()), nativeFloor(outputRect.height())));
- }
-
-+QString atomName(xcb_atom_t atom)
-+{
-+ xcb_connection_t *xcbConn = kwinApp()->x11Connection();
-+ xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom);
-+ xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, nullptr);
-+ if (!nameReply) {
-+ return QString();
-+ }
-+
-+ const size_t length = xcb_get_atom_name_name_length(nameReply);
-+ QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), length);
-+ free(nameReply);
-+ return name;
-+}
-+
- } // namespace Xcb
- } // namespace KWin
-diff --git a/src/utils/xcbutils.h b/src/utils/xcbutils.h
-index 85e20c196fb..bfe6f8ae0b8 100644
---- a/src/utils/xcbutils.h
-+++ b/src/utils/xcbutils.h
-@@ -61,6 +61,8 @@ qreal KWIN_EXPORT nativeRound(qreal value);
- */
- QRectF KWIN_EXPORT nativeFloor(const QRectF &value);
-
-+QString KWIN_EXPORT atomName(xcb_atom_t atom);
-+
- // forward declaration of methods
- static void defineCursor(xcb_window_t window, xcb_cursor_t cursor);
- static void setInputFocus(xcb_window_t window, uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = xTime());
-diff --git a/src/workspace.cpp b/src/workspace.cpp
-index 192c5ca95eb..191a72f6c36 100644
---- a/src/workspace.cpp
-+++ b/src/workspace.cpp
-@@ -82,8 +82,6 @@
- #include <QDBusConnection>
- #include <QDBusPendingCall>
- #include <QMetaProperty>
--// xcb
--#include <xcb/xinerama.h>
-
- namespace KWin
- {
-@@ -1185,6 +1183,16 @@ Output *Workspace::outputAt(const QPointF &pos) const
- return bestOutput;
- }
-
-+Output *Workspace::findOutput(const QString &name) const
-+{
-+ for (Output *output : std::as_const(m_outputs)) {
-+ if (output->name() == name) {
-+ return output;
-+ }
-+ }
-+ return nullptr;
-+}
-+
- Output *Workspace::findOutput(Output *reference, Direction direction, bool wrapAround) const
- {
- QList<Output *> relevantOutputs;
-@@ -2582,31 +2590,26 @@ Output *Workspace::xineramaIndexToOutput(int index) const
- return nullptr;
- }
-
-- const UniqueCPtr<xcb_xinerama_is_active_reply_t> active{xcb_xinerama_is_active_reply(connection, xcb_xinerama_is_active(connection), nullptr)};
-- if (!active || !active->state) {
-+ xcb_randr_get_monitors_cookie_t cookie = xcb_randr_get_monitors(kwinApp()->x11Connection(), kwinApp()->x11RootWindow(), 1);
-+ const UniqueCPtr<xcb_randr_get_monitors_reply_t> monitors(xcb_randr_get_monitors_reply(kwinApp()->x11Connection(), cookie, nullptr));
-+ if (!monitors) {
- return nullptr;
- }
-
-- const UniqueCPtr<xcb_xinerama_query_screens_reply_t> screens(xcb_xinerama_query_screens_reply(connection, xcb_xinerama_query_screens(connection), nullptr));
-- if (!screens) {
-- return nullptr;
-+ xcb_randr_monitor_info_t *monitorInfo = nullptr;
-+ for (auto it = xcb_randr_get_monitors_monitors_iterator(monitors.get()); it.rem; xcb_randr_monitor_info_next(&it)) {
-+ const int current = monitors->nMonitors - it.rem;
-+ if (current == index) {
-+ monitorInfo = it.data;
-+ break;
-+ }
- }
-
-- const int infoCount = xcb_xinerama_query_screens_screen_info_length(screens.get());
-- if (index < 0 || index >= infoCount) {
-+ if (!monitorInfo) {
- return nullptr;
- }
-
-- const xcb_xinerama_screen_info_t *infos = xcb_xinerama_query_screens_screen_info(screens.get());
-- const QRect needle(infos[index].x_org, infos[index].y_org, infos[index].width, infos[index].height);
--
-- for (Output *output : std::as_const(m_outputs)) {
-- if (Xcb::toXNative(output->geometryF()) == needle) {
-- return output;
-- }
-- }
--
-- return nullptr;
-+ return findOutput(Xcb::atomName(monitorInfo->name));
- }
- #endif
-
-diff --git a/src/workspace.h b/src/workspace.h
-index e31f3702486..3112513bc35 100644
---- a/src/workspace.h
-+++ b/src/workspace.h
-@@ -357,6 +357,7 @@ public:
- DirectionNext
- };
- Output *findOutput(Output *reference, Direction direction, bool wrapAround = false) const;
-+ Output *findOutput(const QString &name) const;
- void switchToOutput(Output *output);
-
- QList<Output *> outputs() const;
-diff --git a/src/xwayland/drag_x.cpp b/src/xwayland/drag_x.cpp
-index 93d0c9d1f62..d61f1beff6b 100644
---- a/src/xwayland/drag_x.cpp
-+++ b/src/xwayland/drag_x.cpp
-@@ -51,7 +51,7 @@ static QStringList atomToMimeTypes(xcb_atom_t atom)
- } else if (atom == atoms->netscape_url) {
- mimeTypes << QStringLiteral("_NETSCAPE_URL");
- } else {
-- mimeTypes << Selection::atomName(atom);
-+ mimeTypes << Xcb::atomName(atom);
- }
- return mimeTypes;
- }
-diff --git a/src/xwayland/selection.cpp b/src/xwayland/selection.cpp
-index 3f489b1a9a6..4409075332b 100644
---- a/src/xwayland/selection.cpp
-+++ b/src/xwayland/selection.cpp
-@@ -45,21 +45,6 @@ xcb_atom_t Selection::mimeTypeToAtomLiteral(const QString &mimeType)
- return Xcb::Atom(mimeType.toLatin1(), false, kwinApp()->x11Connection());
- }
-
--QString Selection::atomName(xcb_atom_t atom)
--{
-- xcb_connection_t *xcbConn = kwinApp()->x11Connection();
-- xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom);
-- xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, nullptr);
-- if (!nameReply) {
-- return QString();
-- }
--
-- const size_t length = xcb_get_atom_name_name_length(nameReply);
-- QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), length);
-- free(nameReply);
-- return name;
--}
--
- QStringList Selection::atomToMimeTypes(xcb_atom_t atom)
- {
- QStringList mimeTypes;
-@@ -74,7 +59,7 @@ QStringList Selection::atomToMimeTypes(xcb_atom_t atom)
- } else if (atom == atoms->targets || atom == atoms->timestamp) {
- // Ignore known ICCCM internal atoms
- } else {
-- const QString atomNameName = atomName(atom);
-+ const QString atomNameName = Xcb::atomName(atom);
- // Ignore other non-mimetype atoms
- if (atomNameName.contains(QLatin1Char('/'))) {
- mimeTypes << atomNameName;
-diff --git a/src/xwayland/selection.h b/src/xwayland/selection.h
-index 438fc4c6a6b..65a6ca87c09 100644
---- a/src/xwayland/selection.h
-+++ b/src/xwayland/selection.h
-@@ -49,7 +49,6 @@ public:
- static xcb_atom_t mimeTypeToAtom(const QString &mimeType);
- static xcb_atom_t mimeTypeToAtomLiteral(const QString &mimeType);
- static QStringList atomToMimeTypes(xcb_atom_t atom);
-- static QString atomName(xcb_atom_t atom);
- static void sendSelectionNotify(xcb_selection_request_event_t *event, bool success);
-
- // on selection owner changes by X clients (Xwl -> Wl)
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_070f5ba6_xdgshellwindow-Reset-gravity-on-interactive-resize-finish.patch kwin-6.3.6/debian/patches/upstream_070f5ba6_xdgshellwindow-Reset-gravity-on-interactive-resize-finish.patch
--- kwin-6.3.5/debian/patches/upstream_070f5ba6_xdgshellwindow-Reset-gravity-on-interactive-resize-finish.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_070f5ba6_xdgshellwindow-Reset-gravity-on-interactive-resize-finish.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,39 @@
+From 070f5ba65150bb5ecca4bc7f969d8db44e1203a0 Mon Sep 17 00:00:00 2001
+From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+Date: Tue, 1 Jul 2025 06:42:43 +0000
+Subject: [PATCH] xdgshellwindow: Reset gravity on interactive resize finish
+
+Prevent a maximize operation from accidentally reusing whatever resize
+gravity a previous interactive resize operation used.
+
+This fixes an issue where windows may be placed at a non-0,0 coordinate
+after a maximize on fractionally scaled displays if the window was
+previously resized from the top edge, potentially allowing mouse clicks
+on the top of the display to reach windows underneath the maximized
+window.
+
+
+(cherry picked from commit 0d929de7f0aad8bfd07075bf189cb19ee46f6815)
+
+Co-authored-by: Myrrh Periwinkle <myrrhperiwinkle@qtmlabs.xyz>
+---
+ src/xdgshellwindow.cpp | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/xdgshellwindow.cpp b/src/xdgshellwindow.cpp
+index f0726ca3acd..42c3d8b595e 100644
+--- a/src/xdgshellwindow.cpp
++++ b/src/xdgshellwindow.cpp
+@@ -125,6 +125,9 @@ void XdgSurfaceWindow::sendConfigure()
+ configureEvent->flags |= m_configureFlags;
+ configureEvent->scale = m_nextTargetScale;
+ m_configureFlags = {};
++ if (!isInteractiveMoveResize()) {
++ m_nextGravity = Gravity::None;
++ }
+
+ m_configureEvents.append(configureEvent);
+ }
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_1c071e40_core-iccprofile-also-estimate-black-point-even-if-there-s-no-luminance-tag.patch kwin-6.3.6/debian/patches/upstream_1c071e40_core-iccprofile-also-estimate-black-point-even-if-there-s-no-luminance-tag.patch
--- kwin-6.3.5/debian/patches/upstream_1c071e40_core-iccprofile-also-estimate-black-point-even-if-there-s-no-luminance-tag.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_1c071e40_core-iccprofile-also-estimate-black-point-even-if-there-s-no-luminance-tag.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,127 +0,0 @@
-From 1c071e4006f725b4a2b04ddcfb1891320912e95d Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Mon, 19 May 2025 14:25:56 +0200
-Subject: [PATCH] core/iccprofile: also estimate black point even if there's no
- luminance tag
-
-We can still get a relative value out of it
-
-(cherry picked from commit 0ee6eb5fb3b4389f187d8b9784d5e23b4c883f84)
----
- src/backends/drm/drm_output.cpp | 2 +-
- src/core/iccprofile.cpp | 20 ++++++++++----------
- src/core/iccprofile.h | 6 +++---
- 3 files changed, 14 insertions(+), 14 deletions(-)
-
-diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp
-index 151cb2d466f..f2024b223db 100644
---- a/src/backends/drm/drm_output.cpp
-+++ b/src/backends/drm/drm_output.cpp
-@@ -427,8 +427,8 @@ std::pair<ColorDescription, QVector3D> DrmOutput::createColorDescription(const s
- const double sdrGamutWideness = props->sdrGamutWideness.value_or(m_state.sdrGamutWideness);
- const auto iccProfile = props->iccProfile.value_or(m_state.iccProfile);
- if (colorSource == ColorProfileSource::ICC && !effectiveHdr && !effectiveWcg && iccProfile) {
-- const double minBrightness = iccProfile->minBrightness().value_or(0);
- const double maxBrightness = iccProfile->maxBrightness().value_or(200);
-+ const double minBrightness = iccProfile->relativeBlackPoint().value_or(0) * maxBrightness;
- const auto sdrColor = Colorimetry::fromName(NamedColorimetry::BT709).interpolateGamutTo(iccProfile->colorimetry(), sdrGamutWideness);
- const bool allowSdrSoftwareBrightness = props->allowSdrSoftwareBrightness.value_or(m_state.allowSdrSoftwareBrightness);
- const double brightnessFactor = (!m_brightnessDevice && allowSdrSoftwareBrightness) ? brightness : 1.0;
-diff --git a/src/core/iccprofile.cpp b/src/core/iccprofile.cpp
-index 91ab1f92c64..17be36c93d5 100644
---- a/src/core/iccprofile.cpp
-+++ b/src/core/iccprofile.cpp
-@@ -27,14 +27,14 @@ static const Colorimetry CIEXYZD50 = Colorimetry{
-
- const ColorDescription IccProfile::s_connectionSpace = ColorDescription(CIEXYZD50, TransferFunction(TransferFunction::linear, 0, 1), 1, 0, 1, 1);
-
--IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> minBrightness, std::optional<double> maxBrightness)
-+IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> relativeBlackPoint, std::optional<double> maxBrightness)
- : m_handle(handle)
- , m_colorimetry(colorimetry)
- , m_bToA0Tag(std::move(bToA0Tag))
- , m_bToA1Tag(std::move(bToA1Tag))
- , m_inverseEOTF(inverseEOTF)
- , m_vcgt(vcgt)
-- , m_minBrightness(minBrightness)
-+ , m_relativeBlackPoint(relativeBlackPoint)
- , m_maxBrightness(maxBrightness)
- {
- }
-@@ -44,9 +44,9 @@ IccProfile::~IccProfile()
- cmsCloseProfile(m_handle);
- }
-
--std::optional<double> IccProfile::minBrightness() const
-+std::optional<double> IccProfile::relativeBlackPoint() const
- {
-- return m_minBrightness;
-+ return m_relativeBlackPoint;
- }
-
- std::optional<double> IccProfile::maxBrightness() const
-@@ -341,16 +341,16 @@ IccProfile::Expected IccProfile::load(const QString &path)
- return Expected(i18n("ICC profile \"%1\" is broken, its primaries are invalid", path));
- }
-
-- std::optional<double> minBrightness;
- std::optional<double> maxBrightness;
- if (cmsCIEXYZ *luminance = static_cast<cmsCIEXYZ *>(cmsReadTag(handle, cmsSigLuminanceTag))) {
- // for some reason, lcms exposes the luminance as a XYZ triple...
- // only Y is non-zero, and it's the brightness in nits
- maxBrightness = luminance->Y;
-- cmsCIEXYZ blackPoint;
-- if (cmsDetectDestinationBlackPoint(&blackPoint, handle, INTENT_RELATIVE_COLORIMETRIC, 0)) {
-- minBrightness = blackPoint.Y * luminance->Y;
-- }
-+ }
-+ std::optional<double> relativeBlackPoint;
-+ cmsCIEXYZ blackPoint;
-+ if (cmsDetectDestinationBlackPoint(&blackPoint, handle, INTENT_RELATIVE_COLORIMETRIC, 0)) {
-+ relativeBlackPoint = blackPoint.Y;
- }
-
- if (cmsIsTag(handle, cmsSigBToD1Tag) && !cmsIsTag(handle, cmsSigBToA1Tag) && !cmsIsTag(handle, cmsSigBToA0Tag)) {
-@@ -404,7 +404,7 @@ IccProfile::Expected IccProfile::load(const QString &path)
- std::vector<std::unique_ptr<ColorPipelineStage>> stages;
- stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, toneCurves.size(), toneCurves.data())));
- const auto inverseEOTF = std::make_shared<ColorTransformation>(std::move(stages));
-- return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(bToA0), std::move(bToA1), inverseEOTF, vcgt, minBrightness, maxBrightness);
-+ return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(bToA0), std::move(bToA1), inverseEOTF, vcgt, relativeBlackPoint, maxBrightness);
- }
-
- }
-diff --git a/src/core/iccprofile.h b/src/core/iccprofile.h
-index be4e2e4a74c..bc4057acfd9 100644
---- a/src/core/iccprofile.h
-+++ b/src/core/iccprofile.h
-@@ -25,7 +25,7 @@ class ColorLUT3D;
- class KWIN_EXPORT IccProfile
- {
- public:
-- explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> minBrightness, std::optional<double> maxBrightness);
-+ explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> relativeBlackPoint, std::optional<double> maxBrightness);
- ~IccProfile();
-
- /**
-@@ -44,7 +44,7 @@ public:
- */
- std::shared_ptr<ColorTransformation> vcgt() const;
- const Colorimetry &colorimetry() const;
-- std::optional<double> minBrightness() const;
-+ std::optional<double> relativeBlackPoint() const;
- std::optional<double> maxBrightness() const;
-
- // TODO Plasma 6.4 port this back to std::expected
-@@ -72,7 +72,7 @@ private:
- const std::optional<ColorPipeline> m_bToA1Tag;
- const std::shared_ptr<ColorTransformation> m_inverseEOTF;
- const std::shared_ptr<ColorTransformation> m_vcgt;
-- const std::optional<double> m_minBrightness;
-+ const std::optional<double> m_relativeBlackPoint;
- const std::optional<double> m_maxBrightness;
- };
-
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_225e7360_wayland-Remove-buffer-checks-in-xdg-surface-and-layer-surface-factory-requests.patch kwin-6.3.6/debian/patches/upstream_225e7360_wayland-Remove-buffer-checks-in-xdg-surface-and-layer-surface-factory-requests.patch
--- kwin-6.3.5/debian/patches/upstream_225e7360_wayland-Remove-buffer-checks-in-xdg-surface-and-layer-surface-factory-requests.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_225e7360_wayland-Remove-buffer-checks-in-xdg-surface-and-layer-surface-factory-requests.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,59 @@
+From 225e73606c4bfcf0385d5c8664d2d086c930195a Mon Sep 17 00:00:00 2001
+From: Xaver Hugl <xaver.hugl@gmail.com>
+Date: Fri, 4 Jul 2025 18:21:54 +0200
+Subject: [PATCH] wayland: Remove buffer checks in xdg_surface and
+ layer_surface factory requests
+
+Due to transactions getting delayed sometimes, the current surface state
+may have a buffer. It doesn't necessarily mean that it's a protocol error.
+
+Whether the client has actually committed an invalid state will be
+determined later when the client commits the surface after creating the
+surface role.
+
+BUG: 506412
+
+
+(cherry picked from commit 7772926af7f74e29c14f7b97418745111278dd83)
+
+Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+---
+ src/wayland/layershell_v1.cpp | 5 -----
+ src/wayland/xdgshell.cpp | 5 -----
+ 2 files changed, 10 deletions(-)
+
+diff --git a/src/wayland/layershell_v1.cpp b/src/wayland/layershell_v1.cpp
+index 1cb57ae2bcb..7ae52fa9fe1 100644
+--- a/src/wayland/layershell_v1.cpp
++++ b/src/wayland/layershell_v1.cpp
+@@ -111,11 +111,6 @@ void LayerShellV1InterfacePrivate::zwlr_layer_shell_v1_get_layer_surface(Resourc
+ SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
+ OutputInterface *output = OutputInterface::get(output_resource);
+
+- if (surface->buffer()) {
+- wl_resource_post_error(resource->handle, error_already_constructed, "the wl_surface already has a buffer attached");
+- return;
+- }
+-
+ if (layer > layer_overlay) {
+ wl_resource_post_error(resource->handle, error_invalid_layer, "invalid layer %d", layer);
+ return;
+diff --git a/src/wayland/xdgshell.cpp b/src/wayland/xdgshell.cpp
+index 282678c0938..b8aded88e31 100644
+--- a/src/wayland/xdgshell.cpp
++++ b/src/wayland/xdgshell.cpp
+@@ -85,11 +85,6 @@ void XdgShellInterfacePrivate::xdg_wm_base_get_xdg_surface(Resource *resource, u
+ {
+ SurfaceInterface *surface = SurfaceInterface::get(surfaceResource);
+
+- if (surface->buffer()) {
+- wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation");
+- return;
+- }
+-
+ wl_resource *xdgSurfaceResource = wl_resource_create(resource->client(), &xdg_surface_interface, resource->version(), id);
+
+ XdgSurfaceInterface *xdgSurface = new XdgSurfaceInterface(q, surface, xdgSurfaceResource);
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_252c74c0_Implement-org-freedesktop-a11y-KeyboardMonitor-interface.patch kwin-6.3.6/debian/patches/upstream_252c74c0_Implement-org-freedesktop-a11y-KeyboardMonitor-interface.patch
--- kwin-6.3.5/debian/patches/upstream_252c74c0_Implement-org-freedesktop-a11y-KeyboardMonitor-interface.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_252c74c0_Implement-org-freedesktop-a11y-KeyboardMonitor-interface.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,474 @@
+From 252c74c0ee77d8eec1e303233d045413d0be03fd Mon Sep 17 00:00:00 2001
+From: Nicolas Fella <nicolas.fella@gmx.de>
+Date: Thu, 6 Mar 2025 12:42:43 +0100
+Subject: [PATCH] Implement org.freedesktop.a11y.KeyboardMonitor interface
+
+This allows screen readers to intercept keys for their own
+shortcut handling.
+
+This is not a regular input filter since we need to be able to
+intercept keys before they change the XKB state.
+---
+ src/CMakeLists.txt | 1 +
+ src/a11ykeyboardmonitor.cpp | 194 +++++++++++++++++++++++++++++++++++
+ src/a11ykeyboardmonitor.h | 70 +++++++++++++
+ src/keyboard_input.cpp | 5 +
+ src/keyboard_input.h | 2 +
+ src/org.freedesktop.a11y.xml | 119 +++++++++++++++++++++
+ 6 files changed, 391 insertions(+)
+ create mode 100644 src/a11ykeyboardmonitor.cpp
+ create mode 100644 src/a11ykeyboardmonitor.h
+ create mode 100644 src/org.freedesktop.a11y.xml
+
+diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
+index 246441da77e..04e6718b7b6 100644
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -29,6 +29,7 @@ target_compile_definitions(kwin PRIVATE
+
+ target_sources(kwin PRIVATE
+ 3rdparty/xcursor.c
++ a11ykeyboardmonitor.cpp
+ activation.cpp
+ appmenu.cpp
+ client_machine.cpp
+diff --git a/src/a11ykeyboardmonitor.cpp b/src/a11ykeyboardmonitor.cpp
+new file mode 100644
+index 00000000000..feaa85cb17e
+--- /dev/null
++++ b/src/a11ykeyboardmonitor.cpp
+@@ -0,0 +1,194 @@
++/*
++ SPDX-FileCopyrightText: 2025 Nicolas Fella <nicolas.fella@gmx.de>
++
++ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
++*/
++
++#include "a11ykeyboardmonitor.h"
++#include "keyboard_input.h"
++#include "wayland/keyboard.h"
++#include "wayland/seat.h"
++#include "wayland_server.h"
++#include "xkb.h"
++
++#include <QDBusConnection>
++#include <QDBusConnectionInterface>
++#include <QDBusMetaType>
++#include <QDBusServiceWatcher>
++
++using namespace std::literals;
++
++namespace KWin
++{
++
++A11yKeyboardMonitor::A11yKeyboardMonitor()
++ : QObject()
++{
++ qDBusRegisterMetaType<A11yKeyboardMonitor::KeyStroke>();
++ qDBusRegisterMetaType<QList<A11yKeyboardMonitor::KeyStroke>>();
++
++ m_dbusWatcher.setConnection(QDBusConnection::sessionBus());
++ m_dbusWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
++
++ connect(&m_dbusWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &serviceName) {
++ m_clients.remove(serviceName);
++ });
++
++ QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/freedesktop/a11y/Manager"), this, QDBusConnection::ExportScriptableContents);
++ QDBusConnection::sessionBus().registerService(QStringLiteral("org.freedesktop.a11y.Manager"));
++}
++
++bool A11yKeyboardMonitor::processKey(uint32_t key, KeyboardKeyState state, std::chrono::microseconds time)
++{
++ const auto mods = xkb_state_serialize_mods(input()->keyboard()->xkb()->state(), xkb_state_component(XKB_STATE_MODS_EFFECTIVE));
++
++ const auto keysym = input()->keyboard()->xkb()->toKeysym(key);
++ const auto text = input()->keyboard()->xkb()->toString(keysym);
++ const quint32 unicode = text.isEmpty() ? 0 : text.at(0).unicode();
++ const bool released = state == KeyboardKeyState::Released;
++
++ for (auto [name, data] : m_clients.asKeyValueRange()) {
++ if (data.grabbed) {
++ emitKeyEvent(name, released, mods, keysym, unicode, key);
++ }
++
++ // if the modifier was pressed twice within the key repeat delay process it normally
++ const qint32 keyRepeatDelay = waylandServer()->seat()->keyboard()->keyRepeatDelay();
++ if (state == KeyboardKeyState::Pressed && data.lastModifier == keysym && time < data.lastModifierTime + std::chrono::milliseconds(keyRepeatDelay)) {
++ data.modifierWasForwarded = true;
++ return false;
++ }
++
++ // if the modifier press was forwarded also forward the release
++ if (state == KeyboardKeyState::Released && data.modifierWasForwarded) {
++ data.modifierWasForwarded = false;
++ return false;
++ }
++
++ data.lastModifier = keysym;
++ data.lastModifierTime = time;
++
++ if (data.modifiers.contains(keysym)) {
++ emitKeyEvent(name, released, mods, keysym, unicode, key);
++ return true;
++ }
++
++ for (const KeyStroke &stroke : std::as_const(data.keys)) {
++ if (mods == stroke.modifiers && stroke.keysym == keysym) {
++ emitKeyEvent(name, released, mods, keysym, unicode, key);
++ return true;
++ }
++ }
++
++ if (data.watched) {
++ emitKeyEvent(name, released, mods, keysym, unicode, key);
++ }
++ }
++
++ return false;
++}
++
++void A11yKeyboardMonitor::GrabKeyboard()
++{
++ if (!checkPermission()) {
++ return;
++ }
++
++ m_dbusWatcher.addWatchedService(message().service());
++
++ m_clients[message().service()].grabbed = true;
++}
++
++void A11yKeyboardMonitor::UngrabKeyboard()
++{
++ if (!checkPermission()) {
++ return;
++ }
++
++ if (!m_clients.contains(message().service())) {
++ return;
++ }
++
++ m_clients[message().service()].grabbed = false;
++}
++
++void A11yKeyboardMonitor::WatchKeyboard()
++{
++ if (!checkPermission()) {
++ return;
++ }
++
++ m_dbusWatcher.addWatchedService(message().service());
++
++ m_clients[message().service()].watched = true;
++}
++
++void A11yKeyboardMonitor::UnwatchKeyboard()
++{
++ if (!checkPermission()) {
++ return;
++ }
++
++ if (!m_clients.contains(message().service())) {
++ return;
++ }
++
++ m_clients[message().service()].watched = false;
++}
++
++void A11yKeyboardMonitor::SetKeyGrabs(const QList<quint32> &modifiers, const QList<A11yKeyboardMonitor::KeyStroke> &keystrokes)
++{
++ if (!checkPermission()) {
++ return;
++ }
++
++ m_dbusWatcher.addWatchedService(message().service());
++
++ m_clients[message().service()].modifiers = modifiers;
++ m_clients[message().service()].keys = keystrokes;
++}
++
++void A11yKeyboardMonitor::emitKeyEvent(const QString &name, bool released, quint32 state, quint32 keysym, quint32 unichar, quint16 keycode)
++{
++ QDBusMessage signal = QDBusMessage::createTargetedSignal(name, QStringLiteral("/org/freedesktop/a11y/Manager"), QStringLiteral("org.freedesktop.a11y.KeyboardMonitor"), QStringLiteral("KeyEvent"));
++ QVariant keycodeVariant;
++ keycodeVariant.setValue(keycode);
++ signal.setArguments({released, state, keysym, unichar, keycodeVariant});
++ QDBusConnection::sessionBus().call(signal, QDBus::NoBlock);
++}
++
++bool A11yKeyboardMonitor::checkPermission()
++{
++ QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), QStringLiteral("/org/freedesktop/DBus"), QStringLiteral("org.freedesktop.DBus"), "GetNameOwner");
++ msg.setArguments({QStringLiteral("org.gnome.Orca.KeyboardMonitor")});
++ QDBusReply<QString> orcaName = QDBusConnection::sessionBus().call(msg);
++
++ if (message().service() != orcaName) {
++ sendErrorReply(QDBusError::AccessDenied, "Only screen readers are allowed to use this interface");
++ return false;
++ }
++
++ return true;
++}
++
++const QDBusArgument &operator>>(const QDBusArgument &arg, A11yKeyboardMonitor::KeyStroke &keystroke)
++{
++ arg.beginStructure();
++ arg >> keystroke.keysym;
++ arg >> keystroke.modifiers;
++ arg.endStructure();
++
++ return arg;
++}
++
++const QDBusArgument &operator<<(QDBusArgument &arg, const A11yKeyboardMonitor::KeyStroke &keystroke)
++{
++ arg.beginStructure();
++ arg << keystroke.keysym;
++ arg << keystroke.modifiers;
++ arg.endStructure();
++
++ return arg;
++}
++
++}
+diff --git a/src/a11ykeyboardmonitor.h b/src/a11ykeyboardmonitor.h
+new file mode 100644
+index 00000000000..3fbee438c6e
+--- /dev/null
++++ b/src/a11ykeyboardmonitor.h
+@@ -0,0 +1,70 @@
++/*
++ SPDX-FileCopyrightText: 2025 Nicolas Fella <nicolas.fella@gmx.de>
++
++ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
++*/
++
++#pragma once
++
++#include "input.h"
++#include "input_event.h"
++
++#include <QDBusArgument>
++#include <QDBusContext>
++#include <QDBusServiceWatcher>
++
++namespace KWin
++{
++
++class A11yKeyboardMonitor : public QObject, protected QDBusContext
++{
++ Q_OBJECT
++ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.a11y.KeyboardMonitor")
++public:
++ explicit A11yKeyboardMonitor();
++
++ struct KeyStroke
++ {
++ quint32 keysym;
++ quint32 modifiers;
++ };
++
++ Q_SCRIPTABLE void GrabKeyboard();
++
++ Q_SCRIPTABLE void UngrabKeyboard();
++
++ Q_SCRIPTABLE void WatchKeyboard();
++
++ Q_SCRIPTABLE void UnwatchKeyboard();
++
++ Q_SCRIPTABLE void SetKeyGrabs(const QList<quint32> &modifiers, const QList<KeyStroke> &keystrokes);
++
++Q_SIGNALS:
++ Q_SCRIPTABLE void KeyEvent(bool released, quint32 state, quint32 keysym, quint32 unichar, quint16 keycode);
++
++public:
++ bool processKey(uint32_t key, KeyboardKeyState state, std::chrono::microseconds time);
++
++private:
++ bool checkPermission();
++ void emitKeyEvent(const QString &name, bool released, quint32 state, quint32 keysym, quint32 unichar, quint16 keycode);
++
++ struct GrabData
++ {
++ bool watched;
++ bool grabbed;
++ QList<quint32> modifiers;
++ QList<KeyStroke> keys;
++ quint32 lastModifier;
++ std::chrono::microseconds lastModifierTime;
++ bool modifierWasForwarded = false;
++ };
++
++ QHash<QString, GrabData> m_clients;
++ QDBusServiceWatcher m_dbusWatcher;
++};
++
++const QDBusArgument &operator>>(const QDBusArgument &arg, A11yKeyboardMonitor::KeyStroke &keystroke);
++const QDBusArgument &operator<<(QDBusArgument &arg, const A11yKeyboardMonitor::KeyStroke &keystroke);
++
++}
+diff --git a/src/keyboard_input.cpp b/src/keyboard_input.cpp
+index 083d3fdc55c..97d00a6a860 100644
+--- a/src/keyboard_input.cpp
++++ b/src/keyboard_input.cpp
+@@ -273,6 +273,11 @@ void KeyboardInputRedirection::processKey(uint32_t key, KeyboardKeyState state,
+ return;
+ }
+
++ const bool ret = m_a11yKeyboardMonitor.processKey(key, state, time);
++ if (ret) {
++ return;
++ }
++
+ if (state == KeyboardKeyState::Pressed) {
+ if (!m_pressedKeys.contains(key)) {
+ m_pressedKeys.append(key);
+diff --git a/src/keyboard_input.h b/src/keyboard_input.h
+index 9955e6c1d4b..e4986876805 100644
+--- a/src/keyboard_input.h
++++ b/src/keyboard_input.h
+@@ -8,6 +8,7 @@
+ */
+ #pragma once
+
++#include "a11ykeyboardmonitor.h"
+ #include "input.h"
+
+ #include <QObject>
+@@ -85,6 +86,7 @@ private:
+ KeyboardLayout *m_keyboardLayout = nullptr;
+ QList<uint32_t> m_pressedKeys;
+ QList<uint32_t> m_filteredKeys;
++ A11yKeyboardMonitor m_a11yKeyboardMonitor;
+ };
+
+ }
+diff --git a/src/org.freedesktop.a11y.xml b/src/org.freedesktop.a11y.xml
+new file mode 100644
+index 00000000000..4f1d210423f
+--- /dev/null
++++ b/src/org.freedesktop.a11y.xml
+@@ -0,0 +1,119 @@
++<!DOCTYPE node PUBLIC
++'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
++'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
++<node>
++ <!--
++ org.freedesktop.a11y.KeyboardMonitor:
++ @short_description: interface for monitoring of keyboard input by assistive technologies
++
++ This interface is used by assistive technologies to monitor keyboard
++ input of the compositor. The compositor is expected to listen on
++ the well-known bus name "org.freedesktop.a11y.Manager" at the object
++ path "/org/freedesktop/a11y/Manager".
++ -->
++ <interface name="org.freedesktop.a11y.KeyboardMonitor">
++ <!--
++ GrabKeyboard:
++
++ Starts grabbing all key events. The client receives the events
++ through the KeyEvent signal, and in addition, the events aren't handled
++ normally by the compositor. This includes changes to the state
++ of toggles like Caps Lock, Num Lock, and Scroll Lock.
++
++ This behavior stays in effect until the same client calls
++ UngrabKeyboard or closes its D-Bus connection.
++ -->
++ <method name="GrabKeyboard" />
++
++ <!--
++ UngrabKeyboard:
++
++ Reverses the effect of calling GrabKeyboard. If GrabKeyboard wasn't
++ previously called, this method does nothing.
++
++ After calling this method, the key grabs specified in the last call
++ to SetKeyGrabs, if any, are still in effect.
++ Also, the client will still receive key events through the KeyEvent
++ signal, if it has called WatchKeyboard.
++ -->
++ <method name="UngrabKeyboard" />
++
++ <!--
++ WatchKeyboard:
++
++ Starts watching all key events. The client receives the events
++ through the KeyEvent signal, but the events are still handled
++ normally by the compositor. This includes changes to the state
++ of toggles like Caps Lock, Num Lock, and Scroll Lock.
++
++ This behavior stays in effect until the same client calls
++ UnwatchKeyboard or closes its D-Bus connection.
++ -->
++ <method name="WatchKeyboard" />
++
++ <!--
++ UnwatchKeyboard:
++
++ Reverses the effect of calling WatchKeyboard. If WatchKeyboard wasn't
++ previously called, this method does nothing.
++
++ After calling this method, the key grabs specified in the last call
++ to SetKeyGrabs, if any, are still in effect,
++ but other key events are no longer reported to this client.
++ -->
++ <method name="UnwatchKeyboard" />
++
++ <!--
++ SetKeyGrabs:
++ @modifiers: set of custom modifiers to grab
++ @keystrokes: set of keystrokes without custom modifiers to grab
++
++ Sets the current key grabs for the calling client, overriding
++ any previous call to this method. For grabbed key events, the
++ KeyEvent signal is emitted, and normal key event handling
++ is suppressed, including state changes for toggles like Caps Lock
++ and Num Lock.
++
++ The grabs set by this method stay in effect until the same client
++ calls this method again, or until that client closes its D-Bus
++ connection.
++
++ Each item in @modifiers is an XKB keysym. All keys in this list
++ will be grabbed, and keys pressed while any of these keys are down
++ will also be grabbed.
++
++ Each item in @keystrokes is a struct with the following fields:
++
++ - the XKB keysym of the non-modifier key
++ - the XKB modifier mask of the modifiers, if any, for this keystroke
++
++ If any of the keys in @modifiers is pressed alone, the compositor
++ is required to ignore the key press and release event if a second
++ key press of the same modifier is not received within a reasonable
++ time frame, for example, the key repeat delay.
++ If such event is received, this second event is processed normally.
++ -->
++ <method name="SetKeyGrabs">
++ <arg type="au" name="modifiers" direction="in" />
++ <arg type="a(uu)" name="keystrokes" direction="in" />
++ </method>
++
++ <!--
++ KeyEvent:
++ @released: whether this is a key-up event
++ @state: XKB modifier mask for currently pressed modifiers
++ @keysym: XKB keysym for this key
++ @unichar: Unicode character for this key, or 0 if none
++ @keycode: hardware-dependent keycode for this key
++
++ The compositor emits this signal for each key press or release.
++ -->
++ <signal name="KeyEvent">
++ <arg type="b" name="released" direction="in" />
++ <arg type="u" name="state" direction="in" />
++ <arg type="u" name="keysym" direction="in" />
++ <arg type="u" name="unichar" direction="in" />
++ <arg type="q" name="keycode" direction="in" />
++ </signal>
++ </interface>
++</node>
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_267fa4ec_Fix-activate-and-raise-action-with-panels.patch kwin-6.3.6/debian/patches/upstream_267fa4ec_Fix-activate-and-raise-action-with-panels.patch
--- kwin-6.3.5/debian/patches/upstream_267fa4ec_Fix-activate-and-raise-action-with-panels.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_267fa4ec_Fix-activate-and-raise-action-with-panels.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,68 @@
+From 267fa4ece648dd1a5b4c65f8af34a84f20f428f3 Mon Sep 17 00:00:00 2001
+From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+Date: Fri, 11 Jul 2025 09:39:37 +0000
+Subject: [PATCH] Fix "activate and raise" action with panels
+
+With the "activate and raise" action, the window manager should activate
+and raise the window but not pass the click.
+
+If a window is focusable, then only the mouse press should be filtered
+out.
+
+If a window is not focusable, there is some sophisticated code to see
+whether the window is obstructed by any other window (because isActive()
+is always false for such windows so we need another way to determine
+whether it's the first click). If the window is obstructed, then no click
+will be passed but the action will still be performed, in other words,
+the window will be activated (noop) and raised. However, this breaks if
+a window has transients, which is the case with panels. We want the click
+to be passed along even though the window is covered by a child (it
+doesn't matter whether it accepts focus).
+
+Given that primarily only special surfaces don't accept focus, e.g.
+panels, and they are expected to receive clicks, the special stacking
+order code path can be replaced with a `return false;` statement. Which
+is also identical to the MouseActivate case.
+
+See also e8dad997fae98e3c0c40653667dd22a002747556
+
+BUG: 461414
+
+
+(cherry picked from commit f73200def99d7511955c3c698b53832cd3bb9404)
+
+Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+---
+ src/window.cpp | 21 +--------------------
+ 1 file changed, 1 insertion(+), 20 deletions(-)
+
+diff --git a/src/window.cpp b/src/window.cpp
+index e535ee3c40f..d70e9bccfe9 100644
+--- a/src/window.cpp
++++ b/src/window.cpp
+@@ -2128,21 +2128,9 @@ bool Window::mousePressCommandConsumesEvent(Options::MouseCommand command) const
+ break;
+ case Options::MouseActivateAndRaise: {
+ replay = isActive(); // for clickraise mode
+- bool mustReplay = !rules()->checkAcceptFocus(acceptsFocus());
+- if (mustReplay) {
+- auto it = workspace()->stackingOrder().constEnd(),
+- begin = workspace()->stackingOrder().constBegin();
+- while (mustReplay && --it != begin && *it != this) {
+- auto c = *it;
+- if (!c->isClient() || (c->keepAbove() && !keepAbove()) || (keepBelow() && !c->keepBelow())) {
+- continue; // can never raise above "it"
+- }
+- mustReplay = !(c->isOnCurrentDesktop() && c->isOnCurrentActivity() && c->frameGeometry().intersects(frameGeometry()));
+- }
+- }
+ workspace()->takeActivity(this, Workspace::ActivityFocus | Workspace::ActivityRaise);
+ workspace()->setActiveOutput(globalPos);
+- replay = replay || mustReplay;
++ replay = replay || !rules()->checkAcceptFocus(acceptsFocus());
+ break;
+ }
+ case Options::MouseActivateAndLower:
+ --
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_27d28c9c_backends-drm-reduce-severity-of-pageflip-failure-logging.patch kwin-6.3.6/debian/patches/upstream_27d28c9c_backends-drm-reduce-severity-of-pageflip-failure-logging.patch
--- kwin-6.3.5/debian/patches/upstream_27d28c9c_backends-drm-reduce-severity-of-pageflip-failure-logging.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_27d28c9c_backends-drm-reduce-severity-of-pageflip-failure-logging.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,33 +0,0 @@
-From 27d28c9cf3070de9b3102bf4d7dd93cf9d9ebf38 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Mon, 2 Jun 2025 14:46:25 +0200
-Subject: [PATCH] backends/drm: reduce severity of pageflip failure logging
-
-It can happen with attempted direct scanout
-
-BUG: 505028
-
-
-(cherry picked from commit f061994d57e2b0cd2d2dd8ddccdd8a349624d4c4)
-
-Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
----
- src/backends/drm/drm_pipeline_legacy.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/backends/drm/drm_pipeline_legacy.cpp b/src/backends/drm/drm_pipeline_legacy.cpp
-index b8099fb59e4..1f796dfa33e 100644
---- a/src/backends/drm/drm_pipeline_legacy.cpp
-+++ b/src/backends/drm/drm_pipeline_legacy.cpp
-@@ -35,7 +35,7 @@ DrmPipeline::Error DrmPipeline::presentLegacy(const std::shared_ptr<OutputFrame>
- }
- auto commit = std::make_unique<DrmLegacyCommit>(this, buffer, frame);
- if (!commit->doPageflip(m_pending.presentationMode)) {
-- qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
-+ qCDebug(KWIN_DRM) << "Page flip failed:" << strerror(errno);
- return errnoToError();
- }
- m_commitThread->setPendingCommit(std::move(commit));
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_2d70fe62_plugins-colorpicker-use-BPC-when-converting-to-sRGB.patch kwin-6.3.6/debian/patches/upstream_2d70fe62_plugins-colorpicker-use-BPC-when-converting-to-sRGB.patch
--- kwin-6.3.5/debian/patches/upstream_2d70fe62_plugins-colorpicker-use-BPC-when-converting-to-sRGB.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_2d70fe62_plugins-colorpicker-use-BPC-when-converting-to-sRGB.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,29 +0,0 @@
-From 2d70fe6248591bda75e7fba2d07cd80e3eec3d72 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@kde.org>
-Date: Thu, 22 May 2025 01:03:38 +0200
-Subject: [PATCH] plugins/colorpicker: use BPC when converting to sRGB
-
-It's how most applications present to the screen; without BPC the roundtrip of
-picking an sRGB color off the screen may not work out
-
-CCBUG: 491633
----
- src/plugins/colorpicker/colorpicker.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/plugins/colorpicker/colorpicker.cpp b/src/plugins/colorpicker/colorpicker.cpp
-index dd9204f0af1..77ebd1d29bb 100644
---- a/src/plugins/colorpicker/colorpicker.cpp
-+++ b/src/plugins/colorpicker/colorpicker.cpp
-@@ -69,7 +69,7 @@ void ColorPickerEffect::paintScreen(const RenderTarget &renderTarget, const Rend
- OpenGlContext *context = effects->openglContext();
-
- context->glReadnPixels(texturePosition.x(), renderTarget.size().height() - texturePosition.y() - PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE, GL_RGBA, GL_FLOAT, sizeof(float) * data.size(), data.data());
-- QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB, RenderingIntent::RelativeColorimetric);
-+ QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB, RenderingIntent::RelativeColorimetricWithBPC);
- QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(sRGB.x(), sRGB.y(), sRGB.z())));
- setPicking(false);
- m_scheduledPosition = QPoint(-1, -1);
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_3c724b83_wayland-colormanagement-also-around-max-fall-and-max-cll.patch kwin-6.3.6/debian/patches/upstream_3c724b83_wayland-colormanagement-also-around-max-fall-and-max-cll.patch
--- kwin-6.3.5/debian/patches/upstream_3c724b83_wayland-colormanagement-also-around-max-fall-and-max-cll.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_3c724b83_wayland-colormanagement-also-around-max-fall-and-max-cll.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,32 +0,0 @@
-From 3c724b83719dfb1ea6a09a1ab2b0af832a27e04d Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Tue, 20 May 2025 01:13:46 +0200
-Subject: [PATCH] wayland/colormanagement: also around max_fall and max_cll
-
-To be consistent with the other luminance properties
-
-(cherry picked from commit f8e4168a084a350114968e1eb17902c16092f441)
----
- src/wayland/colormanagement_v1.cpp | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/wayland/colormanagement_v1.cpp b/src/wayland/colormanagement_v1.cpp
-index 979517f70f6..4a721bbc6cd 100644
---- a/src/wayland/colormanagement_v1.cpp
-+++ b/src/wayland/colormanagement_v1.cpp
-@@ -528,10 +528,10 @@ void ImageDescriptionV1::wp_image_description_v1_get_information(Resource *qtRes
- round(masterWhite.x), round(masterWhite.y));
- }
- if (auto maxfall = m_description->maxAverageLuminance()) {
-- wp_image_description_info_v1_send_target_max_fall(resource, *maxfall);
-+ wp_image_description_info_v1_send_target_max_fall(resource, std::round(*maxfall));
- }
- if (auto maxcll = m_description->maxHdrLuminance()) {
-- wp_image_description_info_v1_send_target_max_cll(resource, *maxcll);
-+ wp_image_description_info_v1_send_target_max_cll(resource, std::round(*maxcll));
- }
- wp_image_description_info_v1_send_luminances(resource, std::round(m_description->transferFunction().minLuminance / s_minLuminanceUnit), std::round(m_description->transferFunction().maxLuminance), std::round(m_description->referenceLuminance()));
- wp_image_description_info_v1_send_target_luminance(resource, std::round(m_description->minLuminance() / s_minLuminanceUnit), std::round(m_description->maxHdrLuminance().value_or(800)));
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_3f0a8a88_plugins-dimscreen-Enable-by-default.patch kwin-6.3.6/debian/patches/upstream_3f0a8a88_plugins-dimscreen-Enable-by-default.patch
--- kwin-6.3.5/debian/patches/upstream_3f0a8a88_plugins-dimscreen-Enable-by-default.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_3f0a8a88_plugins-dimscreen-Enable-by-default.patch 2025-07-18 11:21:52.000000000 +0200
@@ -9,11 +9,9 @@
src/plugins/dimscreen/package/metadata.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
-diff --git a/src/plugins/dimscreen/package/metadata.json b/src/plugins/dimscreen/package/metadata.json
-index 3efb222ceda..cba7dd4dcd0 100644
--- a/src/plugins/dimscreen/package/metadata.json
+++ b/src/plugins/dimscreen/package/metadata.json
-@@ -99,7 +99,7 @@
+@@ -101,7 +101,7 @@
"Description[x-test]": "xxDarkens the entire screen when requesting root privilegesxx",
"Description[zh_CN]": "系统请求 root 权限时降低屏幕亮度",
"Description[zh_TW]": "請求 root 權限時將整個螢幕變暗",
@@ -22,6 +20,3 @@
"Icon": "preferences-system-windows-effect-dimscreen",
"Id": "dimscreen",
"License": "GPL",
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_473aebf0_x11-Add-an-environment-variable-to-disable-NET-WM-SYNC-REQUEST-in-X11Window.patch kwin-6.3.6/debian/patches/upstream_473aebf0_x11-Add-an-environment-variable-to-disable-NET-WM-SYNC-REQUEST-in-X11Window.patch
--- kwin-6.3.5/debian/patches/upstream_473aebf0_x11-Add-an-environment-variable-to-disable-NET-WM-SYNC-REQUEST-in-X11Window.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_473aebf0_x11-Add-an-environment-variable-to-disable-NET-WM-SYNC-REQUEST-in-X11Window.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-From 473aebf0f752c5fccb1c270841c14c1bb345e9d9 Mon Sep 17 00:00:00 2001
-From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
-Date: Wed, 21 May 2025 20:36:31 +0000
-Subject: [PATCH] x11: Add an environment variable to disable
- _NET_WM_SYNC_REQUEST in X11Window
-
-This can be useful for debugging purposes.
-
-
-(cherry picked from commit 9b48a9ec8a1be84403f322f7f07d5ef89324ec2e)
-
-Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
----
- src/x11window.cpp | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/x11window.cpp b/src/x11window.cpp
-index 218bc76af9a..b0e0b3e52a6 100644
---- a/src/x11window.cpp
-+++ b/src/x11window.cpp
-@@ -2529,6 +2529,11 @@ void X11Window::getSyncCounter()
- return;
- }
-
-+ static bool noXsync = qEnvironmentVariableIntValue("KWIN_X11_NO_SYNC_REQUEST") == 1;
-+ if (noXsync) {
-+ return;
-+ }
-+
- Xcb::Property syncProp(false, window(), atoms->net_wm_sync_request_counter, XCB_ATOM_CARDINAL, 0, 1);
- const xcb_sync_counter_t counter = syncProp.value<xcb_sync_counter_t>(XCB_NONE);
- if (counter != XCB_NONE) {
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_48ddfb59_wayland-Send-wl-data-source-cancelled-if-wl-data-device-start-drag-is-rejected.patch kwin-6.3.6/debian/patches/upstream_48ddfb59_wayland-Send-wl-data-source-cancelled-if-wl-data-device-start-drag-is-rejected.patch
--- kwin-6.3.5/debian/patches/upstream_48ddfb59_wayland-Send-wl-data-source-cancelled-if-wl-data-device-start-drag-is-rejected.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_48ddfb59_wayland-Send-wl-data-source-cancelled-if-wl-data-device-start-drag-is-rejected.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,38 @@
+From 48ddfb592d1c84000936857a8a752fc2efec4c54 Mon Sep 17 00:00:00 2001
+From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+Date: Wed, 4 Jun 2025 20:25:49 +0000
+Subject: [PATCH] wayland: Send wl_data_source.cancelled if
+ wl_data_device.start_drag is rejected
+
+If a dnd operation is rejected by the start_drag request, currently, the
+client has no any feedback about it.
+
+This change makes kwin send a wl_data_source.cancelled similar to what
+weston does. Note that with internal drags, no feedback will still be
+provided though.
+
+
+(cherry picked from commit d9249be845b788b1e22e9944468c26612d37730b)
+
+Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+---
+ src/wayland/datadevice.cpp | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/wayland/datadevice.cpp b/src/wayland/datadevice.cpp
+index 1becb8290df..eb514528752 100644
+--- a/src/wayland/datadevice.cpp
++++ b/src/wayland/datadevice.cpp
+@@ -96,6 +96,9 @@ void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
+ const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->isSurfaceTouched(focusSurface);
+ if (!touchGrab) {
+ // Client neither has pointer nor touch grab. No drag start allowed.
++ if (dataSource) {
++ dataSource->dndCancelled();
++ }
+ return;
+ }
+ }
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_51c0880f_wayland-close-popups-upon-window-activation.patch kwin-6.3.6/debian/patches/upstream_51c0880f_wayland-close-popups-upon-window-activation.patch
--- kwin-6.3.5/debian/patches/upstream_51c0880f_wayland-close-popups-upon-window-activation.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_51c0880f_wayland-close-popups-upon-window-activation.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,151 @@
+From 51c0880f47ecd2e29ceb61e9dd9b30a535d18ff8 Mon Sep 17 00:00:00 2001
+From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+Date: Wed, 9 Jul 2025 12:22:28 +0000
+Subject: [PATCH] wayland: close popups upon window activation
+
+Popups should grab all input on Wayland, however there are numerous
+(deliberate) ways to get outside the grab, such as a shortcut that opens
+a new window or pressing alt-tab.
+
+If a popup is active when the focus changes we are left in a state where
+the new window visibly has focus and appears on top of the popup's main
+window, but the popup is still visible with an input grab. This is a
+broken state.
+
+This patch cancels popups if the focus window changes for whatever
+reason.
+
+Cancelling a popup also destroys the window server side rather than
+waiting for the client to handle the popupDone so there is no racey
+aspect for the next input.
+
+BUG: 497075
+
+
+(cherry picked from commit 43f7621eb1ef22704c4c12c21c29945bffa37e47)
+
+Co-authored-by: David Edmundson <kde@davidedmundson.co.uk>
+---
+ autotests/integration/xdgshellwindow_test.cpp | 45 +++++++++++++++++++
+ src/popup_input_filter.cpp | 7 +++
+ src/popup_input_filter.h | 2 +-
+ 3 files changed, 53 insertions(+), 1 deletion(-)
+
+diff --git a/autotests/integration/xdgshellwindow_test.cpp b/autotests/integration/xdgshellwindow_test.cpp
+index b7d3946b97f..0903043808d 100644
+--- a/autotests/integration/xdgshellwindow_test.cpp
++++ b/autotests/integration/xdgshellwindow_test.cpp
+@@ -13,6 +13,7 @@
+ #include "decorations/decorationbridge.h"
+ #include "decorations/settings.h"
+ #include "pointer_input.h"
++
+ #include "virtualdesktops.h"
+ #include "wayland/clientconnection.h"
+ #include "wayland/display.h"
+@@ -41,6 +42,8 @@
+ #include <sys/types.h>
+ #include <unistd.h>
+
++#include <linux/input.h>
++
+ #include <csignal>
+
+ using namespace KWin;
+@@ -111,6 +114,7 @@ private Q_SLOTS:
+ void testCloseInactiveModal();
+ void testClosePopupOnParentUnmapped();
+ void testPopupWithDismissedParent();
++ void testPopupDismissedOnFocusChange();
+ void testMinimumSize();
+ void testNoMinimumSize();
+ void testMaximumSize();
+@@ -2417,6 +2421,47 @@ void TestXdgShellWindow::testPopupWithDismissedParent()
+ QVERIFY(grandChildDoneSpy.wait());
+ }
+
++void TestXdgShellWindow::testPopupDismissedOnFocusChange()
++{
++ // This test verifies that a popup window will be dismissed when the focus changes.
++
++ std::unique_ptr<KWayland::Client::Surface> parentSurface = Test::createSurface();
++ std::unique_ptr<Test::XdgToplevel> parentToplevel = Test::createXdgToplevelSurface(parentSurface.get());
++ std::unique_ptr<KWayland::Client::Pointer> pointer(Test::waylandSeat()->createPointer());
++ Window *parent = Test::renderAndWaitForShown(parentSurface.get(), QSize(200, 200), Qt::cyan);
++ QVERIFY(parent);
++
++ QSignalSpy buttonSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged);
++ input()->pointer()->warp(parent->frameGeometry().center());
++ // simulate press
++ quint32 timestamp = 1;
++ Test::pointerButtonPressed(BTN_LEFT, timestamp++);
++ QVERIFY(buttonSpy.wait());
++
++ std::unique_ptr<Test::XdgPositioner> positioner = Test::createXdgPositioner();
++ positioner->set_size(10, 10);
++ positioner->set_anchor_rect(10, 10, 10, 10);
++
++ std::unique_ptr<KWayland::Client::Surface> childSurface = Test::createSurface();
++ std::unique_ptr<Test::XdgPopup> popup = Test::createXdgPopupSurface(childSurface.get(), parentToplevel->xdgSurface(), positioner.get());
++ popup->grab(*Test::waylandSeat(), buttonSpy.first().first().value<quint32>());
++ QPointer<Window> child = Test::renderAndWaitForShown(childSurface.get(), QSize(10, 10), Qt::cyan);
++ QVERIFY(child);
++
++ QSignalSpy popupDismissedSpy(popup.get(), &Test::XdgPopup::doneReceived);
++
++ // create another toplevel that gets focus
++ std::unique_ptr<KWayland::Client::Surface> otherSurface = Test::createSurface();
++ std::unique_ptr<Test::XdgToplevel> otherToplevel = Test::createXdgToplevelSurface(otherSurface.get());
++ Window *other = Test::renderAndWaitForShown(otherSurface.get(), QSize(200, 200), Qt::cyan);
++ QVERIFY(other);
++
++ workspace()->setActiveWindow(other);
++
++ QVERIFY(popupDismissedSpy.wait());
++ QVERIFY(!child); // and the server-side window closed immediately too
++}
++
+ void TestXdgShellWindow::testMinimumSize()
+ {
+ // NOTE: a minimum size of 20px is forced by the compositor
+diff --git a/src/popup_input_filter.cpp b/src/popup_input_filter.cpp
+index 50e47d07021..0fd1d602c3e 100644
+--- a/src/popup_input_filter.cpp
++++ b/src/popup_input_filter.cpp
+@@ -24,6 +24,7 @@ PopupInputFilter::PopupInputFilter()
+ , InputEventFilter(InputFilterOrder::Popup)
+ {
+ connect(workspace(), &Workspace::windowAdded, this, &PopupInputFilter::handleWindowAdded);
++ connect(workspace(), &Workspace::windowActivated, this, &PopupInputFilter::handleWindowFocusChanged);
+ }
+
+ void PopupInputFilter::handleWindowAdded(Window *window)
+@@ -48,6 +49,12 @@ void PopupInputFilter::handleWindowAdded(Window *window)
+ }
+ }
+
++void PopupInputFilter::handleWindowFocusChanged()
++{
++ // user focussed a window through another mechanism such as a shortcut
++ cancelPopups();
++}
++
+ bool PopupInputFilter::pointerButton(PointerButtonEvent *event)
+ {
+ if (m_popupWindows.isEmpty()) {
+diff --git a/src/popup_input_filter.h b/src/popup_input_filter.h
+index 0950666fa8b..622f62b5ccf 100644
+--- a/src/popup_input_filter.h
++++ b/src/popup_input_filter.h
+@@ -27,7 +27,7 @@ public:
+
+ private:
+ void handleWindowAdded(Window *client);
+-
++ void handleWindowFocusChanged();
+ void focus(Window *popup);
+ void cancelPopups();
+
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_56760937_backends-drm-clear-the-test-buffer-with-legacy-modesetting.patch kwin-6.3.6/debian/patches/upstream_56760937_backends-drm-clear-the-test-buffer-with-legacy-modesetting.patch
--- kwin-6.3.5/debian/patches/upstream_56760937_backends-drm-clear-the-test-buffer-with-legacy-modesetting.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_56760937_backends-drm-clear-the-test-buffer-with-legacy-modesetting.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,39 +0,0 @@
-From 56760937743b42f50f46e2ff2a1fa811502132d7 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Fri, 23 May 2025 14:30:44 +0200
-Subject: [PATCH] backends/drm: clear the test buffer with legacy modesetting
-
-With atomic modesetting, we only show the buffer once the compositor has rendered to
-it. With legacy modesetting however, the "test" the drm backend does immediately shows
-the buffer, so not clearing it may mean we show a frame of random VRAM contents.
-
-BUG: 504258
-
-
-(cherry picked from commit ef88b6425278564096d9394537ca9442d750c0cf)
-
-Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
----
- src/backends/drm/drm_egl_layer_surface.cpp | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp
-index 129024da9d7..eb4f9189cf5 100644
---- a/src/backends/drm/drm_egl_layer_surface.cpp
-+++ b/src/backends/drm/drm_egl_layer_surface.cpp
-@@ -594,6 +594,12 @@ std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::doRenderTestBuffer(Surface *
- if (!slot) {
- return nullptr;
- }
-+ if (!m_gpu->atomicModeSetting()) {
-+ EglContext::currentContext()->pushFramebuffer(slot->framebuffer());
-+ glClearColor(0, 0, 0, 0);
-+ glClear(GL_COLOR_BUFFER_BIT);
-+ EglContext::currentContext()->popFramebuffer();
-+ }
- if (const auto ret = importBuffer(surface, slot.get(), FileDescriptor{}, nullptr, infiniteRegion())) {
- surface->currentSlot = slot;
- surface->currentFramebuffer = ret;
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_572fee09_effects-slideback-Also-check-activity-when-matching-windows.patch kwin-6.3.6/debian/patches/upstream_572fee09_effects-slideback-Also-check-activity-when-matching-windows.patch
--- kwin-6.3.5/debian/patches/upstream_572fee09_effects-slideback-Also-check-activity-when-matching-windows.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_572fee09_effects-slideback-Also-check-activity-when-matching-windows.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,37 @@
+From 572fee09a82c9cc51c3fc10d09c989dc4b360766 Mon Sep 17 00:00:00 2001
+From: David Edmundson <david@davidedmundson.co.uk>
+Date: Tue, 27 May 2025 15:15:39 +0000
+Subject: [PATCH] effects/slideback: Also check activity when matching windows
+
+When a window is raised, we compare which windows we have to move.
+
+Otherwise it gets managed, but never painted, and thus never cleared,
+this corrupts the stacking order.
+
+BUG: 455429
+
+
+(cherry picked from commit 0ed015cc8adc86c860b3a87555b2574d94999a4c)
+
+Co-authored-by: David Edmundson <kde@davidedmundson.co.uk>
+---
+ src/plugins/slideback/slideback.cpp | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/plugins/slideback/slideback.cpp b/src/plugins/slideback/slideback.cpp
+index 21e5e295a11..7ea188847b7 100644
+--- a/src/plugins/slideback/slideback.cpp
++++ b/src/plugins/slideback/slideback.cpp
+@@ -67,7 +67,8 @@ void SlideBackEffect::windowRaised(EffectWindow *w)
+ currentFound = true;
+ }
+ } else {
+- if (isWindowUsable(tmp) && tmp->isOnCurrentDesktop() && w->isOnCurrentDesktop()) {
++ if (isWindowUsable(tmp) && tmp->isOnCurrentDesktop() && w->isOnCurrentDesktop()
++ && tmp->isOnCurrentActivity() && w->isOnCurrentActivity()) {
+ // Do we have to move it?
+ if (intersects(w, tmp->frameGeometry().toRect())) {
+ QRect slideRect;
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_5ba6279b_A11yKeyboardMonitor-Fix-sending-keycodes-to-AT.patch kwin-6.3.6/debian/patches/upstream_5ba6279b_A11yKeyboardMonitor-Fix-sending-keycodes-to-AT.patch
--- kwin-6.3.5/debian/patches/upstream_5ba6279b_A11yKeyboardMonitor-Fix-sending-keycodes-to-AT.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_5ba6279b_A11yKeyboardMonitor-Fix-sending-keycodes-to-AT.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,62 @@
+From 5ba6279b1b2e16b38d0addf6241b770d0cd4adac Mon Sep 17 00:00:00 2001
+From: Nicolas Fella <nicolas.fella@gmx.de>
+Date: Tue, 1 Jul 2025 19:15:35 +0200
+Subject: [PATCH] A11yKeyboardMonitor: Fix sending keycodes to AT
+
+processKey receives scancodes, we need to convert to keycodes when emitting KeyEvent
+
+BUG: 506445
+---
+ src/a11ykeyboardmonitor.cpp | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/src/a11ykeyboardmonitor.cpp b/src/a11ykeyboardmonitor.cpp
+index 1bb839d798b..3b7e5fa357a 100644
+--- a/src/a11ykeyboardmonitor.cpp
++++ b/src/a11ykeyboardmonitor.cpp
+@@ -46,16 +46,17 @@ bool A11yKeyboardMonitor::processKey(uint32_t key, KeyboardKeyState state, std::
+ const auto text = input()->keyboard()->xkb()->toString(keysym);
+ const quint32 unicode = text.isEmpty() ? 0 : text.at(0).unicode();
+ const bool released = state == KeyboardKeyState::Released;
++ const auto keycode = key + 8;
+
+ for (auto [name, data] : m_clients.asKeyValueRange()) {
+ if (data.grabbed) {
+- emitKeyEvent(name, released, mods, keysym, unicode, key);
++ emitKeyEvent(name, released, mods, keysym, unicode, keycode);
+ }
+
+ // if any of the grabbed modifiers is currently down, grab the key
+ for (const auto grabbedMod : data.modifiers) {
+ if (grabbedMod != keysym && data.pressedModifiers.contains(grabbedMod)) {
+- emitKeyEvent(name, released, mods, keysym, unicode, key);
++ emitKeyEvent(name, released, mods, keysym, unicode, keycode);
+ return true;
+ }
+ }
+@@ -83,19 +84,19 @@ bool A11yKeyboardMonitor::processKey(uint32_t key, KeyboardKeyState state, std::
+ data.pressedModifiers.insert(keysym);
+ }
+
+- emitKeyEvent(name, released, mods, keysym, unicode, key);
++ emitKeyEvent(name, released, mods, keysym, unicode, keycode);
+ return true;
+ }
+
+ for (const KeyStroke &stroke : std::as_const(data.keys)) {
+ if (mods == stroke.modifiers && stroke.keysym == keysym) {
+- emitKeyEvent(name, released, mods, keysym, unicode, key);
++ emitKeyEvent(name, released, mods, keysym, unicode, keycode);
+ return true;
+ }
+ }
+
+ if (data.watched) {
+- emitKeyEvent(name, released, mods, keysym, unicode, key);
++ emitKeyEvent(name, released, mods, keysym, unicode, keycode);
+ }
+ }
+
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_5c7d2de5_xwayland-Fix-leaking-normal-key-presses-with-keyboard-layouts-other-than-English.patch kwin-6.3.6/debian/patches/upstream_5c7d2de5_xwayland-Fix-leaking-normal-key-presses-with-keyboard-layouts-other-than-English.patch
--- kwin-6.3.5/debian/patches/upstream_5c7d2de5_xwayland-Fix-leaking-normal-key-presses-with-keyboard-layouts-other-than-English.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_5c7d2de5_xwayland-Fix-leaking-normal-key-presses-with-keyboard-layouts-other-than-English.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,365 +0,0 @@
-From 5c7d2de59d6afda320feab9c08a2dd3c4cbc728a Mon Sep 17 00:00:00 2001
-From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
-Date: Wed, 28 May 2025 08:36:38 +0000
-Subject: [PATCH] xwayland: Fix leaking normal key presses with keyboard
- layouts other than English
-
-When using a keyboard layout that's not English, QXkbCommon will report
-a unicode character code for the key code.
-
-In order to address that, this change makes the input sniffing code
-filter out any key with code less than 0x01000000.
-
-Key codes are split in two parts: 0-0x01000000: regular keys (there is
-strong correlation with Unicode codes); and 0x01000000-...: other
-custom keys, e.g. F1, Escape, Enter, etc.
-
-BUG: 500032
-
-
-(cherry picked from commit 68e2fc1a32beab64c072d14a14efc60f36d8a04a)
-
-Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
----
- src/xwayland/xwayland.cpp | 313 +++++++++++---------------------------
- 1 file changed, 93 insertions(+), 220 deletions(-)
-
-diff --git a/src/xwayland/xwayland.cpp b/src/xwayland/xwayland.cpp
-index 755f39ea82d..8aeddf6f9c7 100644
---- a/src/xwayland/xwayland.cpp
-+++ b/src/xwayland/xwayland.cpp
-@@ -118,224 +118,97 @@ public:
- Qt::MetaModifier,
- };
-
-- static const QSet<quint32> characterKeys = {
-- Qt::Key_Any,
-- Qt::Key_Space,
-- Qt::Key_Exclam,
-- Qt::Key_QuoteDbl,
-- Qt::Key_NumberSign,
-- Qt::Key_Dollar,
-- Qt::Key_Percent,
-- Qt::Key_Ampersand,
-- Qt::Key_Apostrophe,
-- Qt::Key_ParenLeft,
-- Qt::Key_ParenRight,
-- Qt::Key_Asterisk,
-- Qt::Key_Plus,
-- Qt::Key_Comma,
-- Qt::Key_Minus,
-- Qt::Key_Period,
-- Qt::Key_Slash,
-- Qt::Key_0,
-- Qt::Key_1,
-- Qt::Key_2,
-- Qt::Key_3,
-- Qt::Key_4,
-- Qt::Key_5,
-- Qt::Key_6,
-- Qt::Key_7,
-- Qt::Key_8,
-- Qt::Key_9,
-- Qt::Key_Colon,
-- Qt::Key_Semicolon,
-- Qt::Key_Less,
-- Qt::Key_Equal,
-- Qt::Key_Greater,
-- Qt::Key_Question,
-- Qt::Key_At,
-- Qt::Key_A,
-- Qt::Key_B,
-- Qt::Key_C,
-- Qt::Key_D,
-- Qt::Key_E,
-- Qt::Key_F,
-- Qt::Key_G,
-- Qt::Key_H,
-- Qt::Key_I,
-- Qt::Key_J,
-- Qt::Key_K,
-- Qt::Key_L,
-- Qt::Key_M,
-- Qt::Key_N,
-- Qt::Key_O,
-- Qt::Key_P,
-- Qt::Key_Q,
-- Qt::Key_R,
-- Qt::Key_S,
-- Qt::Key_T,
-- Qt::Key_U,
-- Qt::Key_V,
-- Qt::Key_W,
-- Qt::Key_X,
-- Qt::Key_Y,
-- Qt::Key_Z,
-- Qt::Key_BracketLeft,
-- Qt::Key_Backslash,
-- Qt::Key_BracketRight,
-- Qt::Key_AsciiCircum,
-- Qt::Key_Underscore,
-- Qt::Key_QuoteLeft,
-- Qt::Key_BraceLeft,
-- Qt::Key_Bar,
-- Qt::Key_BraceRight,
-- Qt::Key_AsciiTilde,
-- Qt::Key_nobreakspace,
-- Qt::Key_exclamdown,
-- Qt::Key_cent,
-- Qt::Key_sterling,
-- Qt::Key_currency,
-- Qt::Key_yen,
-- Qt::Key_brokenbar,
-- Qt::Key_section,
-- Qt::Key_diaeresis,
-- Qt::Key_copyright,
-- Qt::Key_ordfeminine,
-- Qt::Key_guillemotleft,
-- Qt::Key_notsign,
-- Qt::Key_hyphen,
-- Qt::Key_registered,
-- Qt::Key_macron,
-- Qt::Key_degree,
-- Qt::Key_plusminus,
-- Qt::Key_twosuperior,
-- Qt::Key_threesuperior,
-- Qt::Key_acute,
-- Qt::Key_micro,
-- Qt::Key_paragraph,
-- Qt::Key_periodcentered,
-- Qt::Key_cedilla,
-- Qt::Key_onesuperior,
-- Qt::Key_masculine,
-- Qt::Key_guillemotright,
-- Qt::Key_onequarter,
-- Qt::Key_onehalf,
-- Qt::Key_threequarters,
-- Qt::Key_questiondown,
-- Qt::Key_Agrave,
-- Qt::Key_Aacute,
-- Qt::Key_Acircumflex,
-- Qt::Key_Atilde,
-- Qt::Key_Adiaeresis,
-- Qt::Key_Aring,
-- Qt::Key_AE,
-- Qt::Key_Ccedilla,
-- Qt::Key_Egrave,
-- Qt::Key_Eacute,
-- Qt::Key_Ecircumflex,
-- Qt::Key_Ediaeresis,
-- Qt::Key_Igrave,
-- Qt::Key_Iacute,
-- Qt::Key_Icircumflex,
-- Qt::Key_Idiaeresis,
-- Qt::Key_ETH,
-- Qt::Key_Ntilde,
-- Qt::Key_Ograve,
-- Qt::Key_Oacute,
-- Qt::Key_Ocircumflex,
-- Qt::Key_Otilde,
-- Qt::Key_Odiaeresis,
-- Qt::Key_multiply,
-- Qt::Key_Ooblique,
-- Qt::Key_Ugrave,
-- Qt::Key_Uacute,
-- Qt::Key_Ucircumflex,
-- Qt::Key_Udiaeresis,
-- Qt::Key_Yacute,
-- Qt::Key_THORN,
-- Qt::Key_ssharp,
-- Qt::Key_division,
-- Qt::Key_ydiaeresis,
-- Qt::Key_Multi_key,
-- Qt::Key_Codeinput,
-- Qt::Key_SingleCandidate,
-- Qt::Key_MultipleCandidate,
-- Qt::Key_PreviousCandidate,
-- Qt::Key_Mode_switch,
-- Qt::Key_Kanji,
-- Qt::Key_Muhenkan,
-- Qt::Key_Henkan,
-- Qt::Key_Romaji,
-- Qt::Key_Hiragana,
-- Qt::Key_Katakana,
-- Qt::Key_Hiragana_Katakana,
-- Qt::Key_Zenkaku,
-- Qt::Key_Hankaku,
-- Qt::Key_Zenkaku_Hankaku,
-- Qt::Key_Touroku,
-- Qt::Key_Massyo,
-- Qt::Key_Kana_Lock,
-- Qt::Key_Kana_Shift,
-- Qt::Key_Eisu_Shift,
-- Qt::Key_Eisu_toggle,
-- Qt::Key_Hangul,
-- Qt::Key_Hangul_Start,
-- Qt::Key_Hangul_End,
-- Qt::Key_Hangul_Hanja,
-- Qt::Key_Hangul_Jamo,
-- Qt::Key_Hangul_Romaja,
-- Qt::Key_Hangul_Jeonja,
-- Qt::Key_Hangul_Banja,
-- Qt::Key_Hangul_PreHanja,
-- Qt::Key_Hangul_PostHanja,
-- Qt::Key_Hangul_Special,
-- Qt::Key_Dead_Grave,
-- Qt::Key_Dead_Acute,
-- Qt::Key_Dead_Circumflex,
-- Qt::Key_Dead_Tilde,
-- Qt::Key_Dead_Macron,
-- Qt::Key_Dead_Breve,
-- Qt::Key_Dead_Abovedot,
-- Qt::Key_Dead_Diaeresis,
-- Qt::Key_Dead_Abovering,
-- Qt::Key_Dead_Doubleacute,
-- Qt::Key_Dead_Caron,
-- Qt::Key_Dead_Cedilla,
-- Qt::Key_Dead_Ogonek,
-- Qt::Key_Dead_Iota,
-- Qt::Key_Dead_Voiced_Sound,
-- Qt::Key_Dead_Semivoiced_Sound,
-- Qt::Key_Dead_Belowdot,
-- Qt::Key_Dead_Hook,
-- Qt::Key_Dead_Horn,
-- Qt::Key_Dead_Stroke,
-- Qt::Key_Dead_Abovecomma,
-- Qt::Key_Dead_Abovereversedcomma,
-- Qt::Key_Dead_Doublegrave,
-- Qt::Key_Dead_Belowring,
-- Qt::Key_Dead_Belowmacron,
-- Qt::Key_Dead_Belowcircumflex,
-- Qt::Key_Dead_Belowtilde,
-- Qt::Key_Dead_Belowbreve,
-- Qt::Key_Dead_Belowdiaeresis,
-- Qt::Key_Dead_Invertedbreve,
-- Qt::Key_Dead_Belowcomma,
-- Qt::Key_Dead_Currency,
-- Qt::Key_Dead_a,
-- Qt::Key_Dead_A,
-- Qt::Key_Dead_e,
-- Qt::Key_Dead_E,
-- Qt::Key_Dead_i,
-- Qt::Key_Dead_I,
-- Qt::Key_Dead_o,
-- Qt::Key_Dead_O,
-- Qt::Key_Dead_u,
-- Qt::Key_Dead_U,
-- Qt::Key_Dead_Small_Schwa,
-- Qt::Key_Dead_Capital_Schwa,
-- Qt::Key_Dead_Greek,
-- Qt::Key_Dead_Lowline,
-- Qt::Key_Dead_Aboveverticalline,
-- Qt::Key_Dead_Belowverticalline,
-+ static const auto isSpecialKey = [](quint32 key) {
-+ // All keys prior to 0x01000000 (Qt::Key_Escape) are considered as character keys.
-+ if (key < 0x01000000) {
-+ return false;
-+ }
-+
-+ static const QSet<quint32> excludedKeys = {
-+ Qt::Key_Multi_key,
-+ Qt::Key_Codeinput,
-+ Qt::Key_SingleCandidate,
-+ Qt::Key_MultipleCandidate,
-+ Qt::Key_PreviousCandidate,
-+ Qt::Key_Mode_switch,
-+ Qt::Key_Kanji,
-+ Qt::Key_Muhenkan,
-+ Qt::Key_Henkan,
-+ Qt::Key_Romaji,
-+ Qt::Key_Hiragana,
-+ Qt::Key_Katakana,
-+ Qt::Key_Hiragana_Katakana,
-+ Qt::Key_Zenkaku,
-+ Qt::Key_Hankaku,
-+ Qt::Key_Zenkaku_Hankaku,
-+ Qt::Key_Touroku,
-+ Qt::Key_Massyo,
-+ Qt::Key_Kana_Lock,
-+ Qt::Key_Kana_Shift,
-+ Qt::Key_Eisu_Shift,
-+ Qt::Key_Eisu_toggle,
-+ Qt::Key_Hangul,
-+ Qt::Key_Hangul_Start,
-+ Qt::Key_Hangul_End,
-+ Qt::Key_Hangul_Hanja,
-+ Qt::Key_Hangul_Jamo,
-+ Qt::Key_Hangul_Romaja,
-+ Qt::Key_Hangul_Jeonja,
-+ Qt::Key_Hangul_Banja,
-+ Qt::Key_Hangul_PreHanja,
-+ Qt::Key_Hangul_PostHanja,
-+ Qt::Key_Hangul_Special,
-+ Qt::Key_Dead_Grave,
-+ Qt::Key_Dead_Acute,
-+ Qt::Key_Dead_Circumflex,
-+ Qt::Key_Dead_Tilde,
-+ Qt::Key_Dead_Macron,
-+ Qt::Key_Dead_Breve,
-+ Qt::Key_Dead_Abovedot,
-+ Qt::Key_Dead_Diaeresis,
-+ Qt::Key_Dead_Abovering,
-+ Qt::Key_Dead_Doubleacute,
-+ Qt::Key_Dead_Caron,
-+ Qt::Key_Dead_Cedilla,
-+ Qt::Key_Dead_Ogonek,
-+ Qt::Key_Dead_Iota,
-+ Qt::Key_Dead_Voiced_Sound,
-+ Qt::Key_Dead_Semivoiced_Sound,
-+ Qt::Key_Dead_Belowdot,
-+ Qt::Key_Dead_Hook,
-+ Qt::Key_Dead_Horn,
-+ Qt::Key_Dead_Stroke,
-+ Qt::Key_Dead_Abovecomma,
-+ Qt::Key_Dead_Abovereversedcomma,
-+ Qt::Key_Dead_Doublegrave,
-+ Qt::Key_Dead_Belowring,
-+ Qt::Key_Dead_Belowmacron,
-+ Qt::Key_Dead_Belowcircumflex,
-+ Qt::Key_Dead_Belowtilde,
-+ Qt::Key_Dead_Belowbreve,
-+ Qt::Key_Dead_Belowdiaeresis,
-+ Qt::Key_Dead_Invertedbreve,
-+ Qt::Key_Dead_Belowcomma,
-+ Qt::Key_Dead_Currency,
-+ Qt::Key_Dead_a,
-+ Qt::Key_Dead_A,
-+ Qt::Key_Dead_e,
-+ Qt::Key_Dead_E,
-+ Qt::Key_Dead_i,
-+ Qt::Key_Dead_I,
-+ Qt::Key_Dead_o,
-+ Qt::Key_Dead_O,
-+ Qt::Key_Dead_u,
-+ Qt::Key_Dead_U,
-+ Qt::Key_Dead_Small_Schwa,
-+ Qt::Key_Dead_Capital_Schwa,
-+ Qt::Key_Dead_Greek,
-+ Qt::Key_Dead_Lowline,
-+ Qt::Key_Dead_Aboveverticalline,
-+ Qt::Key_Dead_Belowverticalline,
-+ };
-+
-+ return !excludedKeys.contains(key);
- };
-
- switch (mode) {
-@@ -345,13 +218,13 @@ public:
- break;
- case NonCharacterKeys:
- m_filterKey = [](int key, Qt::KeyboardModifiers) {
-- return !characterKeys.contains(key);
-+ return isSpecialKey(key);
- };
- m_filterMouse = eavesdropsMouse;
- break;
- case AllKeysWithModifier:
- m_filterKey = [](int key, Qt::KeyboardModifiers m) {
-- return m.testAnyFlags(modifierKeys) || !characterKeys.contains(key);
-+ return m.testAnyFlags(modifierKeys) || isSpecialKey(key);
- };
- m_filterMouse = eavesdropsMouse;
- break;
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_5cc72fe9_wayland-colormanagement-compare-primaries-with-the-protocol-s-resolution.patch kwin-6.3.6/debian/patches/upstream_5cc72fe9_wayland-colormanagement-compare-primaries-with-the-protocol-s-resolution.patch
--- kwin-6.3.5/debian/patches/upstream_5cc72fe9_wayland-colormanagement-compare-primaries-with-the-protocol-s-resolution.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_5cc72fe9_wayland-colormanagement-compare-primaries-with-the-protocol-s-resolution.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,97 +0,0 @@
-From 5cc72fe966a9bc359300e22a9495d6a4ff4249c8 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@kde.org>
-Date: Wed, 21 May 2025 14:40:04 +0200
-Subject: [PATCH] wayland/colormanagement: compare primaries with the
- protocol's resolution
-
-Otherwise, small floating point differences that can't even be conveyed with the
-protocol prevent us from sending the primaries_named event.
-
-(cherry picked from commit 45f0fe0529f808e93dcb93092635e550f458484a)
----
- src/wayland/colormanagement_v1.cpp | 50 +++++++++++++++++++++---------
- 1 file changed, 35 insertions(+), 15 deletions(-)
-
-diff --git a/src/wayland/colormanagement_v1.cpp b/src/wayland/colormanagement_v1.cpp
-index 4a721bbc6cd..5514248b9f4 100644
---- a/src/wayland/colormanagement_v1.cpp
-+++ b/src/wayland/colormanagement_v1.cpp
-@@ -467,31 +467,51 @@ static uint32_t kwinTFtoProtoTF(TransferFunction tf)
- Q_UNREACHABLE();
- }
-
--static uint32_t kwinPrimariesToProtoPrimaires(NamedColorimetry primaries)
-+static bool compareXY(double left, double right)
- {
-- switch (primaries) {
-- case NamedColorimetry::BT709:
-+ return std::round(left / s_primaryUnit) == std::round(right / s_primaryUnit);
-+}
-+
-+static bool compareXY(xy left, xy right)
-+{
-+ return compareXY(left.x, right.x) && compareXY(left.y, right.y);
-+}
-+
-+static bool comparePrimaries(const Colorimetry &left, const Colorimetry &right)
-+{
-+ // this can't just use Colorimetry::operator==
-+ // as that has a different resolution from the Wayland protocol
-+ return compareXY(left.red().toxy(), right.red().toxy())
-+ && compareXY(left.green().toxy(), right.green().toxy())
-+ && compareXY(left.blue().toxy(), right.blue().toxy())
-+ && compareXY(left.white().toxy(), right.white().toxy());
-+}
-+
-+static std::optional<uint32_t> kwinPrimariesToProtoPrimaires(const Colorimetry &primaries)
-+{
-+ if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::BT709))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_srgb;
-- case NamedColorimetry::PAL_M:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::PAL_M))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal_m;
-- case NamedColorimetry::PAL:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::PAL))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal;
-- case NamedColorimetry::NTSC:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::NTSC))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_ntsc;
-- case NamedColorimetry::GenericFilm:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::GenericFilm))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_generic_film;
-- case NamedColorimetry::BT2020:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::BT2020))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_bt2020;
-- case NamedColorimetry::CIEXYZ:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::CIEXYZ))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_cie1931_xyz;
-- case NamedColorimetry::DCIP3:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::DCIP3))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_dci_p3;
-- case NamedColorimetry::DisplayP3:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::DisplayP3))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_display_p3;
-- case NamedColorimetry::AdobeRGB:
-+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::AdobeRGB))) {
- return QtWaylandServer::wp_color_manager_v1::primaries::primaries_adobe_rgb;
-+ } else {
-+ return std::nullopt;
- }
-- Q_UNREACHABLE();
- }
-
- void ImageDescriptionV1::wp_image_description_v1_get_information(Resource *qtResource, uint32_t information)
-@@ -513,8 +533,8 @@ void ImageDescriptionV1::wp_image_description_v1_get_information(Resource *qtRes
- round(containerGreen.x), round(containerGreen.y),
- round(containerBlue.x), round(containerBlue.y),
- round(containerWhite.x), round(containerWhite.y));
-- if (auto name = m_description->containerColorimetry().name()) {
-- wp_image_description_info_v1_send_primaries_named(resource, kwinPrimariesToProtoPrimaires(*name));
-+ if (auto name = kwinPrimariesToProtoPrimaires(m_description->containerColorimetry())) {
-+ wp_image_description_info_v1_send_primaries_named(resource, *name);
- }
- if (auto m = m_description->masteringColorimetry()) {
- const xyY masterRed = m->red().toxyY();
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_602072c9_backends-drm-remove-unused-method.patch kwin-6.3.6/debian/patches/upstream_602072c9_backends-drm-remove-unused-method.patch
--- kwin-6.3.5/debian/patches/upstream_602072c9_backends-drm-remove-unused-method.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_602072c9_backends-drm-remove-unused-method.patch 2025-07-18 11:21:52.000000000 +0200
@@ -8,11 +8,9 @@
src/backends/drm/drm_commit_thread.h | 8 --------
2 files changed, 25 deletions(-)
-diff --git a/src/backends/drm/drm_commit_thread.cpp b/src/backends/drm/drm_commit_thread.cpp
-index d3dd4f2a60b..94c786acc78 100644
--- a/src/backends/drm/drm_commit_thread.cpp
+++ b/src/backends/drm/drm_commit_thread.cpp
-@@ -383,23 +383,6 @@ std::chrono::nanoseconds DrmCommitThread::safetyMargin() const
+@@ -390,23 +390,6 @@ std::chrono::nanoseconds DrmCommitThread
return m_safetyMargin;
}
@@ -36,8 +34,6 @@
void DrmCommitThread::handlePing()
{
// this will process the pageflip and call pageFlipped if there is one
-diff --git a/src/backends/drm/drm_commit_thread.h b/src/backends/drm/drm_commit_thread.h
-index 4c0dada590e..3973a650837 100644
--- a/src/backends/drm/drm_commit_thread.h
+++ b/src/backends/drm/drm_commit_thread.h
@@ -43,14 +43,6 @@ public:
@@ -55,6 +51,3 @@
private:
void clearDroppedCommits();
TimePoint estimateNextVblank(TimePoint now) const;
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_612ec743_backends-drm-also-generate-modes-for-the-native-refresh-rate-of-the-display.patch kwin-6.3.6/debian/patches/upstream_612ec743_backends-drm-also-generate-modes-for-the-native-refresh-rate-of-the-display.patch
--- kwin-6.3.5/debian/patches/upstream_612ec743_backends-drm-also-generate-modes-for-the-native-refresh-rate-of-the-display.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_612ec743_backends-drm-also-generate-modes-for-the-native-refresh-rate-of-the-display.patch 2025-07-18 11:21:52.000000000 +0200
@@ -14,6 +14,9 @@
Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
---
+From: Aurélien COUDERC <coucouf@debian.org>
+Adapted to apply against Plasma 6.3
+---
autotests/drm/drmTest.cpp | 203 +++++++++++++++++------------
src/backends/drm/drm_connector.cpp | 33 +++--
2 files changed, 142 insertions(+), 94 deletions(-)
diff -Nru kwin-6.3.5/debian/patches/upstream_68dbfe7a_backends-drm-prevent-the-main-thread-from-moving-the-target-pageflip-time-forward.patch kwin-6.3.6/debian/patches/upstream_68dbfe7a_backends-drm-prevent-the-main-thread-from-moving-the-target-pageflip-time-forward.patch
--- kwin-6.3.5/debian/patches/upstream_68dbfe7a_backends-drm-prevent-the-main-thread-from-moving-the-target-pageflip-time-forward.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_68dbfe7a_backends-drm-prevent-the-main-thread-from-moving-the-target-pageflip-time-forward.patch 2025-07-18 11:21:52.000000000 +0200
@@ -11,11 +11,9 @@
src/backends/drm/drm_commit_thread.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
-diff --git a/src/backends/drm/drm_commit_thread.cpp b/src/backends/drm/drm_commit_thread.cpp
-index 69f8000a549..d3dd4f2a60b 100644
--- a/src/backends/drm/drm_commit_thread.cpp
+++ b/src/backends/drm/drm_commit_thread.cpp
-@@ -24,6 +24,7 @@ namespace KWin
+@@ -23,6 +23,7 @@ namespace KWin
DrmCommitThread::DrmCommitThread(DrmGpu *gpu, const QString &name)
: m_gpu(gpu)
@@ -23,7 +21,7 @@
{
if (!gpu->atomicModeSetting()) {
return;
-@@ -314,13 +315,15 @@ void DrmCommitThread::addCommit(std::unique_ptr<DrmAtomicCommit> &&commit)
+@@ -313,13 +314,15 @@ void DrmCommitThread::addCommit(std::uni
std::unique_lock lock(m_mutex);
m_commits.push_back(std::move(commit));
const auto now = std::chrono::steady_clock::now();
@@ -42,6 +40,3 @@
m_commits.back()->setDeadline(m_targetPageflipTime - m_safetyMargin);
m_commitPending.notify_all();
}
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_7b93a776_Implement-MouseKeys-on-Wayland.patch kwin-6.3.6/debian/patches/upstream_7b93a776_Implement-MouseKeys-on-Wayland.patch
--- kwin-6.3.5/debian/patches/upstream_7b93a776_Implement-MouseKeys-on-Wayland.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_7b93a776_Implement-MouseKeys-on-Wayland.patch 2025-07-18 11:21:52.000000000 +0200
@@ -28,11 +28,9 @@
create mode 100644 src/plugins/mousekeys/mousekeys.cpp
create mode 100644 src/plugins/mousekeys/mousekeys.h
-diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt
-index a456d7f9001..59c67b088ae 100644
--- a/autotests/integration/CMakeLists.txt
+++ b/autotests/integration/CMakeLists.txt
-@@ -147,6 +147,7 @@ integrationTest(NAME testMouseActions SRCS mouseactions_test.cpp LIBS)
+@@ -145,6 +145,7 @@ integrationTest(NAME testWorkspace SRCS
integrationTest(NAME testMouseActions SRCS mouseactions_test.cpp LIBS)
integrationTest(NAME testColorManagement SRCS test_colormanagement.cpp)
integrationTest(NAME testKeyboardInput SRCS keyboard_input_test.cpp)
@@ -40,9 +38,6 @@
if(KWIN_BUILD_X11)
integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp LIBS KDecoration3::KDecoration)
-diff --git a/autotests/integration/mouse_keys_test.cpp b/autotests/integration/mouse_keys_test.cpp
-new file mode 100644
-index 00000000000..9543f9e4398
--- /dev/null
+++ b/autotests/integration/mouse_keys_test.cpp
@@ -0,0 +1,222 @@
@@ -268,8 +263,6 @@
+
+WAYLANDTEST_MAIN(KWin::MouseKeysTest)
+#include "mouse_keys_test.moc"
-diff --git a/src/input.h b/src/input.h
-index 03d49a15fa7..ca4f48a7212 100644
--- a/src/input.h
+++ b/src/input.h
@@ -326,6 +326,7 @@ enum Order {
@@ -280,8 +273,6 @@
EisInput,
VirtualTerminal,
-diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
-index cc92d696d2e..6984b95e69f 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -79,6 +79,7 @@ add_subdirectory(magnifier)
@@ -292,9 +283,6 @@
add_subdirectory(mousemark)
add_subdirectory(nightlight)
add_subdirectory(outputlocator)
-diff --git a/src/plugins/mousekeys/CMakeLists.txt b/src/plugins/mousekeys/CMakeLists.txt
-new file mode 100644
-index 00000000000..f711c9bd92d
--- /dev/null
+++ b/src/plugins/mousekeys/CMakeLists.txt
@@ -0,0 +1,18 @@
@@ -316,9 +304,6 @@
+)
+target_link_libraries(MouseKeysPlugin PRIVATE kwin)
+
-diff --git a/src/plugins/mousekeys/main.cpp b/src/plugins/mousekeys/main.cpp
-new file mode 100644
-index 00000000000..ae1e4c266d3
--- /dev/null
+++ b/src/plugins/mousekeys/main.cpp
@@ -0,0 +1,25 @@
@@ -347,9 +332,6 @@
+};
+
+#include "main.moc"
-diff --git a/src/plugins/mousekeys/metadata.json b/src/plugins/mousekeys/metadata.json
-new file mode 100644
-index 00000000000..aa304f40935
--- /dev/null
+++ b/src/plugins/mousekeys/metadata.json
@@ -0,0 +1,5 @@
@@ -358,9 +340,6 @@
+ "EnabledByDefault": true
+ }
+}
-diff --git a/src/plugins/mousekeys/mousekeys.cpp b/src/plugins/mousekeys/mousekeys.cpp
-new file mode 100644
-index 00000000000..517d97cc584
--- /dev/null
+++ b/src/plugins/mousekeys/mousekeys.cpp
@@ -0,0 +1,249 @@
@@ -613,9 +592,6 @@
+
+ return false;
+}
-diff --git a/src/plugins/mousekeys/mousekeys.h b/src/plugins/mousekeys/mousekeys.h
-new file mode 100644
-index 00000000000..02c7e539f6c
--- /dev/null
+++ b/src/plugins/mousekeys/mousekeys.h
@@ -0,0 +1,70 @@
@@ -689,6 +665,3 @@
+ int m_delay = 0;
+ int m_interval = 0;
+};
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_7f088c1b_backends-drm-Add-missing-null-guard.patch kwin-6.3.6/debian/patches/upstream_7f088c1b_backends-drm-Add-missing-null-guard.patch
--- kwin-6.3.5/debian/patches/upstream_7f088c1b_backends-drm-Add-missing-null-guard.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_7f088c1b_backends-drm-Add-missing-null-guard.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-From 7f088c1b69a59c3e0d6696886394658ccec38e27 Mon Sep 17 00:00:00 2001
-From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
-Date: Mon, 26 May 2025 15:59:33 +0000
-Subject: [PATCH] backends/drm: Add missing null guard
-
-The `ret` pointer can be null if a graphics reset occurs.
-
-SENTRY: KWIN-CD0
-
-
-(cherry picked from commit f727f4d3229efc949d2b4d8817a0b868be8ee045)
-
-Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
----
- src/backends/drm/drm_egl_layer.cpp | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/src/backends/drm/drm_egl_layer.cpp b/src/backends/drm/drm_egl_layer.cpp
-index 01d9d6a8062..26dbc57e626 100644
---- a/src/backends/drm/drm_egl_layer.cpp
-+++ b/src/backends/drm/drm_egl_layer.cpp
-@@ -80,7 +80,9 @@ std::shared_ptr<GLTexture> EglGbmLayer::texture() const
- {
- if (m_scanoutBuffer) {
- const auto ret = m_surface.eglBackend()->importDmaBufAsTexture(*m_scanoutBuffer->buffer()->dmabufAttributes());
-- ret->setContentTransform(offloadTransform().combine(OutputTransform::FlipY));
-+ if (ret) {
-+ ret->setContentTransform(offloadTransform().combine(OutputTransform::FlipY));
-+ }
- return ret;
- } else {
- return m_surface.texture();
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_801d31a9_plugins-stickykeys-Unlatch-keys-after-mouse-click.patch kwin-6.3.6/debian/patches/upstream_801d31a9_plugins-stickykeys-Unlatch-keys-after-mouse-click.patch
--- kwin-6.3.5/debian/patches/upstream_801d31a9_plugins-stickykeys-Unlatch-keys-after-mouse-click.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_801d31a9_plugins-stickykeys-Unlatch-keys-after-mouse-click.patch 2025-07-18 11:21:52.000000000 +0200
@@ -10,8 +10,6 @@
src/plugins/stickykeys/stickykeys.h | 1 +
3 files changed, 85 insertions(+)
-diff --git a/autotests/integration/sticky_keys_test.cpp b/autotests/integration/sticky_keys_test.cpp
-index 29fb6110f4f..d0780db627f 100644
--- a/autotests/integration/sticky_keys_test.cpp
+++ b/autotests/integration/sticky_keys_test.cpp
@@ -37,6 +37,8 @@ private Q_SLOTS:
@@ -23,7 +21,7 @@
void testDisableTwoKeys();
};
-@@ -280,6 +282,61 @@ void StickyKeysTest::testDisableTwoKeys()
+@@ -280,6 +282,61 @@ void StickyKeysTest::testDisableTwoKeys(
Test::keyboardKeyReleased(KEY_A, ++timestamp);
QVERIFY(!modifierSpy.wait(10));
}
@@ -85,8 +83,6 @@
}
WAYLANDTEST_MAIN(KWin::StickyKeysTest)
-diff --git a/src/plugins/stickykeys/stickykeys.cpp b/src/plugins/stickykeys/stickykeys.cpp
-index 2b699438fa1..32f586f60d1 100644
--- a/src/plugins/stickykeys/stickykeys.cpp
+++ b/src/plugins/stickykeys/stickykeys.cpp
@@ -9,6 +9,8 @@
@@ -98,7 +94,7 @@
#include <KLazyLocalizedString>
#if KWIN_BUILD_NOTIFICATIONS
#include <KNotification>
-@@ -183,4 +185,29 @@ void StickyKeysFilter::disableStickyKeys()
+@@ -183,4 +185,29 @@ void StickyKeysFilter::disableStickyKeys
KWin::input()->uninstallInputEventFilter(this);
}
@@ -128,8 +124,6 @@
+}
+
#include "moc_stickykeys.cpp"
-diff --git a/src/plugins/stickykeys/stickykeys.h b/src/plugins/stickykeys/stickykeys.h
-index f432618c4e1..f76a7ebfcea 100644
--- a/src/plugins/stickykeys/stickykeys.h
+++ b/src/plugins/stickykeys/stickykeys.h
@@ -18,6 +18,7 @@ public:
@@ -140,6 +134,3 @@
enum KeyState {
None,
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_8450d9a8_wayland-xdgoutput-round-the-scaled-output-position.patch kwin-6.3.6/debian/patches/upstream_8450d9a8_wayland-xdgoutput-round-the-scaled-output-position.patch
--- kwin-6.3.5/debian/patches/upstream_8450d9a8_wayland-xdgoutput-round-the-scaled-output-position.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_8450d9a8_wayland-xdgoutput-round-the-scaled-output-position.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,32 +0,0 @@
-From 8450d9a8ed43b03eb69bc565297fe846a00c81a5 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Thu, 22 May 2025 17:12:26 +0200
-Subject: [PATCH] wayland/xdgoutput: round the scaled output position
-
-Otherwise it just gets clipped, making the result differ from what
-Xwayland::updatePrimary expects (and potentially overlap with another output)
-
-
-(cherry picked from commit 611be1e7f4a19cd3694e3f0d55d9879fccd83caf)
-
-Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
----
- src/wayland/xdgoutput_v1.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/wayland/xdgoutput_v1.cpp b/src/wayland/xdgoutput_v1.cpp
-index 850af72623a..58da0093bc0 100644
---- a/src/wayland/xdgoutput_v1.cpp
-+++ b/src/wayland/xdgoutput_v1.cpp
-@@ -185,7 +185,7 @@ void XdgOutputV1Interface::sendLogicalPosition(Resource *resource)
- ClientConnection *connection = output->display()->getConnection(resource->client());
- qreal scaleOverride = connection->scaleOverride();
-
-- send_logical_position(resource->handle, pos.x() * scaleOverride, pos.y() * scaleOverride);
-+ send_logical_position(resource->handle, std::round(pos.x() * scaleOverride), std::round(pos.y() * scaleOverride));
- }
-
- void XdgOutputV1Interface::sendDone(Resource *resource)
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_85eeafa0_backends-drm-dynamically-adjust-the-safety-margin-based-on-commit-time.patch kwin-6.3.6/debian/patches/upstream_85eeafa0_backends-drm-dynamically-adjust-the-safety-margin-based-on-commit-time.patch
--- kwin-6.3.5/debian/patches/upstream_85eeafa0_backends-drm-dynamically-adjust-the-safety-margin-based-on-commit-time.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_85eeafa0_backends-drm-dynamically-adjust-the-safety-margin-based-on-commit-time.patch 2025-07-18 11:21:52.000000000 +0200
@@ -26,11 +26,9 @@
src/backends/drm/drm_pipeline.cpp | 2 ++
3 files changed, 29 insertions(+), 2 deletions(-)
-diff --git a/src/backends/drm/drm_commit_thread.cpp b/src/backends/drm/drm_commit_thread.cpp
-index 94c786acc78..3761ae241e0 100644
--- a/src/backends/drm/drm_commit_thread.cpp
+++ b/src/backends/drm/drm_commit_thread.cpp
-@@ -144,11 +144,31 @@ void DrmCommitThread::submit()
+@@ -143,11 +143,31 @@ void DrmCommitThread::submit()
const auto vrr = commit->isVrr();
const bool success = commit->commit();
if (success) {
@@ -63,7 +61,7 @@
} else {
if (m_commits.size() > 1) {
// the failure may have been because of the reordering of commits
-@@ -339,6 +359,8 @@ void DrmCommitThread::clearDroppedCommits()
+@@ -338,6 +358,8 @@ void DrmCommitThread::clearDroppedCommit
m_commitsToDelete.clear();
}
@@ -72,7 +70,7 @@
static const std::chrono::microseconds s_safetyMarginMinimum = []() {
bool ok = false;
int value = qEnvironmentVariableIntValue("KWIN_DRM_OVERRIDE_SAFETY_MARGIN", &ok);
-@@ -347,7 +369,8 @@ void DrmCommitThread::setModeInfo(uint32_t maximum, std::chrono::nanoseconds vbl
+@@ -354,7 +376,8 @@ void DrmCommitThread::setModeInfo(uint32
m_minVblankInterval = std::chrono::nanoseconds(1'000'000'000'000ull / maximum);
// the kernel rejects commits that happen during vblank
// the 1.5ms on top of that was chosen experimentally, for the time it takes to commit + scheduling inaccuracies
@@ -82,8 +80,6 @@
}
void DrmCommitThread::pageFlipped(std::chrono::nanoseconds timestamp)
-diff --git a/src/backends/drm/drm_commit_thread.h b/src/backends/drm/drm_commit_thread.h
-index 3973a650837..aeb6ad007cc 100644
--- a/src/backends/drm/drm_commit_thread.h
+++ b/src/backends/drm/drm_commit_thread.h
@@ -65,6 +65,8 @@ private:
@@ -95,11 +91,9 @@
bool m_ping = false;
bool m_pageflipTimeoutDetected = false;
};
-diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp
-index 71b52661854..215899629f6 100644
--- a/src/backends/drm/drm_pipeline.cpp
+++ b/src/backends/drm/drm_pipeline.cpp
-@@ -458,6 +458,8 @@ void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp)
+@@ -444,6 +444,8 @@ void DrmPipeline::pageFlipped(std::chron
{
RenderLoopPrivate::get(m_output->renderLoop())->notifyVblank(timestamp);
m_commitThread->pageFlipped(timestamp);
@@ -108,6 +102,3 @@
if (gpu()->needsModeset()) {
gpu()->maybeModeset(nullptr, nullptr);
}
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_889ffed2_A11yKeyboardMonitor-Distinguish-modifier-and-other-key.patch kwin-6.3.6/debian/patches/upstream_889ffed2_A11yKeyboardMonitor-Distinguish-modifier-and-other-key.patch
--- kwin-6.3.5/debian/patches/upstream_889ffed2_A11yKeyboardMonitor-Distinguish-modifier-and-other-key.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_889ffed2_A11yKeyboardMonitor-Distinguish-modifier-and-other-key.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,45 @@
+From 889ffed2bbb40c44dbb8ef4268e5e0bbcb7671da Mon Sep 17 00:00:00 2001
+From: Michael Weghorn <m.weghorn@posteo.de>
+Date: Mon, 7 Jul 2025 13:09:38 +0200
+Subject: [PATCH] A11yKeyboardMonitor: Distinguish modifier and other key
+
+When processing a key event, only update
+A11yKeyboardMonitor::lastModifier and
+A11yKeyboardMonitor::lastModifierTime if
+the pressed key is actually a modifier.
+
+Otherwise, the "if the modifier was pressed twice within the
+key repeat delay process it normally" logic above would
+trigger when pressing a non-modifier key twice within
+the key repeat delay time, breaking e.g. Orca's structural
+navigation when pressing "H" twice to jump to the heading
+after the next, and instead result in a literal "h" getting
+inserted when input is accepted (e.g. in an editable
+LibreOffice Writer document).
+
+BUG: 506715
+---
+ src/a11ykeyboardmonitor.cpp | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/a11ykeyboardmonitor.cpp b/src/a11ykeyboardmonitor.cpp
+index 3b7e5fa357a..5dad8add460 100644
+--- a/src/a11ykeyboardmonitor.cpp
++++ b/src/a11ykeyboardmonitor.cpp
+@@ -74,10 +74,10 @@ bool A11yKeyboardMonitor::processKey(uint32_t key, KeyboardKeyState state, std::
+ return false;
+ }
+
+- data.lastModifier = keysym;
+- data.lastModifierTime = time;
+-
+ if (data.modifiers.contains(keysym)) {
++ data.lastModifier = keysym;
++ data.lastModifierTime = time;
++
+ if (released) {
+ data.pressedModifiers.remove(keysym);
+ } else {
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_8f331a26_backends-drm-don-t-use-UUID-to-identify-outputs.patch kwin-6.3.6/debian/patches/upstream_8f331a26_backends-drm-don-t-use-UUID-to-identify-outputs.patch
--- kwin-6.3.5/debian/patches/upstream_8f331a26_backends-drm-don-t-use-UUID-to-identify-outputs.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_8f331a26_backends-drm-don-t-use-UUID-to-identify-outputs.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,90 @@
+From 8f331a268cf1e457b8727080763869e1b426dae8 Mon Sep 17 00:00:00 2001
+From: Xaver Hugl <xaver.hugl@gmail.com>
+Date: Fri, 4 Jul 2025 15:57:25 +0200
+Subject: [PATCH] backends/drm: don't use UUID to identify outputs
+
+The UUID is only set later by the output configuration system, so it can't be used in
+the drm backend when deciding the dpms state of a "new" output.
+This changes it to use EDID ID, EDID hash or connector name instead, which are valid
+much earlier and are reliable enough for the dpms workaround.
+
+BUG: 493879
+BUG: 506135
+BUG: 505953
+
+
+(cherry picked from commit e177e1d2da8a278f85d5d3e24ffdc01e7c2eaca6)
+
+Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
+---
+From: Aurélien COUDERC <coucouf@debian.org>
+Adapted to apply against Plasma 6.3
+---
+ src/backends/drm/drm_backend.cpp | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/src/backends/drm/drm_backend.cpp b/src/backends/drm/drm_backend.cpp
+index 0b95e790396..17da998da49 100644
+--- a/src/backends/drm/drm_backend.cpp
++++ b/src/backends/drm/drm_backend.cpp
+@@ -234,12 +234,28 @@ DrmGpu *DrmBackend::addGpu(const QString &fileName)
+ return gpu;
+ }
+
++static QString earlyIdentifier(Output *output)
++{
++ // We can't use the output's UUID because that's only set later, by the output config system.
++ // This doesn't need to be perfectly accurate though, sometimes getting a false positive is ok,
++ // so this just uses EDID ID, EDID hash or connector name, whichever is available
++ if (output->edid().isValid()) {
++ if (!output->edid().identifier().isEmpty()) {
++ return output->edid().identifier();
++ } else {
++ return output->edid().hash();
++ }
++ } else {
++ return output->name();
++ }
++}
++
+ void DrmBackend::addOutput(DrmAbstractOutput *o)
+ {
+ const bool allOff = std::ranges::all_of(m_outputs, [](Output *output) {
+ return output->dpmsMode() != Output::DpmsMode::On;
+ });
+- if (allOff && m_recentlyUnpluggedDpmsOffOutputs.contains(o->uuid())) {
++ if (allOff && m_recentlyUnpluggedDpmsOffOutputs.contains(earlyIdentifier(o))) {
+ if (DrmOutput *drmOutput = qobject_cast<DrmOutput *>(o)) {
+ // When the system is in dpms power saving mode, KWin turns on all outputs if the user plugs a new output in
+ // as that's an intentional action and they expect to see the output light up.
+@@ -248,7 +264,7 @@ void DrmBackend::addOutput(DrmAbstractOutput *o)
+ drmOutput->updateDpmsMode(Output::DpmsMode::Off);
+ drmOutput->pipeline()->setActive(false);
+ drmOutput->renderLoop()->inhibit();
+- m_recentlyUnpluggedDpmsOffOutputs.removeOne(drmOutput->uuid());
++ m_recentlyUnpluggedDpmsOffOutputs.removeOne(earlyIdentifier(drmOutput));
+ }
+ }
+ m_outputs.append(o);
+@@ -268,7 +284,7 @@ static const int s_dpmsTimeout = environmentVariableIntValue("KWIN_DPMS_WORKAROU
+ void DrmBackend::removeOutput(DrmAbstractOutput *o)
+ {
+ if (o->dpmsMode() == Output::DpmsMode::Off) {
+- const QUuid id = o->uuid();
++ const QString id = earlyIdentifier(o);
+ m_recentlyUnpluggedDpmsOffOutputs.push_back(id);
+ QTimer::singleShot(s_dpmsTimeout, this, [this, id]() {
+ m_recentlyUnpluggedDpmsOffOutputs.removeOne(id);
+--- a/src/backends/drm/drm_backend.h
++++ b/src/backends/drm/drm_backend.h
+@@ -87,7 +87,7 @@
+ std::unique_ptr<QSocketNotifier> m_socketNotifier;
+ Session *m_session;
+ QList<DrmAbstractOutput *> m_outputs;
+- QList<QUuid> m_recentlyUnpluggedDpmsOffOutputs;
++ QList<QString> m_recentlyUnpluggedDpmsOffOutputs;
+
+ const QStringList m_explicitGpus;
+ std::vector<std::unique_ptr<DrmGpu>> m_gpus;
+--
+GitLab
diff -Nru kwin-6.3.5/debian/patches/upstream_9580193a_backends-drm-also-handle-GPU-resets-on-secondary-GPUs.patch kwin-6.3.6/debian/patches/upstream_9580193a_backends-drm-also-handle-GPU-resets-on-secondary-GPUs.patch
--- kwin-6.3.5/debian/patches/upstream_9580193a_backends-drm-also-handle-GPU-resets-on-secondary-GPUs.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_9580193a_backends-drm-also-handle-GPU-resets-on-secondary-GPUs.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,107 @@
+From 9580193a086ad4eb38d0a2ebf167abe5a36780f6 Mon Sep 17 00:00:00 2001
+From: Xaver Hugl <xaver.hugl@gmail.com>
+Date: Wed, 11 Jun 2025 14:12:10 +0200
+Subject: [PATCH] backends/drm: also handle GPU resets on secondary GPUs
+
+Just re-creates the swapchains and context.
+
+
+(cherry picked from commit 7268ced6517dccc2d11420b62668af31f26a58e8)
+
+Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
+---
+From: Aurélien COUDERC <coucouf@debian.org>
+Adapt patch to cope with changes caused by a88748a4d8246a089345769cba881dbb85d3efff
+---
+ src/backends/drm/drm_egl_backend.cpp | 5 +++++
+ src/backends/drm/drm_egl_backend.h | 1 +
+ src/backends/drm/drm_egl_layer_surface.cpp | 17 ++++++++++++++---
+ src/backends/drm/drm_egl_layer_surface.h | 2 ++
+ 4 files changed, 22 insertions(+), 3 deletions(-)
+
+diff --git a/src/backends/drm/drm_egl_backend.cpp b/src/backends/drm/drm_egl_backend.cpp
+index 033555e9768..c96f34e229d 100644
+--- a/src/backends/drm/drm_egl_backend.cpp
++++ b/src/backends/drm/drm_egl_backend.cpp
+@@ -132,6 +132,11 @@ std::shared_ptr<EglContext> EglGbmBackend::contextForGpu(DrmGpu *gpu)
+ return ret;
+ }
+
++void EglGbmBackend::resetContextForGpu(DrmGpu *gpu)
++{
++ m_contexts.erase(gpu->eglDisplay());
++}
++
+ std::unique_ptr<SurfaceTexture> EglGbmBackend::createSurfaceTextureWayland(SurfacePixmap *pixmap)
+ {
+ return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
+diff --git a/src/backends/drm/drm_egl_backend.h b/src/backends/drm/drm_egl_backend.h
+index 49bfe715d98..3a6f94dfd9f 100644
+--- a/src/backends/drm/drm_egl_backend.h
++++ b/src/backends/drm/drm_egl_backend.h
+@@ -61,6 +61,7 @@ public:
+
+ EglDisplay *displayForGpu(DrmGpu *gpu);
+ std::shared_ptr<EglContext> contextForGpu(DrmGpu *gpu);
++ void resetContextForGpu(DrmGpu *gpu);
+
+ private:
+ bool initializeEgl();
+diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp
+index bb9a98e5df0..71f92398b7a 100644
+--- a/src/backends/drm/drm_egl_layer_surface.cpp
++++ b/src/backends/drm/drm_egl_layer_surface.cpp
+@@ -408,7 +408,7 @@ bool EglGbmLayerSurface::checkSurface(const QSize &size, const QHash<uint32_t, Q
+
+ bool EglGbmLayerSurface::doesSurfaceFit(Surface *surface, const QSize &size, const QHash<uint32_t, QList<uint64_t>> &formats, Output::ColorPowerTradeoff tradeoff) const
+ {
+- if (!surface || !surface->gbmSwapchain || surface->gbmSwapchain->size() != size) {
++ if (!surface || surface->needsRecreation || !surface->gbmSwapchain || surface->gbmSwapchain->size() != size) {
+ return false;
+ }
+ if (surface->tradeoff != tradeoff) {
+@@ -635,6 +635,19 @@ std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithEgl(Surface *surfa
+ }
+
+ if (!surface->importContext->makeCurrent()) {
++ qCWarning(KWIN_DRM, "Failed to make import context current");
++ // this is probably caused by a GPU reset, let's not take any chances
++ surface->needsRecreation = true;
++ m_eglBackend->resetContextForGpu(m_gpu);
++ return nullptr;
++ }
++ const auto restoreContext = qScopeGuard([this]() {
++ m_eglBackend->makeCurrent();
++ });
++ if (surface->importContext->checkGraphicsResetStatus() != GL_NO_ERROR) {
++ qCWarning(KWIN_DRM, "Detected GPU reset on secondary GPU %s", qPrintable(m_gpu->drmDevice()->path()));
++ surface->needsRecreation = true;
++ m_eglBackend->resetContextForGpu(m_gpu);
+ return nullptr;
+ }
+ std::unique_ptr<GLRenderTimeQuery> renderTime;
+@@ -703,8 +716,6 @@ std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithEgl(Surface *surfa
+ frame->addRenderTimeQuery(std::move(renderTime));
+ }
+
+- // restore the old context
+- m_eglBackend->makeCurrent();
+ return m_gpu->importBuffer(slot->buffer(), endFence.takeFileDescriptor());
+ }
+
+diff --git a/src/backends/drm/drm_egl_layer_surface.h b/src/backends/drm/drm_egl_layer_surface.h
+index 2d5d8806796..72643e6eec0 100644
+--- a/src/backends/drm/drm_egl_layer_surface.h
++++ b/src/backends/drm/drm_egl_layer_surface.h
+@@ -80,6 +80,8 @@ private:
+ {
+ ~Surface();
+
++ bool needsRecreation = false;
++
+ std::shared_ptr<EglContext> context;
+ std::shared_ptr<EglSwapchain> gbmSwapchain;
+ std::shared_ptr<EglSwapchainSlot> currentSlot;
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_98b64e9b_backends-drm-allow-night-light-to-get-closer-to-the-edges-of-the-gamut.patch kwin-6.3.6/debian/patches/upstream_98b64e9b_backends-drm-allow-night-light-to-get-closer-to-the-edges-of-the-gamut.patch
--- kwin-6.3.5/debian/patches/upstream_98b64e9b_backends-drm-allow-night-light-to-get-closer-to-the-edges-of-the-gamut.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_98b64e9b_backends-drm-allow-night-light-to-get-closer-to-the-edges-of-the-gamut.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,39 @@
+From 98b64e9bff63fb7742a260f3f83b78cba3605c92 Mon Sep 17 00:00:00 2001
+From: Xaver Hugl <xaver.hugl@gmail.com>
+Date: Thu, 26 Jun 2025 16:53:42 +0200
+Subject: [PATCH] backends/drm: allow night light to get closer to the edges of
+ the gamut
+
+The limit changes the result vs. 6.2 / kwin_x11. 0.0001 is enough to prevent the
+math from going crazy, and changes the result a lot less.
+
+BUG: 505495
+
+
+(cherry picked from commit 29dc8ee4cacb6372c0d93c784a18059158b6f1b9)
+
+Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
+---
+ src/backends/drm/drm_output.cpp | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp
+index a76fd12aaf6..a34d29c8258 100644
+--- a/src/backends/drm/drm_output.cpp
++++ b/src/backends/drm/drm_output.cpp
+@@ -441,9 +441,9 @@ static QVector3D adaptChannelFactors(const ColorDescription &originalColor, cons
+ QVector3D adaptedChannelFactors = ColorDescription::sRGB.containerColorimetry().relativeColorimetricTo(originalColor.containerColorimetry()) * sRGBchannelFactors;
+ // ensure none of the values reach zero, otherwise the white point might end up on or outside
+ // the edges of the gamut, which leads to terrible glitches
+- adaptedChannelFactors.setX(std::max(adaptedChannelFactors.x(), 0.01f));
+- adaptedChannelFactors.setY(std::max(adaptedChannelFactors.y(), 0.01f));
+- adaptedChannelFactors.setZ(std::max(adaptedChannelFactors.z(), 0.01f));
++ adaptedChannelFactors.setX(std::max(adaptedChannelFactors.x(), 0.0001f));
++ adaptedChannelFactors.setY(std::max(adaptedChannelFactors.y(), 0.0001f));
++ adaptedChannelFactors.setZ(std::max(adaptedChannelFactors.z(), 0.0001f));
+ return adaptedChannelFactors;
+ }
+
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_be55cfa2_wayland-colormanagement-fix-sending-target-luminance-levels.patch kwin-6.3.6/debian/patches/upstream_be55cfa2_wayland-colormanagement-fix-sending-target-luminance-levels.patch
--- kwin-6.3.5/debian/patches/upstream_be55cfa2_wayland-colormanagement-fix-sending-target-luminance-levels.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_be55cfa2_wayland-colormanagement-fix-sending-target-luminance-levels.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,26 +0,0 @@
-From be55cfa27efb7d477ffd96daba438f61b57eb82a Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Tue, 20 May 2025 01:06:09 +0200
-Subject: [PATCH] wayland/colormanagement: fix sending target luminance levels
-
-(cherry picked from commit c6ea19d4594de393cf0dbff6dc568c5051fb463b)
----
- src/wayland/colormanagement_v1.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/wayland/colormanagement_v1.cpp b/src/wayland/colormanagement_v1.cpp
-index 94859f5b61c..979517f70f6 100644
---- a/src/wayland/colormanagement_v1.cpp
-+++ b/src/wayland/colormanagement_v1.cpp
-@@ -534,7 +534,7 @@ void ImageDescriptionV1::wp_image_description_v1_get_information(Resource *qtRes
- wp_image_description_info_v1_send_target_max_cll(resource, *maxcll);
- }
- wp_image_description_info_v1_send_luminances(resource, std::round(m_description->transferFunction().minLuminance / s_minLuminanceUnit), std::round(m_description->transferFunction().maxLuminance), std::round(m_description->referenceLuminance()));
-- wp_image_description_info_v1_send_target_luminance(resource, m_description->minLuminance(), m_description->maxHdrLuminance().value_or(800));
-+ wp_image_description_info_v1_send_target_luminance(resource, std::round(m_description->minLuminance() / s_minLuminanceUnit), std::round(m_description->maxHdrLuminance().value_or(800)));
- wp_image_description_info_v1_send_tf_named(resource, kwinTFtoProtoTF(m_description->transferFunction()));
- wp_image_description_info_v1_send_done(resource);
- wl_resource_destroy(resource);
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_cc5d1256_outputconfigurationstore-disable-adaptive-sync-by-default.patch kwin-6.3.6/debian/patches/upstream_cc5d1256_outputconfigurationstore-disable-adaptive-sync-by-default.patch
--- kwin-6.3.5/debian/patches/upstream_cc5d1256_outputconfigurationstore-disable-adaptive-sync-by-default.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_cc5d1256_outputconfigurationstore-disable-adaptive-sync-by-default.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,40 +0,0 @@
-From cc5d125619bebfa93eb956f2020a014103caab3a Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Fri, 23 May 2025 19:35:31 +0200
-Subject: [PATCH] outputconfigurationstore: disable adaptive sync by default
-
-I've seen too many driver bugs with VRR since we added it, ranging from displays blanking for
-a moment when enabling or disabling VRR to even turning off completely while it's actually used.
-There is also a secondary issue that many displays have some amount of brightness flicker while
-VRR is used, which we can't properly limit without new KMS APIs.
-
-Some of these issues are rather bad, and while they're not our fault, it still gives people a
-bad user experience and bad impression of Plasma, so this should be turned off by default at
-least until the most severe issues are resolved.
-
-This change does not affect existing configurations.
-
-
-(cherry picked from commit 701f1cec07753b3b27d11b9a71e95fab929d3a8a)
-
-Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
----
- src/outputconfigurationstore.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/outputconfigurationstore.cpp b/src/outputconfigurationstore.cpp
-index 255fc9dfaa1..dbe6efecf4d 100644
---- a/src/outputconfigurationstore.cpp
-+++ b/src/outputconfigurationstore.cpp
-@@ -509,7 +509,7 @@ std::pair<OutputConfiguration, QList<Output *>> OutputConfigurationStore::genera
- .manualTransform = existingData.manualTransform.value_or(kscreenChangeSet.transform.value_or(output->panelOrientation())),
- .overscan = existingData.overscan.value_or(kscreenChangeSet.overscan.value_or(0)),
- .rgbRange = existingData.rgbRange.value_or(kscreenChangeSet.rgbRange.value_or(Output::RgbRange::Automatic)),
-- .vrrPolicy = existingData.vrrPolicy.value_or(kscreenChangeSet.vrrPolicy.value_or(VrrPolicy::Automatic)),
-+ .vrrPolicy = existingData.vrrPolicy.value_or(kscreenChangeSet.vrrPolicy.value_or(VrrPolicy::Never)),
- .highDynamicRange = existingData.highDynamicRange.value_or(false),
- .referenceLuminance = existingData.referenceLuminance.value_or(std::clamp(output->maxAverageBrightness().value_or(200), 200.0, 500.0)),
- .wideColorGamut = existingData.wideColorGamut.value_or(false),
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_cfc05b0e_a11ykeyboardmanager-Grab-keys-when-grabbed-modifier-is-pressed.patch kwin-6.3.6/debian/patches/upstream_cfc05b0e_a11ykeyboardmanager-Grab-keys-when-grabbed-modifier-is-pressed.patch
--- kwin-6.3.5/debian/patches/upstream_cfc05b0e_a11ykeyboardmanager-Grab-keys-when-grabbed-modifier-is-pressed.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_cfc05b0e_a11ykeyboardmanager-Grab-keys-when-grabbed-modifier-is-pressed.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,73 @@
+From cfc05b0e788b225dbb0c3291c4a16d0beb9bb573 Mon Sep 17 00:00:00 2001
+From: Nicolas Fella <nicolas.fella@gmx.de>
+Date: Fri, 27 Jun 2025 23:39:50 +0200
+Subject: [PATCH] a11ykeyboardmanager: Grab keys when grabbed modifier is
+ pressed
+
+The description for 'modifiers' in SetKeyGrabs says "All keys in this list
+will be grabbed, and keys pressed while any of these keys are down will also be grabbed."
+
+That last part was missing, causing keys to be processed normally and sent to the application
+when they shouldn't.
+
+BUG: 506078
+---
+ src/a11ykeyboardmonitor.cpp | 15 +++++++++++++++
+ src/a11ykeyboardmonitor.h | 1 +
+ 2 files changed, 16 insertions(+)
+
+diff --git a/src/a11ykeyboardmonitor.cpp b/src/a11ykeyboardmonitor.cpp
+index feaa85cb17e..1bb839d798b 100644
+--- a/src/a11ykeyboardmonitor.cpp
++++ b/src/a11ykeyboardmonitor.cpp
+@@ -52,6 +52,14 @@ bool A11yKeyboardMonitor::processKey(uint32_t key, KeyboardKeyState state, std::
+ emitKeyEvent(name, released, mods, keysym, unicode, key);
+ }
+
++ // if any of the grabbed modifiers is currently down, grab the key
++ for (const auto grabbedMod : data.modifiers) {
++ if (grabbedMod != keysym && data.pressedModifiers.contains(grabbedMod)) {
++ emitKeyEvent(name, released, mods, keysym, unicode, key);
++ return true;
++ }
++ }
++
+ // if the modifier was pressed twice within the key repeat delay process it normally
+ const qint32 keyRepeatDelay = waylandServer()->seat()->keyboard()->keyRepeatDelay();
+ if (state == KeyboardKeyState::Pressed && data.lastModifier == keysym && time < data.lastModifierTime + std::chrono::milliseconds(keyRepeatDelay)) {
+@@ -69,6 +77,12 @@ bool A11yKeyboardMonitor::processKey(uint32_t key, KeyboardKeyState state, std::
+ data.lastModifierTime = time;
+
+ if (data.modifiers.contains(keysym)) {
++ if (released) {
++ data.pressedModifiers.remove(keysym);
++ } else {
++ data.pressedModifiers.insert(keysym);
++ }
++
+ emitKeyEvent(name, released, mods, keysym, unicode, key);
+ return true;
+ }
+@@ -146,6 +160,7 @@ void A11yKeyboardMonitor::SetKeyGrabs(const QList<quint32> &modifiers, const QLi
+
+ m_clients[message().service()].modifiers = modifiers;
+ m_clients[message().service()].keys = keystrokes;
++ m_clients[message().service()].pressedModifiers.clear();
+ }
+
+ void A11yKeyboardMonitor::emitKeyEvent(const QString &name, bool released, quint32 state, quint32 keysym, quint32 unichar, quint16 keycode)
+diff --git a/src/a11ykeyboardmonitor.h b/src/a11ykeyboardmonitor.h
+index 3fbee438c6e..288308624b1 100644
+--- a/src/a11ykeyboardmonitor.h
++++ b/src/a11ykeyboardmonitor.h
+@@ -58,6 +58,7 @@ private:
+ quint32 lastModifier;
+ std::chrono::microseconds lastModifierTime;
+ bool modifierWasForwarded = false;
++ QSet<quint32> pressedModifiers;
+ };
+
+ QHash<QString, GrabData> m_clients;
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_cfd044a3_plugins-colorpicker-round-the-result.patch kwin-6.3.6/debian/patches/upstream_cfd044a3_plugins-colorpicker-round-the-result.patch
--- kwin-6.3.5/debian/patches/upstream_cfd044a3_plugins-colorpicker-round-the-result.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_cfd044a3_plugins-colorpicker-round-the-result.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,29 +0,0 @@
-From cfd044a3f2c55c83df86bae5b065352199dc3069 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@kde.org>
-Date: Thu, 22 May 2025 01:02:32 +0200
-Subject: [PATCH] plugins/colorpicker: round the result
-
-Otherwise it gets clipped, which means the tiniest of rounding errors can
-change the 8 bit result.
-
-BUG: 491633
----
- src/plugins/colorpicker/colorpicker.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/plugins/colorpicker/colorpicker.cpp b/src/plugins/colorpicker/colorpicker.cpp
-index 77ebd1d29bb..8ba8562727d 100644
---- a/src/plugins/colorpicker/colorpicker.cpp
-+++ b/src/plugins/colorpicker/colorpicker.cpp
-@@ -70,7 +70,7 @@ void ColorPickerEffect::paintScreen(const RenderTarget &renderTarget, const Rend
-
- context->glReadnPixels(texturePosition.x(), renderTarget.size().height() - texturePosition.y() - PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE, GL_RGBA, GL_FLOAT, sizeof(float) * data.size(), data.data());
- QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB, RenderingIntent::RelativeColorimetricWithBPC);
-- QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(sRGB.x(), sRGB.y(), sRGB.z())));
-+ QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(std::round(sRGB.x()), std::round(sRGB.y()), std::round(sRGB.z()))));
- setPicking(false);
- m_scheduledPosition = QPoint(-1, -1);
- }
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_e4e144db_backends-drm-Fix-memory-leak-in-DrmGpu-createNonMasterFd.patch kwin-6.3.6/debian/patches/upstream_e4e144db_backends-drm-Fix-memory-leak-in-DrmGpu-createNonMasterFd.patch
--- kwin-6.3.5/debian/patches/upstream_e4e144db_backends-drm-Fix-memory-leak-in-DrmGpu-createNonMasterFd.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_e4e144db_backends-drm-Fix-memory-leak-in-DrmGpu-createNonMasterFd.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,31 @@
+From e4e144db2115a29e354425e56489e2b1abb07b80 Mon Sep 17 00:00:00 2001
+From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+Date: Mon, 7 Jul 2025 12:08:05 +0000
+Subject: [PATCH] backends/drm: Fix memory leak in DrmGpu::createNonMasterFd
+
+drmGetDeviceNameFromFd2 returns an allocated string, which means we
+should free it.
+
+
+(cherry picked from commit e6e3431da596e7580c367ea1ed13b1b614638026)
+
+Co-authored-by: Myrrh Periwinkle <myrrhperiwinkle@qtmlabs.xyz>
+---
+ src/backends/drm/drm_gpu.cpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp
+index 35cccaa1ad3..2dd35108d7a 100644
+--- a/src/backends/drm/drm_gpu.cpp
++++ b/src/backends/drm/drm_gpu.cpp
+@@ -122,6 +122,7 @@ FileDescriptor DrmGpu::createNonMasterFd() const
+ {
+ char *path = drmGetDeviceNameFromFd2(m_fd);
+ FileDescriptor fd{open(path, O_RDWR | O_CLOEXEC)};
++ free(path);
+ if (!fd.isValid()) {
+ qCWarning(KWIN_DRM) << "Could not open DRM fd for leasing!" << strerror(errno);
+ } else {
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_e8541cf2_wayland-Fix-resizing-with-fractional-increments.patch kwin-6.3.6/debian/patches/upstream_e8541cf2_wayland-Fix-resizing-with-fractional-increments.patch
--- kwin-6.3.5/debian/patches/upstream_e8541cf2_wayland-Fix-resizing-with-fractional-increments.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_e8541cf2_wayland-Fix-resizing-with-fractional-increments.patch 2025-07-18 11:21:52.000000000 +0200
@@ -0,0 +1,52 @@
+From e8541cf2233d22fa255da85eb12ffcbb7ca227e0 Mon Sep 17 00:00:00 2001
+From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+Date: Wed, 9 Jul 2025 12:59:15 +0000
+Subject: [PATCH] wayland: Fix resizing with fractional increments
+
+Rounded client sizes are checked, which is okay if we want to determine
+whether the window geometries match. But it doesn't mean that the
+requested frame size can be used.
+
+We need to run nextClientSizeToFrameSize() on top to make sure that the
+frame tightly wraps the surface.
+
+For example, consider that the rect.width() is 10.5 and the target scale
+factor is 2. If there are no borders, the requested client size will be
+11. If the current client size happens to be 11 as well, then the final
+frame geometry will have 10.5 width, which is totally incorrect, it has
+to be 11. If kwin uses 10.5 width, then the outline around the window
+may not have uniform thickness.
+
+During interactive resize, the snapped geometry also needs to be
+gravitated so when you drag the left edge of the window, the right edge
+doesn't move left and right chaotically.
+
+(cherry picked from commit 5b7abb00a3cbbf4084683c9a2070970d0897a39a)
+
+Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
+---
+ src/xdgshellwindow.cpp | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/src/xdgshellwindow.cpp b/src/xdgshellwindow.cpp
+index 42c3d8b595e..9d529d5da74 100644
+--- a/src/xdgshellwindow.cpp
++++ b/src/xdgshellwindow.cpp
+@@ -271,8 +271,12 @@ void XdgSurfaceWindow::moveResizeInternal(const QRectF &rect, MoveResizeMode mod
+ // and current client sizes have to be rounded to integers
+ const QSizeF requestedFrameSize = snapToPixels(rect.size(), nextTargetScale());
+ const QSizeF requestedClientSize = nextFrameSizeToClientSize(requestedFrameSize);
+- if (requestedClientSize.toSize() == clientSize().toSize()) {
+- updateGeometry(QRectF(rect.topLeft(), requestedFrameSize));
++ const QSize roundedRequestedClientSize = requestedClientSize.toSize();
++
++ const QSize roundedClientSize = clientSize().toSize();
++ if (roundedRequestedClientSize == roundedClientSize) {
++ const QRectF snappedRect = QRectF(rect.topLeft(), nextClientSizeToFrameSize(snapToPixels(roundedClientSize, nextTargetScale())));
++ updateGeometry(gravitateGeometry(snappedRect, rect, m_nextGravity));
+ } else {
+ m_configureFlags |= XdgSurfaceConfigure::ConfigurePosition;
+ scheduleConfigure();
+--
+GitLab
+
diff -Nru kwin-6.3.5/debian/patches/upstream_f1f25d2d_backends-drm-also-guard-DrmOutput-cursorLayer-for-nullptr-pipeline.patch kwin-6.3.6/debian/patches/upstream_f1f25d2d_backends-drm-also-guard-DrmOutput-cursorLayer-for-nullptr-pipeline.patch
--- kwin-6.3.5/debian/patches/upstream_f1f25d2d_backends-drm-also-guard-DrmOutput-cursorLayer-for-nullptr-pipeline.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_f1f25d2d_backends-drm-also-guard-DrmOutput-cursorLayer-for-nullptr-pipeline.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-From f1f25d2da9e6a7c0b28a333684449cc7733dc388 Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Mon, 19 May 2025 14:37:58 +0000
-Subject: [PATCH] backends/drm: also guard DrmOutput::cursorLayer for nullptr
- pipeline
-
-BUG: 504516
-
-
-(cherry picked from commit ceb2415e5bc314bb325ddc1664e684844f1a7ff3)
-
-Co-authored-by: Xaver Hugl <xaver.hugl@gmail.com>
----
- src/backends/drm/drm_output.cpp | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp
-index a467d737901..151cb2d466f 100644
---- a/src/backends/drm/drm_output.cpp
-+++ b/src/backends/drm/drm_output.cpp
-@@ -568,6 +568,11 @@ DrmOutputLayer *DrmOutput::primaryLayer() const
-
- DrmOutputLayer *DrmOutput::cursorLayer() const
- {
-+ if (!m_pipeline) {
-+ // this can happen when the output gets hot-unplugged
-+ // FIXME fix output lifetimes so that this doesn't happen anymore...
-+ return nullptr;
-+ }
- return m_pipeline->cursorLayer();
- }
-
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_f82034f0_don-t-leak-lcms-tone-curves.patch kwin-6.3.6/debian/patches/upstream_f82034f0_don-t-leak-lcms-tone-curves.patch
--- kwin-6.3.5/debian/patches/upstream_f82034f0_don-t-leak-lcms-tone-curves.patch 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/patches/upstream_f82034f0_don-t-leak-lcms-tone-curves.patch 1970-01-01 01:00:00.000000000 +0100
@@ -1,79 +0,0 @@
-From f82034f0ac7f3a89b360fbf30bdd61e94aaee8fa Mon Sep 17 00:00:00 2001
-From: Xaver Hugl <xaver.hugl@gmail.com>
-Date: Thu, 22 May 2025 14:54:40 +0200
-Subject: [PATCH] don't leak lcms tone curves
-
-cmsStageAllocToneCurves duplicates them, it does not take ownership
-
-
-(cherry picked from commit 14604f68f835c88427fcef0a9e7380d5a182a182)
-
-Co-authored-by: Xaver Hugl <xaver.hugl@kde.org>
----
- src/colors/colordevice.cpp | 2 --
- src/core/colortransformation.cpp | 5 +++++
- src/core/iccprofile.cpp | 11 ++++-------
- 3 files changed, 9 insertions(+), 9 deletions(-)
-
-diff --git a/src/colors/colordevice.cpp b/src/colors/colordevice.cpp
-index ff2de5713fc..c58f2bae1a9 100644
---- a/src/colors/colordevice.cpp
-+++ b/src/colors/colordevice.cpp
-@@ -14,8 +14,6 @@
-
- #include <QTimer>
-
--#include <lcms2.h>
--
- namespace KWin
- {
-
-diff --git a/src/core/colortransformation.cpp b/src/core/colortransformation.cpp
-index c6d063e05b9..03a688f4003 100644
---- a/src/core/colortransformation.cpp
-+++ b/src/core/colortransformation.cpp
-@@ -86,6 +86,11 @@ std::unique_ptr<ColorTransformation> ColorTransformation::createScalingTransform
- auto g = cmsBuildParametricToneCurve(nullptr, 2, curveParams.data());
- curveParams = {1.0, scale.z(), 0.0};
- auto b = cmsBuildParametricToneCurve(nullptr, 2, curveParams.data());
-+ const auto guard = qScopeGuard([r, g, b]() {
-+ cmsFreeToneCurve(r);
-+ cmsFreeToneCurve(g);
-+ cmsFreeToneCurve(b);
-+ });
- if (!r || !g || !b) {
- qCWarning(KWIN_CORE) << "Failed to build tone curves";
- return nullptr;
-diff --git a/src/core/iccprofile.cpp b/src/core/iccprofile.cpp
-index 17be36c93d5..4707224ef67 100644
---- a/src/core/iccprofile.cpp
-+++ b/src/core/iccprofile.cpp
-@@ -267,14 +267,8 @@ IccProfile::Expected IccProfile::load(const QString &path)
- std::shared_ptr<ColorTransformation> vcgt;
- cmsToneCurve **vcgtTag = static_cast<cmsToneCurve **>(cmsReadTag(handle, cmsSigVcgtTag));
- if (vcgtTag && vcgtTag[0]) {
-- // Need to duplicate the VCGT tone curves as they are owned by the profile.
-- cmsToneCurve *toneCurves[] = {
-- cmsDupToneCurve(vcgtTag[0]),
-- cmsDupToneCurve(vcgtTag[1]),
-- cmsDupToneCurve(vcgtTag[2]),
-- };
- std::vector<std::unique_ptr<ColorPipelineStage>> stages;
-- stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, toneCurves)));
-+ stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, vcgtTag)));
- vcgt = std::make_shared<ColorTransformation>(std::move(stages));
- }
-
-@@ -403,6 +397,9 @@ IccProfile::Expected IccProfile::load(const QString &path)
- }
- std::vector<std::unique_ptr<ColorPipelineStage>> stages;
- stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, toneCurves.size(), toneCurves.data())));
-+ for (auto toneCurve : toneCurves) {
-+ cmsFreeToneCurve(toneCurve);
-+ }
- const auto inverseEOTF = std::make_shared<ColorTransformation>(std::move(stages));
- return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(bToA0), std::move(bToA1), inverseEOTF, vcgt, relativeBlackPoint, maxBrightness);
- }
---
-GitLab
-
diff -Nru kwin-6.3.5/debian/patches/upstream_fd0eef29_Fix-config-options-for-inactive-window-action.patch kwin-6.3.6/debian/patches/upstream_fd0eef29_Fix-config-options-for-inactive-window-action.patch
--- kwin-6.3.5/debian/patches/upstream_fd0eef29_Fix-config-options-for-inactive-window-action.patch 1970-01-01 01:00:00.000000000 +0100
+++ kwin-6.3.6/debian/patches/upstream_fd0eef29_Fix-config-options-for-inactive-window-action.patch 2025-07-16 00:06:03.000000000 +0200
@@ -0,0 +1,57 @@
+From fd0eef29d8c3cb55040ffacfcf77b17d87935ac2 Mon Sep 17 00:00:00 2001
+From: John Kizer <john.kizer@proton.me>
+Date: Wed, 30 Apr 2025 08:45:34 -0400
+Subject: [PATCH] Fix config options for inactive window action
+
+The "Activate, pass click and raise on release" option was added to the Window Actions Inactive Inner Window left click dropdown, but in this file was instead added to the Titlebar actions list: https://invent.kde.org/plasma/kwin/-/merge_requests/6555/diffs?file=794ca93c1a0a39541fac0b789307cde9ec80df1d#diff-content-794ca93c1a0a39541fac0b789307cde9ec80df1d
+
+This moves the added config file option to the Inner Window section, which should help it operate as expected when non-default options are selected.
+
+BUG: 501457
+---
+ src/kcms/options/kwinoptions_settings.kcfg | 6 +++---
+ src/kwin.kcfg | 2 +-
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/kcms/options/kwinoptions_settings.kcfg b/src/kcms/options/kwinoptions_settings.kcfg
+index 1d22c22d725..3b97101f058 100644
+--- a/src/kcms/options/kwinoptions_settings.kcfg
++++ b/src/kcms/options/kwinoptions_settings.kcfg
+@@ -201,9 +201,8 @@
+ </entry>
+
+ <entry key="CommandInactiveTitlebar1" type="Enum">
+- <default>ActivateRaiseOnReleaseAndPassClick</default>
++ <default>ActivateAndRaise</default>
+ <choices>
+- <choice name="ActivateRaiseOnReleaseAndPassClick" value="Activate, pass click and raise on release"></choice>
+ <choice name="ActivateAndRaise" value="Activate and raise"></choice>
+ <choice name="ActivateAndLower" value="Activate and lower"></choice>
+ <choice name="Activate"></choice>
+@@ -253,8 +252,9 @@
+ </entry>
+
+ <entry key="CommandWindow1" type="Enum">
+- <default>ActivateRaisePassClick</default>
++ <default>ActivateRaiseOnReleaseAndPassClick</default>
+ <choices>
++ <choice name="ActivateRaiseOnReleaseAndPassClick" value="Activate, pass click and raise on release"></choice>
+ <choice name="ActivateRaisePassClick" value="Activate, raise and pass click"></choice>
+ <choice name="ActivatePassClick" value="Activate and pass click"></choice>
+ <choice name="Activate"></choice>
+diff --git a/src/kwin.kcfg b/src/kwin.kcfg
+index bcbb1e8678e..985696ef2cf 100644
+--- a/src/kwin.kcfg
++++ b/src/kwin.kcfg
+@@ -25,7 +25,7 @@
+ <default>Operations menu</default>
+ </entry>
+ <entry name="CommandInactiveTitlebar1" type="String">
+- <default>Activate, pass click and raise on release</default>
++ <default>Activate and raise</default>
+ </entry>
+ <entry name="CommandInactiveTitlebar2" type="String">
+ <default>Nothing</default>
+--
+GitLab
+
diff -Nru kwin-6.3.5/po/pl/kwin.po kwin-6.3.6/po/pl/kwin.po
--- kwin-6.3.5/po/pl/kwin.po 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/po/pl/kwin.po 2025-07-08 13:45:08.000000000 +0200
@@ -1,5 +1,5 @@
# translation of kwin.po to
-# Version: $Revision: 1707783 $
+# Version: $Revision: 1708617 $
# translation of kwin.po to Polish
# translation of kwin.po to
# SPDX-FileCopyrightText: 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
@@ -16,7 +16,7 @@
"Project-Id-Version: kwin\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2025-04-26 02:35+0000\n"
-"PO-Revision-Date: 2025-01-11 11:58+0100\n"
+"PO-Revision-Date: 2025-05-10 12:07+0200\n"
"Last-Translator: Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>\n"
"Language-Team: Polish <kde-i18n-doc@kde.org>\n"
"Language: pl\n"
@@ -2792,7 +2792,7 @@
#: plugins/tileseditor/qml/TileDelegate.qml:211
#, kde-format
msgid "Add Floating Tile"
-msgstr "Dodaj pływający tytuł"
+msgstr "Dodaj pływające okno"
#: plugins/tileseditor/qml/TileDelegate.qml:191
#, kde-format
diff -Nru kwin-6.3.5/src/backends/drm/drm_crtc.cpp kwin-6.3.6/src/backends/drm/drm_crtc.cpp
--- kwin-6.3.5/src/backends/drm/drm_crtc.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_crtc.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -64,10 +64,9 @@
m_postBlendingColorOps.push_back(std::make_unique<LegacyMatrixColorOp>(next, &ctm));
next = m_postBlendingColorOps.back().get();
}
- if (!gpu()->isNVidia() && !gpu()->isI915() && degammaLut.isValid() && degammaLutSize.isValid() && degammaLutSize.value() > 0) {
- m_postBlendingColorOps.push_back(std::make_unique<DrmLutColorOp>(next, °ammaLut, degammaLutSize.value()));
- next = m_postBlendingColorOps.back().get();
- }
+ // DEGAMMA_LUT is intentionally not part of the post blending pipeline
+ // as on most hardware it actually maps to pre-blending operations,
+ // and more importantly it's buggy on Intel, AMD and NVidia...
postBlendingPipeline = next;
}
diff -Nru kwin-6.3.5/src/backends/drm/drm_egl_layer.cpp kwin-6.3.6/src/backends/drm/drm_egl_layer.cpp
--- kwin-6.3.5/src/backends/drm/drm_egl_layer.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_egl_layer.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -80,7 +80,9 @@
{
if (m_scanoutBuffer) {
const auto ret = m_surface.eglBackend()->importDmaBufAsTexture(*m_scanoutBuffer->buffer()->dmabufAttributes());
- ret->setContentTransform(offloadTransform().combine(OutputTransform::FlipY));
+ if (ret) {
+ ret->setContentTransform(offloadTransform().combine(OutputTransform::FlipY));
+ }
return ret;
} else {
return m_surface.texture();
@@ -104,6 +106,16 @@
// the hardware to some buffer format we can't switch away from
return false;
}
+ if (m_pipeline->gpu() != m_pipeline->gpu()->platform()->primaryGpu()) {
+ // Disable direct scanout between GPUs, as
+ // - there are some significant driver bugs with direct scanout from other GPUs,
+ // like https://gitlab.freedesktop.org/drm/amd/-/issues/2075
+ // - with implicit modifiers, direct scanout on secondary GPUs
+ // is also very unlikely to yield the correct results.
+ // TODO once we know what buffer a GPU is meant for, loosen this check again
+ // Right now this just assumes all buffers are on the primary GPU
+ return false;
+ }
if (m_pipeline->output()->colorProfileSource() == Output::ColorProfileSource::ICC && !m_pipeline->output()->highDynamicRange() && m_pipeline->iccProfile()) {
// TODO make the icc profile output a color pipeline too?
return false;
@@ -128,10 +140,6 @@
if (offloadTransform() != OutputTransform::Kind::Normal && (!plane || !plane->supportsTransformation(offloadTransform()))) {
return false;
}
- // importing a buffer from another GPU without an explicit modifier can mess up the buffer format
- if (buffer->dmabufAttributes()->modifier == DRM_FORMAT_MOD_INVALID && m_pipeline->gpu()->platform()->gpuCount() > 1) {
- return false;
- }
m_scanoutBuffer = m_pipeline->gpu()->importBuffer(buffer, FileDescriptor{});
if (m_scanoutBuffer) {
m_surface.forgetDamage(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal. It's more flexible and less error-prone
diff -Nru kwin-6.3.5/src/backends/drm/drm_egl_layer_surface.cpp kwin-6.3.6/src/backends/drm/drm_egl_layer_surface.cpp
--- kwin-6.3.5/src/backends/drm/drm_egl_layer_surface.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_egl_layer_surface.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -594,6 +594,12 @@
if (!slot) {
return nullptr;
}
+ if (!m_gpu->atomicModeSetting()) {
+ EglContext::currentContext()->pushFramebuffer(slot->framebuffer());
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ EglContext::currentContext()->popFramebuffer();
+ }
if (const auto ret = importBuffer(surface, slot.get(), FileDescriptor{}, nullptr, infiniteRegion())) {
surface->currentSlot = slot;
surface->currentFramebuffer = ret;
diff -Nru kwin-6.3.5/src/backends/drm/drm_output.cpp kwin-6.3.6/src/backends/drm/drm_output.cpp
--- kwin-6.3.5/src/backends/drm/drm_output.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_output.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -427,8 +427,8 @@
const double sdrGamutWideness = props->sdrGamutWideness.value_or(m_state.sdrGamutWideness);
const auto iccProfile = props->iccProfile.value_or(m_state.iccProfile);
if (colorSource == ColorProfileSource::ICC && !effectiveHdr && !effectiveWcg && iccProfile) {
- const double minBrightness = iccProfile->minBrightness().value_or(0);
const double maxBrightness = iccProfile->maxBrightness().value_or(200);
+ const double minBrightness = iccProfile->relativeBlackPoint().value_or(0) * maxBrightness;
const auto sdrColor = Colorimetry::fromName(NamedColorimetry::BT709).interpolateGamutTo(iccProfile->colorimetry(), sdrGamutWideness);
const bool allowSdrSoftwareBrightness = props->allowSdrSoftwareBrightness.value_or(m_state.allowSdrSoftwareBrightness);
const double brightnessFactor = (!m_brightnessDevice && allowSdrSoftwareBrightness) ? brightness : 1.0;
@@ -568,6 +568,11 @@
DrmOutputLayer *DrmOutput::cursorLayer() const
{
+ if (!m_pipeline) {
+ // this can happen when the output gets hot-unplugged
+ // FIXME fix output lifetimes so that this doesn't happen anymore...
+ return nullptr;
+ }
return m_pipeline->cursorLayer();
}
diff -Nru kwin-6.3.5/src/backends/drm/drm_pipeline.cpp kwin-6.3.6/src/backends/drm/drm_pipeline.cpp
--- kwin-6.3.5/src/backends/drm/drm_pipeline.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_pipeline.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -326,6 +326,9 @@
commit->addProperty(m_pending.crtc->active, 1);
commit->addBlob(m_pending.crtc->modeId, m_pending.mode->blob());
+ if (m_pending.crtc->degammaLut.isValid()) {
+ commit->addProperty(m_pending.crtc->degammaLut, 0);
+ }
const auto primary = m_pending.crtc->primaryPlane();
commit->addProperty(primary->crtcId, m_pending.crtc->id());
diff -Nru kwin-6.3.5/src/backends/drm/drm_pipeline_legacy.cpp kwin-6.3.6/src/backends/drm/drm_pipeline_legacy.cpp
--- kwin-6.3.5/src/backends/drm/drm_pipeline_legacy.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_pipeline_legacy.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -35,7 +35,7 @@
}
auto commit = std::make_unique<DrmLegacyCommit>(this, buffer, frame);
if (!commit->doPageflip(m_pending.presentationMode)) {
- qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
+ qCDebug(KWIN_DRM) << "Page flip failed:" << strerror(errno);
return errnoToError();
}
m_commitThread->setPendingCommit(std::move(commit));
diff -Nru kwin-6.3.5/src/colors/colordevice.cpp kwin-6.3.6/src/colors/colordevice.cpp
--- kwin-6.3.5/src/colors/colordevice.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/colors/colordevice.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -14,8 +14,6 @@
#include <QTimer>
-#include <lcms2.h>
-
namespace KWin
{
diff -Nru kwin-6.3.5/src/core/colortransformation.cpp kwin-6.3.6/src/core/colortransformation.cpp
--- kwin-6.3.5/src/core/colortransformation.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/core/colortransformation.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -86,6 +86,11 @@
auto g = cmsBuildParametricToneCurve(nullptr, 2, curveParams.data());
curveParams = {1.0, scale.z(), 0.0};
auto b = cmsBuildParametricToneCurve(nullptr, 2, curveParams.data());
+ const auto guard = qScopeGuard([r, g, b]() {
+ cmsFreeToneCurve(r);
+ cmsFreeToneCurve(g);
+ cmsFreeToneCurve(b);
+ });
if (!r || !g || !b) {
qCWarning(KWIN_CORE) << "Failed to build tone curves";
return nullptr;
diff -Nru kwin-6.3.5/src/core/iccprofile.cpp kwin-6.3.6/src/core/iccprofile.cpp
--- kwin-6.3.5/src/core/iccprofile.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/core/iccprofile.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -27,14 +27,14 @@
const ColorDescription IccProfile::s_connectionSpace = ColorDescription(CIEXYZD50, TransferFunction(TransferFunction::linear, 0, 1), 1, 0, 1, 1);
-IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> minBrightness, std::optional<double> maxBrightness)
+IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> relativeBlackPoint, std::optional<double> maxBrightness)
: m_handle(handle)
, m_colorimetry(colorimetry)
, m_bToA0Tag(std::move(bToA0Tag))
, m_bToA1Tag(std::move(bToA1Tag))
, m_inverseEOTF(inverseEOTF)
, m_vcgt(vcgt)
- , m_minBrightness(minBrightness)
+ , m_relativeBlackPoint(relativeBlackPoint)
, m_maxBrightness(maxBrightness)
{
}
@@ -44,9 +44,9 @@
cmsCloseProfile(m_handle);
}
-std::optional<double> IccProfile::minBrightness() const
+std::optional<double> IccProfile::relativeBlackPoint() const
{
- return m_minBrightness;
+ return m_relativeBlackPoint;
}
std::optional<double> IccProfile::maxBrightness() const
@@ -267,14 +267,8 @@
std::shared_ptr<ColorTransformation> vcgt;
cmsToneCurve **vcgtTag = static_cast<cmsToneCurve **>(cmsReadTag(handle, cmsSigVcgtTag));
if (vcgtTag && vcgtTag[0]) {
- // Need to duplicate the VCGT tone curves as they are owned by the profile.
- cmsToneCurve *toneCurves[] = {
- cmsDupToneCurve(vcgtTag[0]),
- cmsDupToneCurve(vcgtTag[1]),
- cmsDupToneCurve(vcgtTag[2]),
- };
std::vector<std::unique_ptr<ColorPipelineStage>> stages;
- stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, toneCurves)));
+ stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, vcgtTag)));
vcgt = std::make_shared<ColorTransformation>(std::move(stages));
}
@@ -341,16 +335,16 @@
return Expected(i18n("ICC profile \"%1\" is broken, its primaries are invalid", path));
}
- std::optional<double> minBrightness;
std::optional<double> maxBrightness;
if (cmsCIEXYZ *luminance = static_cast<cmsCIEXYZ *>(cmsReadTag(handle, cmsSigLuminanceTag))) {
// for some reason, lcms exposes the luminance as a XYZ triple...
// only Y is non-zero, and it's the brightness in nits
maxBrightness = luminance->Y;
- cmsCIEXYZ blackPoint;
- if (cmsDetectDestinationBlackPoint(&blackPoint, handle, INTENT_RELATIVE_COLORIMETRIC, 0)) {
- minBrightness = blackPoint.Y * luminance->Y;
- }
+ }
+ std::optional<double> relativeBlackPoint;
+ cmsCIEXYZ blackPoint;
+ if (cmsDetectDestinationBlackPoint(&blackPoint, handle, INTENT_RELATIVE_COLORIMETRIC, 0)) {
+ relativeBlackPoint = blackPoint.Y;
}
if (cmsIsTag(handle, cmsSigBToD1Tag) && !cmsIsTag(handle, cmsSigBToA1Tag) && !cmsIsTag(handle, cmsSigBToA0Tag)) {
@@ -403,8 +397,11 @@
}
std::vector<std::unique_ptr<ColorPipelineStage>> stages;
stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, toneCurves.size(), toneCurves.data())));
+ for (auto toneCurve : toneCurves) {
+ cmsFreeToneCurve(toneCurve);
+ }
const auto inverseEOTF = std::make_shared<ColorTransformation>(std::move(stages));
- return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(bToA0), std::move(bToA1), inverseEOTF, vcgt, minBrightness, maxBrightness);
+ return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(bToA0), std::move(bToA1), inverseEOTF, vcgt, relativeBlackPoint, maxBrightness);
}
}
diff -Nru kwin-6.3.5/src/core/iccprofile.h kwin-6.3.6/src/core/iccprofile.h
--- kwin-6.3.5/src/core/iccprofile.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/core/iccprofile.h 2025-07-08 13:45:08.000000000 +0200
@@ -25,7 +25,7 @@
class KWIN_EXPORT IccProfile
{
public:
- explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> minBrightness, std::optional<double> maxBrightness);
+ explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, std::optional<ColorPipeline> &&bToA0Tag, std::optional<ColorPipeline> &&bToA1Tag, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> relativeBlackPoint, std::optional<double> maxBrightness);
~IccProfile();
/**
@@ -44,7 +44,7 @@
*/
std::shared_ptr<ColorTransformation> vcgt() const;
const Colorimetry &colorimetry() const;
- std::optional<double> minBrightness() const;
+ std::optional<double> relativeBlackPoint() const;
std::optional<double> maxBrightness() const;
// TODO Plasma 6.4 port this back to std::expected
@@ -72,7 +72,7 @@
const std::optional<ColorPipeline> m_bToA1Tag;
const std::shared_ptr<ColorTransformation> m_inverseEOTF;
const std::shared_ptr<ColorTransformation> m_vcgt;
- const std::optional<double> m_minBrightness;
+ const std::optional<double> m_relativeBlackPoint;
const std::optional<double> m_maxBrightness;
};
diff -Nru kwin-6.3.5/src/core/renderloop.cpp kwin-6.3.6/src/core/renderloop.cpp
--- kwin-6.3.5/src/core/renderloop.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/core/renderloop.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -264,8 +264,8 @@
const bool vrr = d->presentationMode == PresentationMode::AdaptiveSync || d->presentationMode == PresentationMode::AdaptiveAsync;
const bool tearing = d->presentationMode == PresentationMode::Async || d->presentationMode == PresentationMode::AdaptiveAsync;
if ((vrr || tearing) && workspace() && workspace()->activeWindow() && d->output) {
- Window *const activeWindow = workspace()->activeWindow();
- if ((item || layer || outputLayer) && activeWindowControlsVrrRefreshRate() && item != activeWindow->surfaceItem()) {
+ SurfaceItem *const surfaceItem = workspace()->activeWindow()->surfaceItem();
+ if ((item || layer || outputLayer) && activeWindowControlsVrrRefreshRate() && item != surfaceItem && !surfaceItem->isAncestorOf(item)) {
d->delayedVrrTimer.start();
return;
}
diff -Nru kwin-6.3.5/src/layers.cpp kwin-6.3.6/src/layers.cpp
--- kwin-6.3.5/src/layers.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/layers.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -210,7 +210,7 @@
cl.clear();
for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
X11Window *window = qobject_cast<X11Window *>(*it);
- if (window && !window->isUnmanaged()) {
+ if (window && !window->isDeleted() && !window->isUnmanaged()) {
cl.push_back(window->window());
}
}
diff -Nru kwin-6.3.5/src/outputconfigurationstore.cpp kwin-6.3.6/src/outputconfigurationstore.cpp
--- kwin-6.3.5/src/outputconfigurationstore.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/outputconfigurationstore.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -509,7 +509,7 @@
.manualTransform = existingData.manualTransform.value_or(kscreenChangeSet.transform.value_or(output->panelOrientation())),
.overscan = existingData.overscan.value_or(kscreenChangeSet.overscan.value_or(0)),
.rgbRange = existingData.rgbRange.value_or(kscreenChangeSet.rgbRange.value_or(Output::RgbRange::Automatic)),
- .vrrPolicy = existingData.vrrPolicy.value_or(kscreenChangeSet.vrrPolicy.value_or(VrrPolicy::Automatic)),
+ .vrrPolicy = existingData.vrrPolicy.value_or(kscreenChangeSet.vrrPolicy.value_or(VrrPolicy::Never)),
.highDynamicRange = existingData.highDynamicRange.value_or(false),
.referenceLuminance = existingData.referenceLuminance.value_or(std::clamp(output->maxAverageBrightness().value_or(200), 200.0, 500.0)),
.wideColorGamut = existingData.wideColorGamut.value_or(false),
@@ -825,9 +825,17 @@
}
if (const auto it = data.find("maxPeakBrightnessOverride"); it != data.end() && it->isDouble()) {
state.maxPeakBrightnessOverride = it->toDouble();
+ if (*state.maxPeakBrightnessOverride < 50) {
+ // clearly nonsense
+ state.maxPeakBrightnessOverride.reset();
+ }
}
if (const auto it = data.find("maxAverageBrightnessOverride"); it != data.end() && it->isDouble()) {
state.maxAverageBrightnessOverride = it->toDouble();
+ if (*state.maxAverageBrightnessOverride < 50) {
+ // clearly nonsense
+ state.maxAverageBrightnessOverride.reset();
+ }
}
if (const auto it = data.find("minBrightnessOverride"); it != data.end() && it->isDouble()) {
state.minBrightnessOverride = it->toDouble();
diff -Nru kwin-6.3.5/src/plugins/colorpicker/colorpicker.cpp kwin-6.3.6/src/plugins/colorpicker/colorpicker.cpp
--- kwin-6.3.5/src/plugins/colorpicker/colorpicker.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/colorpicker/colorpicker.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -69,8 +69,8 @@
OpenGlContext *context = effects->openglContext();
context->glReadnPixels(texturePosition.x(), renderTarget.size().height() - texturePosition.y() - PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE, GL_RGBA, GL_FLOAT, sizeof(float) * data.size(), data.data());
- QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB, RenderingIntent::RelativeColorimetric);
- QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(sRGB.x(), sRGB.y(), sRGB.z())));
+ QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB, RenderingIntent::RelativeColorimetricWithBPC);
+ QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(std::round(sRGB.x()), std::round(sRGB.y()), std::round(sRGB.z()))));
setPicking(false);
m_scheduledPosition = QPoint(-1, -1);
}
diff -Nru kwin-6.3.5/src/plugins/slideback/motionmanager.cpp kwin-6.3.6/src/plugins/slideback/motionmanager.cpp
--- kwin-6.3.5/src/plugins/slideback/motionmanager.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/slideback/motionmanager.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -69,11 +69,11 @@
}
double strength = 0.12;
- double smoothness = 4.0;
+ double smoothness = 2.5;
if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) {
// If the factor is == 0 then we just skip the calculation completely
strength = 0.12 / effects->animationTimeFactor();
- smoothness = effects->animationTimeFactor() * 4.0;
+ smoothness = effects->animationTimeFactor() * 2.5;
}
WindowMotion &motion = m_managedWindows[w];
diff -Nru kwin-6.3.5/src/plugins/systembell/systembell.cpp kwin-6.3.6/src/plugins/systembell/systembell.cpp
--- kwin-6.3.5/src/plugins/systembell/systembell.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/systembell/systembell.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -106,6 +106,13 @@
m_audioThrottleTimer.setInterval(100ms);
m_audioThrottleTimer.setSingleShot(true);
+
+ // The Web Content Accessibility Guidelines (WCAG) recommend that any
+ // element that flashes in the screen must have a maximum period of
+ // 3Hz to avoid the risk of Photosensitivity Seizures.
+ // 3Hz is 333ms, double that to account for the window un-inverting, and round up
+ m_visualThrottleTimer.setInterval(700ms);
+ m_visualThrottleTimer.setSingleShot(true);
}
SystemBellEffect::~SystemBellEffect()
@@ -192,6 +199,12 @@
if (m_visibleBell) {
m_allWindows = true;
+ if (m_visualThrottleTimer.isActive()) {
+ return;
+ }
+
+ m_visualThrottleTimer.start();
+
const auto windows = effects->stackingOrder();
for (EffectWindow *window : windows) {
flash(window);
@@ -226,6 +239,12 @@
}
if (m_visibleBell) {
+ if (m_visualThrottleTimer.isActive()) {
+ return;
+ }
+
+ m_visualThrottleTimer.start();
+
m_windows.append(window);
flash(window);
diff -Nru kwin-6.3.5/src/plugins/systembell/systembell.h kwin-6.3.6/src/plugins/systembell/systembell.h
--- kwin-6.3.5/src/plugins/systembell/systembell.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/systembell/systembell.h 2025-07-08 13:45:08.000000000 +0200
@@ -80,6 +80,7 @@
static QTimer *s_systemBellRemoveTimer;
QTimer m_audioThrottleTimer;
+ QTimer m_visualThrottleTimer;
static XdgSystemBellV1Interface *s_systemBell;
};
diff -Nru kwin-6.3.5/src/plugins/translucency/package/contents/code/main.js kwin-6.3.6/src/plugins/translucency/package/contents/code/main.js
--- kwin-6.3.5/src/plugins/translucency/package/contents/code/main.js 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/translucency/package/contents/code/main.js 2025-07-08 13:45:08.000000000 +0200
@@ -212,7 +212,7 @@
window.minimizedChanged.connect(() => {
if (window.minimized) {
- translucencyEffect.cancelAnimations();
+ translucencyEffect.cancelAnimations(window);
} else {
translucencyEffect.startAnimation(window);
translucencyEffect.inactive.animate(window);
diff -Nru kwin-6.3.5/src/scene/item.cpp kwin-6.3.6/src/scene/item.cpp
--- kwin-6.3.5/src/scene/item.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/item.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -631,6 +631,13 @@
m_effectCount--;
}
+bool Item::isAncestorOf(const Item *item) const
+{
+ return std::ranges::any_of(m_childItems, [item](const Item *child) {
+ return child == item || child->isAncestorOf(item);
+ });
+}
+
} // namespace KWin
#include "moc_item.cpp"
diff -Nru kwin-6.3.5/src/scene/item.h kwin-6.3.6/src/scene/item.h
--- kwin-6.3.5/src/scene/item.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/item.h 2025-07-08 13:45:08.000000000 +0200
@@ -142,6 +142,8 @@
void addEffect();
void removeEffect();
+ bool isAncestorOf(const Item *item) const;
+
Q_SIGNALS:
void childAdded(Item *item);
/**
diff -Nru kwin-6.3.5/src/scene/surfaceitem.cpp kwin-6.3.6/src/scene/surfaceitem.cpp
--- kwin-6.3.5/src/scene/surfaceitem.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/surfaceitem.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -284,7 +284,7 @@
std::chrono::nanoseconds ret = frameTimeEstimation();
const auto children = childItems();
for (Item *child : children) {
- ret = std::max(ret, static_cast<SurfaceItem *>(child)->frameTimeEstimation());
+ ret = std::min(ret, static_cast<SurfaceItem *>(child)->frameTimeEstimation());
}
return ret;
}
diff -Nru kwin-6.3.5/src/scene/surfaceitem_wayland.cpp kwin-6.3.6/src/scene/surfaceitem_wayland.cpp
--- kwin-6.3.5/src/scene/surfaceitem_wayland.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/surfaceitem_wayland.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -67,6 +67,8 @@
setBufferSourceBox(surface->bufferSourceBox());
setBufferSize(surface->bufferSize());
setColorDescription(surface->colorDescription());
+ setRenderingIntent(surface->renderingIntent());
+ setPresentationHint(surface->presentationModeHint());
setOpacity(surface->alphaMultiplier());
}
diff -Nru kwin-6.3.5/src/scene/workspacescene.cpp kwin-6.3.6/src/scene/workspacescene.cpp
--- kwin-6.3.5/src/scene/workspacescene.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/workspacescene.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -269,7 +269,7 @@
continue;
}
Window *window = static_cast<WindowItem *>(item)->window();
- if (!window->isOnOutput(output)) {
+ if (window->moveResizeOutput() != output) {
continue;
}
if (auto surface = window->surface()) {
diff -Nru kwin-6.3.5/src/utils/xcbutils.cpp kwin-6.3.6/src/utils/xcbutils.cpp
--- kwin-6.3.5/src/utils/xcbutils.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/utils/xcbutils.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -672,5 +672,20 @@
nativeFloor(outputRect.width()), nativeFloor(outputRect.height())));
}
+QString atomName(xcb_atom_t atom)
+{
+ xcb_connection_t *xcbConn = kwinApp()->x11Connection();
+ xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom);
+ xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, nullptr);
+ if (!nameReply) {
+ return QString();
+ }
+
+ const size_t length = xcb_get_atom_name_name_length(nameReply);
+ QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), length);
+ free(nameReply);
+ return name;
+}
+
} // namespace Xcb
} // namespace KWin
diff -Nru kwin-6.3.5/src/utils/xcbutils.h kwin-6.3.6/src/utils/xcbutils.h
--- kwin-6.3.5/src/utils/xcbutils.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/utils/xcbutils.h 2025-07-08 13:45:08.000000000 +0200
@@ -61,6 +61,8 @@
*/
QRectF KWIN_EXPORT nativeFloor(const QRectF &value);
+QString KWIN_EXPORT atomName(xcb_atom_t atom);
+
// forward declaration of methods
static void defineCursor(xcb_window_t window, xcb_cursor_t cursor);
static void setInputFocus(xcb_window_t window, uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = xTime());
diff -Nru kwin-6.3.5/src/wayland/colormanagement_v1.cpp kwin-6.3.6/src/wayland/colormanagement_v1.cpp
--- kwin-6.3.5/src/wayland/colormanagement_v1.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/colormanagement_v1.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -467,31 +467,51 @@
Q_UNREACHABLE();
}
-static uint32_t kwinPrimariesToProtoPrimaires(NamedColorimetry primaries)
+static bool compareXY(double left, double right)
{
- switch (primaries) {
- case NamedColorimetry::BT709:
+ return std::round(left / s_primaryUnit) == std::round(right / s_primaryUnit);
+}
+
+static bool compareXY(xy left, xy right)
+{
+ return compareXY(left.x, right.x) && compareXY(left.y, right.y);
+}
+
+static bool comparePrimaries(const Colorimetry &left, const Colorimetry &right)
+{
+ // this can't just use Colorimetry::operator==
+ // as that has a different resolution from the Wayland protocol
+ return compareXY(left.red().toxy(), right.red().toxy())
+ && compareXY(left.green().toxy(), right.green().toxy())
+ && compareXY(left.blue().toxy(), right.blue().toxy())
+ && compareXY(left.white().toxy(), right.white().toxy());
+}
+
+static std::optional<uint32_t> kwinPrimariesToProtoPrimaires(const Colorimetry &primaries)
+{
+ if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::BT709))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_srgb;
- case NamedColorimetry::PAL_M:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::PAL_M))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal_m;
- case NamedColorimetry::PAL:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::PAL))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal;
- case NamedColorimetry::NTSC:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::NTSC))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_ntsc;
- case NamedColorimetry::GenericFilm:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::GenericFilm))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_generic_film;
- case NamedColorimetry::BT2020:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::BT2020))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_bt2020;
- case NamedColorimetry::CIEXYZ:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::CIEXYZ))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_cie1931_xyz;
- case NamedColorimetry::DCIP3:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::DCIP3))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_dci_p3;
- case NamedColorimetry::DisplayP3:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::DisplayP3))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_display_p3;
- case NamedColorimetry::AdobeRGB:
+ } else if (comparePrimaries(primaries, Colorimetry::fromName(NamedColorimetry::AdobeRGB))) {
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_adobe_rgb;
+ } else {
+ return std::nullopt;
}
- Q_UNREACHABLE();
}
void ImageDescriptionV1::wp_image_description_v1_get_information(Resource *qtResource, uint32_t information)
@@ -513,8 +533,8 @@
round(containerGreen.x), round(containerGreen.y),
round(containerBlue.x), round(containerBlue.y),
round(containerWhite.x), round(containerWhite.y));
- if (auto name = m_description->containerColorimetry().name()) {
- wp_image_description_info_v1_send_primaries_named(resource, kwinPrimariesToProtoPrimaires(*name));
+ if (auto name = kwinPrimariesToProtoPrimaires(m_description->containerColorimetry())) {
+ wp_image_description_info_v1_send_primaries_named(resource, *name);
}
if (auto m = m_description->masteringColorimetry()) {
const xyY masterRed = m->red().toxyY();
@@ -528,13 +548,13 @@
round(masterWhite.x), round(masterWhite.y));
}
if (auto maxfall = m_description->maxAverageLuminance()) {
- wp_image_description_info_v1_send_target_max_fall(resource, *maxfall);
+ wp_image_description_info_v1_send_target_max_fall(resource, std::round(*maxfall));
}
if (auto maxcll = m_description->maxHdrLuminance()) {
- wp_image_description_info_v1_send_target_max_cll(resource, *maxcll);
+ wp_image_description_info_v1_send_target_max_cll(resource, std::round(*maxcll));
}
wp_image_description_info_v1_send_luminances(resource, std::round(m_description->transferFunction().minLuminance / s_minLuminanceUnit), std::round(m_description->transferFunction().maxLuminance), std::round(m_description->referenceLuminance()));
- wp_image_description_info_v1_send_target_luminance(resource, m_description->minLuminance(), m_description->maxHdrLuminance().value_or(800));
+ wp_image_description_info_v1_send_target_luminance(resource, std::round(m_description->minLuminance() / s_minLuminanceUnit), std::round(m_description->maxHdrLuminance().value_or(800)));
wp_image_description_info_v1_send_tf_named(resource, kwinTFtoProtoTF(m_description->transferFunction()));
wp_image_description_info_v1_send_done(resource);
wl_resource_destroy(resource);
diff -Nru kwin-6.3.5/src/wayland/outputmanagement_v2.cpp kwin-6.3.6/src/wayland/outputmanagement_v2.cpp
--- kwin-6.3.5/src/wayland/outputmanagement_v2.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/outputmanagement_v2.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -324,6 +324,14 @@
if (invalid) {
return;
}
+ if (max_peak_brightness != -1 && max_peak_brightness < 50) {
+ failureReason = QStringLiteral("Invalid peak brightness override requested");
+ return;
+ }
+ if (max_average_brightness != -1 && max_average_brightness < 50) {
+ failureReason = QStringLiteral("Invalid max average brightness override requested");
+ return;
+ }
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
config.changeSet(output->handle())->maxPeakBrightnessOverride = max_peak_brightness == -1 ? std::nullopt : std::optional<double>(max_peak_brightness);
config.changeSet(output->handle())->maxAverageBrightnessOverride = max_average_brightness == -1 ? std::nullopt : std::optional<double>(max_average_brightness);
diff -Nru kwin-6.3.5/src/wayland/tablet_v2.cpp kwin-6.3.6/src/wayland/tablet_v2.cpp
--- kwin-6.3.5/src/wayland/tablet_v2.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/tablet_v2.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -76,7 +76,7 @@
{
}
- void update(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot)
+ void update(quint32 serial, SurfaceInterface *surface, const QPointF &hotspot)
{
const bool diff = m_serial != serial || m_surface != surface || m_hotspot != hotspot;
if (diff) {
@@ -92,7 +92,7 @@
quint32 m_serial = 0;
QPointer<SurfaceInterface> m_surface;
- QPoint m_hotspot;
+ QPointF m_hotspot;
};
TabletSurfaceCursorV2::TabletSurfaceCursorV2()
@@ -103,7 +103,7 @@
TabletSurfaceCursorV2::~TabletSurfaceCursorV2() = default;
-QPoint TabletSurfaceCursorV2::hotspot() const
+QPointF TabletSurfaceCursorV2::hotspot() const
{
return d->m_hotspot;
}
@@ -170,6 +170,7 @@
void zwp_tablet_tool_v2_set_cursor(Resource *resource, uint32_t serial, struct ::wl_resource *_surface, int32_t hotspot_x, int32_t hotspot_y) override
{
SurfaceInterface *surface = SurfaceInterface::get(_surface);
+ QPointF hotspot = QPointF(hotspot_x, hotspot_y);
if (surface) {
static SurfaceRole cursorRole(QByteArrayLiteral("tablet_cursor_v2"));
if (const SurfaceRole *role = surface->role()) {
@@ -181,10 +182,11 @@
} else {
surface->setRole(&cursorRole);
}
+ hotspot /= surface->client()->scaleOverride();
}
TabletSurfaceCursorV2 *c = m_cursors[resource->client()];
- c->d->update(serial, surface, {hotspot_x, hotspot_y});
+ c->d->update(serial, surface, hotspot);
const auto resources = targetResources();
if (std::any_of(resources.begin(), resources.end(), [resource](const Resource *res) {
return res->handle == resource->handle;
diff -Nru kwin-6.3.5/src/wayland/tablet_v2.h kwin-6.3.6/src/wayland/tablet_v2.h
--- kwin-6.3.5/src/wayland/tablet_v2.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/tablet_v2.h 2025-07-08 13:45:08.000000000 +0200
@@ -63,7 +63,7 @@
Q_OBJECT
public:
~TabletSurfaceCursorV2() override;
- QPoint hotspot() const;
+ QPointF hotspot() const;
quint32 enteredSerial() const;
SurfaceInterface *surface() const;
diff -Nru kwin-6.3.5/src/wayland/xdgoutput_v1.cpp kwin-6.3.6/src/wayland/xdgoutput_v1.cpp
--- kwin-6.3.5/src/wayland/xdgoutput_v1.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/xdgoutput_v1.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -185,7 +185,7 @@
ClientConnection *connection = output->display()->getConnection(resource->client());
qreal scaleOverride = connection->scaleOverride();
- send_logical_position(resource->handle, pos.x() * scaleOverride, pos.y() * scaleOverride);
+ send_logical_position(resource->handle, std::round(pos.x() * scaleOverride), std::round(pos.y() * scaleOverride));
}
void XdgOutputV1Interface::sendDone(Resource *resource)
diff -Nru kwin-6.3.5/src/workspace.cpp kwin-6.3.6/src/workspace.cpp
--- kwin-6.3.5/src/workspace.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/workspace.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -82,8 +82,6 @@
#include <QDBusConnection>
#include <QDBusPendingCall>
#include <QMetaProperty>
-// xcb
-#include <xcb/xinerama.h>
namespace KWin
{
@@ -957,14 +955,7 @@
void Workspace::slotCurrentDesktopChanged(VirtualDesktop *oldDesktop, VirtualDesktop *newDesktop)
{
- closeActivePopup();
- ++block_focus;
- StackingUpdatesBlocker blocker(this);
- updateWindowVisibilityOnDesktopChange(newDesktop);
- // Restore the focus on this desktop
- --block_focus;
-
- activateWindowOnDesktop(newDesktop);
+ updateWindowVisibilityAndActivateOnDesktopChange(newDesktop);
Q_EMIT currentDesktopChanged(oldDesktop, m_moveResizeWindow);
}
@@ -987,7 +978,7 @@
if (!c) {
continue;
}
- if (!c->isOnDesktop(newDesktop) && c != m_moveResizeWindow && c->isOnCurrentActivity()) {
+ if (!(c->isOnDesktop(newDesktop) && c->isOnCurrentActivity()) && c != m_moveResizeWindow) {
(c)->updateVisibility();
}
}
@@ -997,6 +988,8 @@
}
#endif
+ // FIXME: Keep Move/Resize window across activities
+
if (m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop(newDesktop)) {
m_moveResizeWindow->setDesktops({newDesktop});
}
@@ -1017,6 +1010,18 @@
}
}
+void Workspace::updateWindowVisibilityAndActivateOnDesktopChange(VirtualDesktop *newDesktop)
+{
+ closeActivePopup();
+ ++block_focus;
+ StackingUpdatesBlocker blocker(this);
+ updateWindowVisibilityOnDesktopChange(newDesktop);
+ // Restore the focus on this desktop
+ --block_focus;
+
+ activateWindowOnDesktop(newDesktop);
+}
+
void Workspace::activateWindowOnDesktop(VirtualDesktop *desktop)
{
Window *window = nullptr;
@@ -1026,7 +1031,7 @@
// If "unreasonable focus policy" and m_activeWindow is on_all_desktops and
// under mouse (Hence == old_active_window), conserve focus.
// (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
- else if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) {
+ else if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) {
window = m_activeWindow;
}
@@ -1047,7 +1052,7 @@
Window *Workspace::findWindowToActivateOnDesktop(VirtualDesktop *desktop)
{
- if (m_moveResizeWindow != nullptr && m_activeWindow == m_moveResizeWindow && m_focusChain->contains(m_activeWindow, desktop) && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) {
+ if (m_moveResizeWindow != nullptr && m_activeWindow == m_moveResizeWindow && m_focusChain->contains(m_activeWindow, desktop) && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) {
// A requestFocus call will fail, as the window is already active
return m_activeWindow;
}
@@ -1088,77 +1093,8 @@
if (!m_activities) {
return;
}
- // closeActivePopup();
- ++block_focus;
- // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
- StackingUpdatesBlocker blocker(this);
-
- // Optimized Desktop switching: unmapping done from back to front
- // mapping done from front to back => less exposure events
- // Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
-
-#if KWIN_BUILD_X11
- for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
- X11Window *window = qobject_cast<X11Window *>(*it);
- if (!window) {
- continue;
- }
- if (!window->isOnActivity(new_activity) && window != m_moveResizeWindow && window->isOnCurrentDesktop()) {
- window->updateVisibility();
- }
- }
-
- // Now propagate the change, after hiding, before showing
- // rootInfo->setCurrentDesktop( currentDesktop() );
-
- /* TODO someday enable dragging windows to other activities
- if ( m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop( new_desktop ))
- {
- m_moveResizeWindow->setDesktop( new_desktop );
- */
- for (int i = stacking_order.size() - 1; i >= 0; --i) {
- X11Window *window = qobject_cast<X11Window *>(stacking_order.at(i));
- if (!window) {
- continue;
- }
- if (window->isOnActivity(new_activity)) {
- window->updateVisibility();
- }
- }
-#endif
-
- // FIXME not sure if I should do this either
- if (showingDesktop()) { // Do this only after desktop change to avoid flicker
- setShowingDesktop(false);
- }
-
- // Restore the focus on this desktop
- --block_focus;
- Window *window = nullptr;
-
- // FIXME below here is a lot of focuschain stuff, probably all wrong now
- // Keep active window focused if it's on the new activity
- if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) {
- window = m_activeWindow;
- } else if (options->focusPolicyIsReasonable()) {
- // Search in focus chain
- window = m_focusChain->getForActivation(VirtualDesktopManager::self()->currentDesktop());
- }
-
- if (!window) {
- window = findDesktop(VirtualDesktopManager::self()->currentDesktop(), activeOutput());
- }
-
- if (window != m_activeWindow) {
- setActiveWindow(nullptr);
- }
-
- if (window) {
- requestFocus(window);
- } else {
- focusToNull();
- }
+ updateWindowVisibilityAndActivateOnDesktopChange(VirtualDesktopManager::self()->currentDesktop());
Q_EMIT currentActivityChanged();
#endif
@@ -1185,6 +1121,16 @@
return bestOutput;
}
+Output *Workspace::findOutput(const QString &name) const
+{
+ for (Output *output : std::as_const(m_outputs)) {
+ if (output->name() == name) {
+ return output;
+ }
+ }
+ return nullptr;
+}
+
Output *Workspace::findOutput(Output *reference, Direction direction, bool wrapAround) const
{
QList<Output *> relevantOutputs;
@@ -2582,31 +2528,26 @@
return nullptr;
}
- const UniqueCPtr<xcb_xinerama_is_active_reply_t> active{xcb_xinerama_is_active_reply(connection, xcb_xinerama_is_active(connection), nullptr)};
- if (!active || !active->state) {
+ xcb_randr_get_monitors_cookie_t cookie = xcb_randr_get_monitors(kwinApp()->x11Connection(), kwinApp()->x11RootWindow(), 1);
+ const UniqueCPtr<xcb_randr_get_monitors_reply_t> monitors(xcb_randr_get_monitors_reply(kwinApp()->x11Connection(), cookie, nullptr));
+ if (!monitors) {
return nullptr;
}
- const UniqueCPtr<xcb_xinerama_query_screens_reply_t> screens(xcb_xinerama_query_screens_reply(connection, xcb_xinerama_query_screens(connection), nullptr));
- if (!screens) {
- return nullptr;
+ xcb_randr_monitor_info_t *monitorInfo = nullptr;
+ for (auto it = xcb_randr_get_monitors_monitors_iterator(monitors.get()); it.rem; xcb_randr_monitor_info_next(&it)) {
+ const int current = monitors->nMonitors - it.rem;
+ if (current == index) {
+ monitorInfo = it.data;
+ break;
+ }
}
- const int infoCount = xcb_xinerama_query_screens_screen_info_length(screens.get());
- if (index < 0 || index >= infoCount) {
+ if (!monitorInfo) {
return nullptr;
}
- const xcb_xinerama_screen_info_t *infos = xcb_xinerama_query_screens_screen_info(screens.get());
- const QRect needle(infos[index].x_org, infos[index].y_org, infos[index].width, infos[index].height);
-
- for (Output *output : std::as_const(m_outputs)) {
- if (Xcb::toXNative(output->geometryF()) == needle) {
- return output;
- }
- }
-
- return nullptr;
+ return findOutput(Xcb::atomName(monitorInfo->name));
}
#endif
diff -Nru kwin-6.3.5/src/workspace.h kwin-6.3.6/src/workspace.h
--- kwin-6.3.5/src/workspace.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/workspace.h 2025-07-08 13:45:08.000000000 +0200
@@ -357,6 +357,7 @@
DirectionNext
};
Output *findOutput(Output *reference, Direction direction, bool wrapAround = false) const;
+ Output *findOutput(const QString &name) const;
void switchToOutput(Output *output);
QList<Output *> outputs() const;
@@ -636,6 +637,7 @@
void closeActivePopup();
void updateWindowVisibilityOnDesktopChange(VirtualDesktop *newDesktop);
+ void updateWindowVisibilityAndActivateOnDesktopChange(VirtualDesktop *newDesktop);
void activateWindowOnDesktop(VirtualDesktop *desktop);
Window *findWindowToActivateOnDesktop(VirtualDesktop *desktop);
void removeWindow(Window *window);
diff -Nru kwin-6.3.5/src/x11window.cpp kwin-6.3.6/src/x11window.cpp
--- kwin-6.3.5/src/x11window.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/x11window.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -2529,6 +2529,11 @@
return;
}
+ static bool noXsync = qEnvironmentVariableIntValue("KWIN_X11_NO_SYNC_REQUEST") == 1;
+ if (noXsync) {
+ return;
+ }
+
Xcb::Property syncProp(false, window(), atoms->net_wm_sync_request_counter, XCB_ATOM_CARDINAL, 0, 1);
const xcb_sync_counter_t counter = syncProp.value<xcb_sync_counter_t>(XCB_NONE);
if (counter != XCB_NONE) {
diff -Nru kwin-6.3.5/src/xwayland/drag_x.cpp kwin-6.3.6/src/xwayland/drag_x.cpp
--- kwin-6.3.5/src/xwayland/drag_x.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/xwayland/drag_x.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -51,7 +51,7 @@
} else if (atom == atoms->netscape_url) {
mimeTypes << QStringLiteral("_NETSCAPE_URL");
} else {
- mimeTypes << Selection::atomName(atom);
+ mimeTypes << Xcb::atomName(atom);
}
return mimeTypes;
}
diff -Nru kwin-6.3.5/src/xwayland/selection.cpp kwin-6.3.6/src/xwayland/selection.cpp
--- kwin-6.3.5/src/xwayland/selection.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/xwayland/selection.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -45,21 +45,6 @@
return Xcb::Atom(mimeType.toLatin1(), false, kwinApp()->x11Connection());
}
-QString Selection::atomName(xcb_atom_t atom)
-{
- xcb_connection_t *xcbConn = kwinApp()->x11Connection();
- xcb_get_atom_name_cookie_t nameCookie = xcb_get_atom_name(xcbConn, atom);
- xcb_get_atom_name_reply_t *nameReply = xcb_get_atom_name_reply(xcbConn, nameCookie, nullptr);
- if (!nameReply) {
- return QString();
- }
-
- const size_t length = xcb_get_atom_name_name_length(nameReply);
- QString name = QString::fromLatin1(xcb_get_atom_name_name(nameReply), length);
- free(nameReply);
- return name;
-}
-
QStringList Selection::atomToMimeTypes(xcb_atom_t atom)
{
QStringList mimeTypes;
@@ -74,7 +59,7 @@
} else if (atom == atoms->targets || atom == atoms->timestamp) {
// Ignore known ICCCM internal atoms
} else {
- const QString atomNameName = atomName(atom);
+ const QString atomNameName = Xcb::atomName(atom);
// Ignore other non-mimetype atoms
if (atomNameName.contains(QLatin1Char('/'))) {
mimeTypes << atomNameName;
diff -Nru kwin-6.3.5/src/xwayland/selection.h kwin-6.3.6/src/xwayland/selection.h
--- kwin-6.3.5/src/xwayland/selection.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/xwayland/selection.h 2025-07-08 13:45:08.000000000 +0200
@@ -49,7 +49,6 @@
static xcb_atom_t mimeTypeToAtom(const QString &mimeType);
static xcb_atom_t mimeTypeToAtomLiteral(const QString &mimeType);
static QStringList atomToMimeTypes(xcb_atom_t atom);
- static QString atomName(xcb_atom_t atom);
static void sendSelectionNotify(xcb_selection_request_event_t *event, bool success);
// on selection owner changes by X clients (Xwl -> Wl)
diff -Nru kwin-6.3.5/src/xwayland/xwayland.cpp kwin-6.3.6/src/xwayland/xwayland.cpp
--- kwin-6.3.5/src/xwayland/xwayland.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/xwayland/xwayland.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -118,224 +118,97 @@
Qt::MetaModifier,
};
- static const QSet<quint32> characterKeys = {
- Qt::Key_Any,
- Qt::Key_Space,
- Qt::Key_Exclam,
- Qt::Key_QuoteDbl,
- Qt::Key_NumberSign,
- Qt::Key_Dollar,
- Qt::Key_Percent,
- Qt::Key_Ampersand,
- Qt::Key_Apostrophe,
- Qt::Key_ParenLeft,
- Qt::Key_ParenRight,
- Qt::Key_Asterisk,
- Qt::Key_Plus,
- Qt::Key_Comma,
- Qt::Key_Minus,
- Qt::Key_Period,
- Qt::Key_Slash,
- Qt::Key_0,
- Qt::Key_1,
- Qt::Key_2,
- Qt::Key_3,
- Qt::Key_4,
- Qt::Key_5,
- Qt::Key_6,
- Qt::Key_7,
- Qt::Key_8,
- Qt::Key_9,
- Qt::Key_Colon,
- Qt::Key_Semicolon,
- Qt::Key_Less,
- Qt::Key_Equal,
- Qt::Key_Greater,
- Qt::Key_Question,
- Qt::Key_At,
- Qt::Key_A,
- Qt::Key_B,
- Qt::Key_C,
- Qt::Key_D,
- Qt::Key_E,
- Qt::Key_F,
- Qt::Key_G,
- Qt::Key_H,
- Qt::Key_I,
- Qt::Key_J,
- Qt::Key_K,
- Qt::Key_L,
- Qt::Key_M,
- Qt::Key_N,
- Qt::Key_O,
- Qt::Key_P,
- Qt::Key_Q,
- Qt::Key_R,
- Qt::Key_S,
- Qt::Key_T,
- Qt::Key_U,
- Qt::Key_V,
- Qt::Key_W,
- Qt::Key_X,
- Qt::Key_Y,
- Qt::Key_Z,
- Qt::Key_BracketLeft,
- Qt::Key_Backslash,
- Qt::Key_BracketRight,
- Qt::Key_AsciiCircum,
- Qt::Key_Underscore,
- Qt::Key_QuoteLeft,
- Qt::Key_BraceLeft,
- Qt::Key_Bar,
- Qt::Key_BraceRight,
- Qt::Key_AsciiTilde,
- Qt::Key_nobreakspace,
- Qt::Key_exclamdown,
- Qt::Key_cent,
- Qt::Key_sterling,
- Qt::Key_currency,
- Qt::Key_yen,
- Qt::Key_brokenbar,
- Qt::Key_section,
- Qt::Key_diaeresis,
- Qt::Key_copyright,
- Qt::Key_ordfeminine,
- Qt::Key_guillemotleft,
- Qt::Key_notsign,
- Qt::Key_hyphen,
- Qt::Key_registered,
- Qt::Key_macron,
- Qt::Key_degree,
- Qt::Key_plusminus,
- Qt::Key_twosuperior,
- Qt::Key_threesuperior,
- Qt::Key_acute,
- Qt::Key_micro,
- Qt::Key_paragraph,
- Qt::Key_periodcentered,
- Qt::Key_cedilla,
- Qt::Key_onesuperior,
- Qt::Key_masculine,
- Qt::Key_guillemotright,
- Qt::Key_onequarter,
- Qt::Key_onehalf,
- Qt::Key_threequarters,
- Qt::Key_questiondown,
- Qt::Key_Agrave,
- Qt::Key_Aacute,
- Qt::Key_Acircumflex,
- Qt::Key_Atilde,
- Qt::Key_Adiaeresis,
- Qt::Key_Aring,
- Qt::Key_AE,
- Qt::Key_Ccedilla,
- Qt::Key_Egrave,
- Qt::Key_Eacute,
- Qt::Key_Ecircumflex,
- Qt::Key_Ediaeresis,
- Qt::Key_Igrave,
- Qt::Key_Iacute,
- Qt::Key_Icircumflex,
- Qt::Key_Idiaeresis,
- Qt::Key_ETH,
- Qt::Key_Ntilde,
- Qt::Key_Ograve,
- Qt::Key_Oacute,
- Qt::Key_Ocircumflex,
- Qt::Key_Otilde,
- Qt::Key_Odiaeresis,
- Qt::Key_multiply,
- Qt::Key_Ooblique,
- Qt::Key_Ugrave,
- Qt::Key_Uacute,
- Qt::Key_Ucircumflex,
- Qt::Key_Udiaeresis,
- Qt::Key_Yacute,
- Qt::Key_THORN,
- Qt::Key_ssharp,
- Qt::Key_division,
- Qt::Key_ydiaeresis,
- Qt::Key_Multi_key,
- Qt::Key_Codeinput,
- Qt::Key_SingleCandidate,
- Qt::Key_MultipleCandidate,
- Qt::Key_PreviousCandidate,
- Qt::Key_Mode_switch,
- Qt::Key_Kanji,
- Qt::Key_Muhenkan,
- Qt::Key_Henkan,
- Qt::Key_Romaji,
- Qt::Key_Hiragana,
- Qt::Key_Katakana,
- Qt::Key_Hiragana_Katakana,
- Qt::Key_Zenkaku,
- Qt::Key_Hankaku,
- Qt::Key_Zenkaku_Hankaku,
- Qt::Key_Touroku,
- Qt::Key_Massyo,
- Qt::Key_Kana_Lock,
- Qt::Key_Kana_Shift,
- Qt::Key_Eisu_Shift,
- Qt::Key_Eisu_toggle,
- Qt::Key_Hangul,
- Qt::Key_Hangul_Start,
- Qt::Key_Hangul_End,
- Qt::Key_Hangul_Hanja,
- Qt::Key_Hangul_Jamo,
- Qt::Key_Hangul_Romaja,
- Qt::Key_Hangul_Jeonja,
- Qt::Key_Hangul_Banja,
- Qt::Key_Hangul_PreHanja,
- Qt::Key_Hangul_PostHanja,
- Qt::Key_Hangul_Special,
- Qt::Key_Dead_Grave,
- Qt::Key_Dead_Acute,
- Qt::Key_Dead_Circumflex,
- Qt::Key_Dead_Tilde,
- Qt::Key_Dead_Macron,
- Qt::Key_Dead_Breve,
- Qt::Key_Dead_Abovedot,
- Qt::Key_Dead_Diaeresis,
- Qt::Key_Dead_Abovering,
- Qt::Key_Dead_Doubleacute,
- Qt::Key_Dead_Caron,
- Qt::Key_Dead_Cedilla,
- Qt::Key_Dead_Ogonek,
- Qt::Key_Dead_Iota,
- Qt::Key_Dead_Voiced_Sound,
- Qt::Key_Dead_Semivoiced_Sound,
- Qt::Key_Dead_Belowdot,
- Qt::Key_Dead_Hook,
- Qt::Key_Dead_Horn,
- Qt::Key_Dead_Stroke,
- Qt::Key_Dead_Abovecomma,
- Qt::Key_Dead_Abovereversedcomma,
- Qt::Key_Dead_Doublegrave,
- Qt::Key_Dead_Belowring,
- Qt::Key_Dead_Belowmacron,
- Qt::Key_Dead_Belowcircumflex,
- Qt::Key_Dead_Belowtilde,
- Qt::Key_Dead_Belowbreve,
- Qt::Key_Dead_Belowdiaeresis,
- Qt::Key_Dead_Invertedbreve,
- Qt::Key_Dead_Belowcomma,
- Qt::Key_Dead_Currency,
- Qt::Key_Dead_a,
- Qt::Key_Dead_A,
- Qt::Key_Dead_e,
- Qt::Key_Dead_E,
- Qt::Key_Dead_i,
- Qt::Key_Dead_I,
- Qt::Key_Dead_o,
- Qt::Key_Dead_O,
- Qt::Key_Dead_u,
- Qt::Key_Dead_U,
- Qt::Key_Dead_Small_Schwa,
- Qt::Key_Dead_Capital_Schwa,
- Qt::Key_Dead_Greek,
- Qt::Key_Dead_Lowline,
- Qt::Key_Dead_Aboveverticalline,
- Qt::Key_Dead_Belowverticalline,
+ static const auto isSpecialKey = [](quint32 key) {
+ // All keys prior to 0x01000000 (Qt::Key_Escape) are considered as character keys.
+ if (key < 0x01000000) {
+ return false;
+ }
+
+ static const QSet<quint32> excludedKeys = {
+ Qt::Key_Multi_key,
+ Qt::Key_Codeinput,
+ Qt::Key_SingleCandidate,
+ Qt::Key_MultipleCandidate,
+ Qt::Key_PreviousCandidate,
+ Qt::Key_Mode_switch,
+ Qt::Key_Kanji,
+ Qt::Key_Muhenkan,
+ Qt::Key_Henkan,
+ Qt::Key_Romaji,
+ Qt::Key_Hiragana,
+ Qt::Key_Katakana,
+ Qt::Key_Hiragana_Katakana,
+ Qt::Key_Zenkaku,
+ Qt::Key_Hankaku,
+ Qt::Key_Zenkaku_Hankaku,
+ Qt::Key_Touroku,
+ Qt::Key_Massyo,
+ Qt::Key_Kana_Lock,
+ Qt::Key_Kana_Shift,
+ Qt::Key_Eisu_Shift,
+ Qt::Key_Eisu_toggle,
+ Qt::Key_Hangul,
+ Qt::Key_Hangul_Start,
+ Qt::Key_Hangul_End,
+ Qt::Key_Hangul_Hanja,
+ Qt::Key_Hangul_Jamo,
+ Qt::Key_Hangul_Romaja,
+ Qt::Key_Hangul_Jeonja,
+ Qt::Key_Hangul_Banja,
+ Qt::Key_Hangul_PreHanja,
+ Qt::Key_Hangul_PostHanja,
+ Qt::Key_Hangul_Special,
+ Qt::Key_Dead_Grave,
+ Qt::Key_Dead_Acute,
+ Qt::Key_Dead_Circumflex,
+ Qt::Key_Dead_Tilde,
+ Qt::Key_Dead_Macron,
+ Qt::Key_Dead_Breve,
+ Qt::Key_Dead_Abovedot,
+ Qt::Key_Dead_Diaeresis,
+ Qt::Key_Dead_Abovering,
+ Qt::Key_Dead_Doubleacute,
+ Qt::Key_Dead_Caron,
+ Qt::Key_Dead_Cedilla,
+ Qt::Key_Dead_Ogonek,
+ Qt::Key_Dead_Iota,
+ Qt::Key_Dead_Voiced_Sound,
+ Qt::Key_Dead_Semivoiced_Sound,
+ Qt::Key_Dead_Belowdot,
+ Qt::Key_Dead_Hook,
+ Qt::Key_Dead_Horn,
+ Qt::Key_Dead_Stroke,
+ Qt::Key_Dead_Abovecomma,
+ Qt::Key_Dead_Abovereversedcomma,
+ Qt::Key_Dead_Doublegrave,
+ Qt::Key_Dead_Belowring,
+ Qt::Key_Dead_Belowmacron,
+ Qt::Key_Dead_Belowcircumflex,
+ Qt::Key_Dead_Belowtilde,
+ Qt::Key_Dead_Belowbreve,
+ Qt::Key_Dead_Belowdiaeresis,
+ Qt::Key_Dead_Invertedbreve,
+ Qt::Key_Dead_Belowcomma,
+ Qt::Key_Dead_Currency,
+ Qt::Key_Dead_a,
+ Qt::Key_Dead_A,
+ Qt::Key_Dead_e,
+ Qt::Key_Dead_E,
+ Qt::Key_Dead_i,
+ Qt::Key_Dead_I,
+ Qt::Key_Dead_o,
+ Qt::Key_Dead_O,
+ Qt::Key_Dead_u,
+ Qt::Key_Dead_U,
+ Qt::Key_Dead_Small_Schwa,
+ Qt::Key_Dead_Capital_Schwa,
+ Qt::Key_Dead_Greek,
+ Qt::Key_Dead_Lowline,
+ Qt::Key_Dead_Aboveverticalline,
+ Qt::Key_Dead_Belowverticalline,
+ };
+
+ return !excludedKeys.contains(key);
};
switch (mode) {
@@ -345,13 +218,13 @@
break;
case NonCharacterKeys:
m_filterKey = [](int key, Qt::KeyboardModifiers) {
- return !characterKeys.contains(key);
+ return isSpecialKey(key);
};
m_filterMouse = eavesdropsMouse;
break;
case AllKeysWithModifier:
m_filterKey = [](int key, Qt::KeyboardModifiers m) {
- return m.testAnyFlags(modifierKeys) || !characterKeys.contains(key);
+ return m.testAnyFlags(modifierKeys) || isSpecialKey(key);
};
m_filterMouse = eavesdropsMouse;
break;
@@ -434,6 +307,11 @@
if (!m_filterMouse) {
return false;
}
+ if (event->button == Qt::MouseButton::LeftButton
+ || event->button == Qt::MouseButton::RightButton
+ || event->button == Qt::MouseButton::MiddleButton) {
+ return false;
+ }
auto pointer = waylandServer()->seat()->pointer();
auto surface = pointer->focusedSurface();
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/CMakeLists.txt kwin-6.3.6/CMakeLists.txt
--- kwin-6.3.5/CMakeLists.txt 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/CMakeLists.txt 2025-07-20 16:38:09.000000000 +0200
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
-set(PROJECT_VERSION "6.3.5") # Handled by release scripts
+set(PROJECT_VERSION "6.3.6") # Handled by release scripts
project(KWin VERSION ${PROJECT_VERSION})
set(CMAKE_C_STANDARD 99)
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/debian/changelog kwin-6.3.6/debian/changelog
--- kwin-6.3.5/debian/changelog 2025-05-20 08:34:30.000000000 +0200
+++ kwin-6.3.6/debian/changelog 2025-07-18 11:21:52.000000000 +0200
@@ -1,3 +1,78 @@
+kwin (4:6.3.6-1) unstable; urgency=medium
+
+ [ Aurélien COUDERC ]
+ * New upstream bugfix release (6.3.6).
+ - Backends/drm: never use DEGAMMA_LUT. (kde#505869)
+ - Workspace: Fix window activation on activity change. (kde#501393)
+ - Don't add deleted windows to the stacking order.
+ - Xwayland: don't forward left/middle/right mouse buttons to Xwayland.
+ (kcde#490057)
+ - Output management: add some safe guards for invalid brightness
+ overrides. (kde#506090)
+ - Wayland/tablet_v2: fix the tablet cursor hotspot with Xwayland scaling.
+ - Core/renderloop: fix subsurfaces vrr scheduling.
+ - Plugins/slideback: Scale smoothness proportionally to adjusted strength
+ from 6.3. (kde#503964)
+ - Plugins/systembell: Throttle visual bell.
+ - Backends/drm: disable direct scanout on secondary GPUs.
+ - Scene/surfaceitem_wayland: handle some missing initial properties.
+ - Plugins/translucency: Fix unsetting animations for minimized windows.
+ (kde#504687)
+ - Autotests/integration/outputchanges: wait for frame callbacks before committing.
+ - Autotests/outputchanges: add a test for evacuating windows from removed outputs.
+ - Scene/workspacescene: restrict frame callbacks based on the output
+ rather than geometry. (kde#479694, kde#498628, kde#505060)
+ - Backends/drm: reduce severity of pageflip failure logging. (kde#505028)
+ - Xwayland: Fix leaking normal key presses with keyboard layouts other
+ than English. (kde#500032)
+ - Backends/drm: Add missing null guard.
+ - Outputconfigurationstore: disable adaptive sync by default.
+ - Map xinerama index to Output by output name.
+ - Backends/drm: clear the test buffer with legacy modesetting.
+ (kde#504258)
+ - Wayland/xdgoutput: round the scaled output position.
+ - Don't leak lcms tone curves.
+ - Plugins/colorpicker: round the result. (kde#491633)
+ - Plugins/colorpicker: use BPC when converting to sRGB. (kde#491633)
+ - X11: Add an environment variable to disable _NET_WM_SYNC_REQUEST in X11Window.
+ - Wayland/colormanagement: compare primaries with the protocol's resolution.
+ - Wayland/colormanagement: also around max_fall and max_cll.
+ - Wayland/colormanagement: fix sending target luminance levels.
+ - Core/iccprofile: also estimate black point even if there's no luminance tag.
+ - Backends/drm: also guard DrmOutput::cursorLayer for nullptr pipeline.
+ (kde#504516)
+ * Drop backported patches now part of the upstreame release.
+ * Refresh patches.
+ * Backport upstream commits:
+ - Fix inactive inner window action "Activate, raise and pass click" not
+ raising the window. (kde#501457)
+ - Fix incorrect window sometimes receiving clicks when slideback effect in
+ use. [572fee09] (kde#455429)
+ - wayland: Send wl_data_source.cancelled if wl_data_device.start_drag is
+ rejected similar to what weston does. [48ddfb59]
+ - backends/drm: also handle GPU resets on secondary GPUs. [9580193a]
+ - Fix night light not tinting properly on wayland. [98b64e9b] (kde#505495)
+ - Implement org.freedesktop.a11y.KeyboardMonitor interface which allows
+ screen readers to intercept keys for their own shortcut handling.
+ [252c74c0, cfc05b0e, 5ba6279b, 889ffed2]
+ - Fix an issue where windows may be placed at a non-0,0 coordinate after a
+ maximize on fractionally scaled displays if the window was previously
+ resized from the top edge, potentially allowing mouse clicks on the top of
+ the display to reach windows underneath the maximized window. [070f5ba6]
+ - Fix for multiples instances of screens waking up just after going to
+ sleep. [8f331a26] (kde#493879, kde#506135, kde#505953)
+ - Fix some wayland clients crashing in some race conditions when
+ attempting to create an xdg_surface while mapping a window. [225e7360]
+ (kde#506412)
+ - backends/drm: Fix memory leak in DrmGpu::createNonMasterFd. [e4e144db]
+ - wayland: Fix resizing with fractional increments. [e8541cf2]
+ - wayland: close popups upon window activation, fixing GTK4 popups
+ preventing the kicker launcher getting focus. [51c0880f] (kde#497075)
+ - Fix panel not registering clicks when window behavior set to "activate
+ and raise", and a tooltip is visible. [267fa4ec] (kde#461414)
+
+ -- Aurélien COUDERC <coucouf@debian.org> Fri, 18 Jul 2025 11:21:52 +0200
+
kwin (4:6.3.5-1) unstable; urgency=medium
[ Aurélien COUDERC ]
Seulement dans kwin-6.3.6/src: a11ykeyboardmonitor.cpp
Seulement dans kwin-6.3.6/src: a11ykeyboardmonitor.h
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_backend.cpp kwin-6.3.6/src/backends/drm/drm_backend.cpp
--- kwin-6.3.5/src/backends/drm/drm_backend.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_backend.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -234,12 +234,28 @@
return gpu;
}
+static QString earlyIdentifier(Output *output)
+{
+ // We can't use the output's UUID because that's only set later, by the output config system.
+ // This doesn't need to be perfectly accurate though, sometimes getting a false positive is ok,
+ // so this just uses EDID ID, EDID hash or connector name, whichever is available
+ if (output->edid().isValid()) {
+ if (!output->edid().identifier().isEmpty()) {
+ return output->edid().identifier();
+ } else {
+ return output->edid().hash();
+ }
+ } else {
+ return output->name();
+ }
+}
+
void DrmBackend::addOutput(DrmAbstractOutput *o)
{
const bool allOff = std::ranges::all_of(m_outputs, [](Output *output) {
return output->dpmsMode() != Output::DpmsMode::On;
});
- if (allOff && m_recentlyUnpluggedDpmsOffOutputs.contains(o->uuid())) {
+ if (allOff && m_recentlyUnpluggedDpmsOffOutputs.contains(earlyIdentifier(o))) {
if (DrmOutput *drmOutput = qobject_cast<DrmOutput *>(o)) {
// When the system is in dpms power saving mode, KWin turns on all outputs if the user plugs a new output in
// as that's an intentional action and they expect to see the output light up.
@@ -248,7 +264,7 @@
drmOutput->updateDpmsMode(Output::DpmsMode::Off);
drmOutput->pipeline()->setActive(false);
drmOutput->renderLoop()->inhibit();
- m_recentlyUnpluggedDpmsOffOutputs.removeOne(drmOutput->uuid());
+ m_recentlyUnpluggedDpmsOffOutputs.removeOne(earlyIdentifier(drmOutput));
}
}
m_outputs.append(o);
@@ -268,7 +284,7 @@
void DrmBackend::removeOutput(DrmAbstractOutput *o)
{
if (o->dpmsMode() == Output::DpmsMode::Off) {
- const QUuid id = o->uuid();
+ const QString id = earlyIdentifier(o);
m_recentlyUnpluggedDpmsOffOutputs.push_back(id);
QTimer::singleShot(s_dpmsTimeout, this, [this, id]() {
m_recentlyUnpluggedDpmsOffOutputs.removeOne(id);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_backend.h kwin-6.3.6/src/backends/drm/drm_backend.h
--- kwin-6.3.5/src/backends/drm/drm_backend.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_backend.h 2025-07-20 16:38:09.000000000 +0200
@@ -87,7 +87,7 @@
std::unique_ptr<QSocketNotifier> m_socketNotifier;
Session *m_session;
QList<DrmAbstractOutput *> m_outputs;
- QList<QUuid> m_recentlyUnpluggedDpmsOffOutputs;
+ QList<QString> m_recentlyUnpluggedDpmsOffOutputs;
const QStringList m_explicitGpus;
std::vector<std::unique_ptr<DrmGpu>> m_gpus;
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_crtc.cpp kwin-6.3.6/src/backends/drm/drm_crtc.cpp
--- kwin-6.3.5/src/backends/drm/drm_crtc.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_crtc.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -64,10 +64,9 @@
m_postBlendingColorOps.push_back(std::make_unique<LegacyMatrixColorOp>(next, &ctm));
next = m_postBlendingColorOps.back().get();
}
- if (!gpu()->isNVidia() && !gpu()->isI915() && degammaLut.isValid() && degammaLutSize.isValid() && degammaLutSize.value() > 0) {
- m_postBlendingColorOps.push_back(std::make_unique<DrmLutColorOp>(next, °ammaLut, degammaLutSize.value()));
- next = m_postBlendingColorOps.back().get();
- }
+ // DEGAMMA_LUT is intentionally not part of the post blending pipeline
+ // as on most hardware it actually maps to pre-blending operations,
+ // and more importantly it's buggy on Intel, AMD and NVidia...
postBlendingPipeline = next;
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_egl_backend.cpp kwin-6.3.6/src/backends/drm/drm_egl_backend.cpp
--- kwin-6.3.5/src/backends/drm/drm_egl_backend.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_egl_backend.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -132,6 +132,11 @@
return ret;
}
+void EglGbmBackend::resetContextForGpu(DrmGpu *gpu)
+{
+ m_contexts.erase(gpu->eglDisplay());
+}
+
std::unique_ptr<SurfaceTexture> EglGbmBackend::createSurfaceTextureWayland(SurfacePixmap *pixmap)
{
return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_egl_backend.h kwin-6.3.6/src/backends/drm/drm_egl_backend.h
--- kwin-6.3.5/src/backends/drm/drm_egl_backend.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_egl_backend.h 2025-07-20 16:38:09.000000000 +0200
@@ -61,6 +61,7 @@
EglDisplay *displayForGpu(DrmGpu *gpu);
std::shared_ptr<EglContext> contextForGpu(DrmGpu *gpu);
+ void resetContextForGpu(DrmGpu *gpu);
private:
bool initializeEgl();
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_egl_layer.cpp kwin-6.3.6/src/backends/drm/drm_egl_layer.cpp
--- kwin-6.3.5/src/backends/drm/drm_egl_layer.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_egl_layer.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -106,6 +106,16 @@
// the hardware to some buffer format we can't switch away from
return false;
}
+ if (m_pipeline->gpu() != m_pipeline->gpu()->platform()->primaryGpu()) {
+ // Disable direct scanout between GPUs, as
+ // - there are some significant driver bugs with direct scanout from other GPUs,
+ // like https://gitlab.freedesktop.org/drm/amd/-/issues/2075
+ // - with implicit modifiers, direct scanout on secondary GPUs
+ // is also very unlikely to yield the correct results.
+ // TODO once we know what buffer a GPU is meant for, loosen this check again
+ // Right now this just assumes all buffers are on the primary GPU
+ return false;
+ }
if (m_pipeline->output()->colorProfileSource() == Output::ColorProfileSource::ICC && !m_pipeline->output()->highDynamicRange() && m_pipeline->iccProfile()) {
// TODO make the icc profile output a color pipeline too?
return false;
@@ -130,10 +140,6 @@
if (offloadTransform() != OutputTransform::Kind::Normal && (!plane || !plane->supportsTransformation(offloadTransform()))) {
return false;
}
- // importing a buffer from another GPU without an explicit modifier can mess up the buffer format
- if (buffer->dmabufAttributes()->modifier == DRM_FORMAT_MOD_INVALID && m_pipeline->gpu()->platform()->gpuCount() > 1) {
- return false;
- }
m_scanoutBuffer = m_pipeline->gpu()->importBuffer(buffer, FileDescriptor{});
if (m_scanoutBuffer) {
m_surface.forgetDamage(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal. It's more flexible and less error-prone
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_egl_layer_surface.cpp kwin-6.3.6/src/backends/drm/drm_egl_layer_surface.cpp
--- kwin-6.3.5/src/backends/drm/drm_egl_layer_surface.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_egl_layer_surface.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -408,7 +408,7 @@
bool EglGbmLayerSurface::doesSurfaceFit(Surface *surface, const QSize &size, const QHash<uint32_t, QList<uint64_t>> &formats, Output::ColorPowerTradeoff tradeoff) const
{
- if (!surface || !surface->gbmSwapchain || surface->gbmSwapchain->size() != size) {
+ if (!surface || surface->needsRecreation || !surface->gbmSwapchain || surface->gbmSwapchain->size() != size) {
return false;
}
if (surface->tradeoff != tradeoff) {
@@ -635,6 +635,19 @@
}
if (!surface->importContext->makeCurrent()) {
+ qCWarning(KWIN_DRM, "Failed to make import context current");
+ // this is probably caused by a GPU reset, let's not take any chances
+ surface->needsRecreation = true;
+ m_eglBackend->resetContextForGpu(m_gpu);
+ return nullptr;
+ }
+ const auto restoreContext = qScopeGuard([this]() {
+ m_eglBackend->makeCurrent();
+ });
+ if (surface->importContext->checkGraphicsResetStatus() != GL_NO_ERROR) {
+ qCWarning(KWIN_DRM, "Detected GPU reset on secondary GPU %s", qPrintable(m_gpu->drmDevice()->path()));
+ surface->needsRecreation = true;
+ m_eglBackend->resetContextForGpu(m_gpu);
return nullptr;
}
std::unique_ptr<GLRenderTimeQuery> renderTime;
@@ -703,8 +716,6 @@
frame->addRenderTimeQuery(std::move(renderTime));
}
- // restore the old context
- m_eglBackend->makeCurrent();
return m_gpu->importBuffer(slot->buffer(), endFence.takeFileDescriptor());
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_egl_layer_surface.h kwin-6.3.6/src/backends/drm/drm_egl_layer_surface.h
--- kwin-6.3.5/src/backends/drm/drm_egl_layer_surface.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_egl_layer_surface.h 2025-07-20 16:38:09.000000000 +0200
@@ -80,6 +80,8 @@
{
~Surface();
+ bool needsRecreation = false;
+
std::shared_ptr<EglContext> context;
std::shared_ptr<EglSwapchain> gbmSwapchain;
std::shared_ptr<EglSwapchainSlot> currentSlot;
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_gpu.cpp kwin-6.3.6/src/backends/drm/drm_gpu.cpp
--- kwin-6.3.5/src/backends/drm/drm_gpu.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_gpu.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -121,6 +121,7 @@
{
char *path = drmGetDeviceNameFromFd2(m_fd);
FileDescriptor fd{open(path, O_RDWR | O_CLOEXEC)};
+ free(path);
if (!fd.isValid()) {
qCWarning(KWIN_DRM) << "Could not open DRM fd for leasing!" << strerror(errno);
} else {
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_output.cpp kwin-6.3.6/src/backends/drm/drm_output.cpp
--- kwin-6.3.5/src/backends/drm/drm_output.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_output.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -404,9 +404,9 @@
QVector3D adaptedChannelFactors = ColorDescription::sRGB.containerColorimetry().relativeColorimetricTo(originalColor.containerColorimetry()) * sRGBchannelFactors;
// ensure none of the values reach zero, otherwise the white point might end up on or outside
// the edges of the gamut, which leads to terrible glitches
- adaptedChannelFactors.setX(std::max(adaptedChannelFactors.x(), 0.01f));
- adaptedChannelFactors.setY(std::max(adaptedChannelFactors.y(), 0.01f));
- adaptedChannelFactors.setZ(std::max(adaptedChannelFactors.z(), 0.01f));
+ adaptedChannelFactors.setX(std::max(adaptedChannelFactors.x(), 0.0001f));
+ adaptedChannelFactors.setY(std::max(adaptedChannelFactors.y(), 0.0001f));
+ adaptedChannelFactors.setZ(std::max(adaptedChannelFactors.z(), 0.0001f));
return adaptedChannelFactors;
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/backends/drm/drm_pipeline.cpp kwin-6.3.6/src/backends/drm/drm_pipeline.cpp
--- kwin-6.3.5/src/backends/drm/drm_pipeline.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/backends/drm/drm_pipeline.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -326,6 +326,9 @@
commit->addProperty(m_pending.crtc->active, 1);
commit->addBlob(m_pending.crtc->modeId, m_pending.mode->blob());
+ if (m_pending.crtc->degammaLut.isValid()) {
+ commit->addProperty(m_pending.crtc->degammaLut, 0);
+ }
const auto primary = m_pending.crtc->primaryPlane();
commit->addProperty(primary->crtcId, m_pending.crtc->id());
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/CMakeLists.txt kwin-6.3.6/src/CMakeLists.txt
--- kwin-6.3.5/src/CMakeLists.txt 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/CMakeLists.txt 2025-07-20 16:38:09.000000000 +0200
@@ -35,6 +35,7 @@
target_sources(kwin PRIVATE
3rdparty/xcursor.c
+ a11ykeyboardmonitor.cpp
activation.cpp
appmenu.cpp
client_machine.cpp
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/core/renderloop.cpp kwin-6.3.6/src/core/renderloop.cpp
--- kwin-6.3.5/src/core/renderloop.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/core/renderloop.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -264,8 +264,8 @@
const bool vrr = d->presentationMode == PresentationMode::AdaptiveSync || d->presentationMode == PresentationMode::AdaptiveAsync;
const bool tearing = d->presentationMode == PresentationMode::Async || d->presentationMode == PresentationMode::AdaptiveAsync;
if ((vrr || tearing) && workspace() && workspace()->activeWindow() && d->output) {
- Window *const activeWindow = workspace()->activeWindow();
- if ((item || layer || outputLayer) && activeWindowControlsVrrRefreshRate() && item != activeWindow->surfaceItem()) {
+ SurfaceItem *const surfaceItem = workspace()->activeWindow()->surfaceItem();
+ if ((item || layer || outputLayer) && activeWindowControlsVrrRefreshRate() && item != surfaceItem && !surfaceItem->isAncestorOf(item)) {
d->delayedVrrTimer.start();
return;
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/kcms/options/kwinoptions_settings.kcfg kwin-6.3.6/src/kcms/options/kwinoptions_settings.kcfg
--- kwin-6.3.5/src/kcms/options/kwinoptions_settings.kcfg 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/kcms/options/kwinoptions_settings.kcfg 2025-07-20 16:38:09.000000000 +0200
@@ -201,9 +201,8 @@
</entry>
<entry key="CommandInactiveTitlebar1" type="Enum">
- <default>ActivateRaiseOnReleaseAndPassClick</default>
+ <default>ActivateAndRaise</default>
<choices>
- <choice name="ActivateRaiseOnReleaseAndPassClick" value="Activate, pass click and raise on release"></choice>
<choice name="ActivateAndRaise" value="Activate and raise"></choice>
<choice name="ActivateAndLower" value="Activate and lower"></choice>
<choice name="Activate"></choice>
@@ -253,8 +252,9 @@
</entry>
<entry key="CommandWindow1" type="Enum">
- <default>ActivateRaisePassClick</default>
+ <default>ActivateRaiseOnReleaseAndPassClick</default>
<choices>
+ <choice name="ActivateRaiseOnReleaseAndPassClick" value="Activate, pass click and raise on release"></choice>
<choice name="ActivateRaisePassClick" value="Activate, raise and pass click"></choice>
<choice name="ActivatePassClick" value="Activate and pass click"></choice>
<choice name="Activate"></choice>
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/keyboard_input.cpp kwin-6.3.6/src/keyboard_input.cpp
--- kwin-6.3.5/src/keyboard_input.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/keyboard_input.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -273,6 +273,11 @@
return;
}
+ const bool ret = m_a11yKeyboardMonitor.processKey(key, state, time);
+ if (ret) {
+ return;
+ }
+
if (state == KeyboardKeyState::Pressed) {
if (!m_pressedKeys.contains(key)) {
m_pressedKeys.append(key);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/keyboard_input.h kwin-6.3.6/src/keyboard_input.h
--- kwin-6.3.5/src/keyboard_input.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/keyboard_input.h 2025-07-20 16:38:09.000000000 +0200
@@ -8,6 +8,7 @@
*/
#pragma once
+#include "a11ykeyboardmonitor.h"
#include "input.h"
#include <QObject>
@@ -85,6 +86,7 @@
KeyboardLayout *m_keyboardLayout = nullptr;
QList<uint32_t> m_pressedKeys;
QList<uint32_t> m_filteredKeys;
+ A11yKeyboardMonitor m_a11yKeyboardMonitor;
};
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/kwin.kcfg kwin-6.3.6/src/kwin.kcfg
--- kwin-6.3.5/src/kwin.kcfg 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/kwin.kcfg 2025-07-20 16:38:09.000000000 +0200
@@ -25,7 +25,7 @@
<default>Operations menu</default>
</entry>
<entry name="CommandInactiveTitlebar1" type="String">
- <default>Activate, pass click and raise on release</default>
+ <default>Activate and raise</default>
</entry>
<entry name="CommandInactiveTitlebar2" type="String">
<default>Nothing</default>
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/layers.cpp kwin-6.3.6/src/layers.cpp
--- kwin-6.3.5/src/layers.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/layers.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -210,7 +210,7 @@
cl.clear();
for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
X11Window *window = qobject_cast<X11Window *>(*it);
- if (window && !window->isUnmanaged()) {
+ if (window && !window->isDeleted() && !window->isUnmanaged()) {
cl.push_back(window->window());
}
}
Seulement dans kwin-6.3.6/src: org.freedesktop.a11y.xml
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/outputconfigurationstore.cpp kwin-6.3.6/src/outputconfigurationstore.cpp
--- kwin-6.3.5/src/outputconfigurationstore.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/outputconfigurationstore.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -825,9 +825,17 @@
}
if (const auto it = data.find("maxPeakBrightnessOverride"); it != data.end() && it->isDouble()) {
state.maxPeakBrightnessOverride = it->toDouble();
+ if (*state.maxPeakBrightnessOverride < 50) {
+ // clearly nonsense
+ state.maxPeakBrightnessOverride.reset();
+ }
}
if (const auto it = data.find("maxAverageBrightnessOverride"); it != data.end() && it->isDouble()) {
state.maxAverageBrightnessOverride = it->toDouble();
+ if (*state.maxAverageBrightnessOverride < 50) {
+ // clearly nonsense
+ state.maxAverageBrightnessOverride.reset();
+ }
}
if (const auto it = data.find("minBrightnessOverride"); it != data.end() && it->isDouble()) {
state.minBrightnessOverride = it->toDouble();
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/plugins/slideback/motionmanager.cpp kwin-6.3.6/src/plugins/slideback/motionmanager.cpp
--- kwin-6.3.5/src/plugins/slideback/motionmanager.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/slideback/motionmanager.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -69,11 +69,11 @@
}
double strength = 0.12;
- double smoothness = 4.0;
+ double smoothness = 2.5;
if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) {
// If the factor is == 0 then we just skip the calculation completely
strength = 0.12 / effects->animationTimeFactor();
- smoothness = effects->animationTimeFactor() * 4.0;
+ smoothness = effects->animationTimeFactor() * 2.5;
}
WindowMotion &motion = m_managedWindows[w];
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/plugins/slideback/slideback.cpp kwin-6.3.6/src/plugins/slideback/slideback.cpp
--- kwin-6.3.5/src/plugins/slideback/slideback.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/slideback/slideback.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -67,7 +67,8 @@
currentFound = true;
}
} else {
- if (isWindowUsable(tmp) && tmp->isOnCurrentDesktop() && w->isOnCurrentDesktop()) {
+ if (isWindowUsable(tmp) && tmp->isOnCurrentDesktop() && w->isOnCurrentDesktop()
+ && tmp->isOnCurrentActivity() && w->isOnCurrentActivity()) {
// Do we have to move it?
if (intersects(w, tmp->frameGeometry().toRect())) {
QRect slideRect;
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/plugins/systembell/systembell.cpp kwin-6.3.6/src/plugins/systembell/systembell.cpp
--- kwin-6.3.5/src/plugins/systembell/systembell.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/systembell/systembell.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -106,6 +106,13 @@
m_audioThrottleTimer.setInterval(100ms);
m_audioThrottleTimer.setSingleShot(true);
+
+ // The Web Content Accessibility Guidelines (WCAG) recommend that any
+ // element that flashes in the screen must have a maximum period of
+ // 3Hz to avoid the risk of Photosensitivity Seizures.
+ // 3Hz is 333ms, double that to account for the window un-inverting, and round up
+ m_visualThrottleTimer.setInterval(700ms);
+ m_visualThrottleTimer.setSingleShot(true);
}
SystemBellEffect::~SystemBellEffect()
@@ -192,6 +199,12 @@
if (m_visibleBell) {
m_allWindows = true;
+ if (m_visualThrottleTimer.isActive()) {
+ return;
+ }
+
+ m_visualThrottleTimer.start();
+
const auto windows = effects->stackingOrder();
for (EffectWindow *window : windows) {
flash(window);
@@ -226,6 +239,12 @@
}
if (m_visibleBell) {
+ if (m_visualThrottleTimer.isActive()) {
+ return;
+ }
+
+ m_visualThrottleTimer.start();
+
m_windows.append(window);
flash(window);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/plugins/systembell/systembell.h kwin-6.3.6/src/plugins/systembell/systembell.h
--- kwin-6.3.5/src/plugins/systembell/systembell.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/systembell/systembell.h 2025-07-08 13:45:08.000000000 +0200
@@ -80,6 +80,7 @@
static QTimer *s_systemBellRemoveTimer;
QTimer m_audioThrottleTimer;
+ QTimer m_visualThrottleTimer;
static XdgSystemBellV1Interface *s_systemBell;
};
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/plugins/translucency/package/contents/code/main.js kwin-6.3.6/src/plugins/translucency/package/contents/code/main.js
--- kwin-6.3.5/src/plugins/translucency/package/contents/code/main.js 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/plugins/translucency/package/contents/code/main.js 2025-07-08 13:45:08.000000000 +0200
@@ -212,7 +212,7 @@
window.minimizedChanged.connect(() => {
if (window.minimized) {
- translucencyEffect.cancelAnimations();
+ translucencyEffect.cancelAnimations(window);
} else {
translucencyEffect.startAnimation(window);
translucencyEffect.inactive.animate(window);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/popup_input_filter.cpp kwin-6.3.6/src/popup_input_filter.cpp
--- kwin-6.3.5/src/popup_input_filter.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/popup_input_filter.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -24,6 +24,7 @@
, InputEventFilter(InputFilterOrder::Popup)
{
connect(workspace(), &Workspace::windowAdded, this, &PopupInputFilter::handleWindowAdded);
+ connect(workspace(), &Workspace::windowActivated, this, &PopupInputFilter::handleWindowFocusChanged);
}
void PopupInputFilter::handleWindowAdded(Window *window)
@@ -48,6 +49,12 @@
}
}
+void PopupInputFilter::handleWindowFocusChanged()
+{
+ // user focussed a window through another mechanism such as a shortcut
+ cancelPopups();
+}
+
bool PopupInputFilter::pointerButton(PointerButtonEvent *event)
{
if (m_popupWindows.isEmpty()) {
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/popup_input_filter.h kwin-6.3.6/src/popup_input_filter.h
--- kwin-6.3.5/src/popup_input_filter.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/popup_input_filter.h 2025-07-20 16:38:09.000000000 +0200
@@ -27,7 +27,7 @@
private:
void handleWindowAdded(Window *client);
-
+ void handleWindowFocusChanged();
void focus(Window *popup);
void cancelPopups();
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/scene/item.cpp kwin-6.3.6/src/scene/item.cpp
--- kwin-6.3.5/src/scene/item.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/item.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -631,6 +631,13 @@
m_effectCount--;
}
+bool Item::isAncestorOf(const Item *item) const
+{
+ return std::ranges::any_of(m_childItems, [item](const Item *child) {
+ return child == item || child->isAncestorOf(item);
+ });
+}
+
} // namespace KWin
#include "moc_item.cpp"
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/scene/item.h kwin-6.3.6/src/scene/item.h
--- kwin-6.3.5/src/scene/item.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/item.h 2025-07-08 13:45:08.000000000 +0200
@@ -142,6 +142,8 @@
void addEffect();
void removeEffect();
+ bool isAncestorOf(const Item *item) const;
+
Q_SIGNALS:
void childAdded(Item *item);
/**
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/scene/surfaceitem.cpp kwin-6.3.6/src/scene/surfaceitem.cpp
--- kwin-6.3.5/src/scene/surfaceitem.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/surfaceitem.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -284,7 +284,7 @@
std::chrono::nanoseconds ret = frameTimeEstimation();
const auto children = childItems();
for (Item *child : children) {
- ret = std::max(ret, static_cast<SurfaceItem *>(child)->frameTimeEstimation());
+ ret = std::min(ret, static_cast<SurfaceItem *>(child)->frameTimeEstimation());
}
return ret;
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/scene/surfaceitem_wayland.cpp kwin-6.3.6/src/scene/surfaceitem_wayland.cpp
--- kwin-6.3.5/src/scene/surfaceitem_wayland.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/surfaceitem_wayland.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -67,6 +67,8 @@
setBufferSourceBox(surface->bufferSourceBox());
setBufferSize(surface->bufferSize());
setColorDescription(surface->colorDescription());
+ setRenderingIntent(surface->renderingIntent());
+ setPresentationHint(surface->presentationModeHint());
setOpacity(surface->alphaMultiplier());
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/scene/workspacescene.cpp kwin-6.3.6/src/scene/workspacescene.cpp
--- kwin-6.3.5/src/scene/workspacescene.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/scene/workspacescene.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -269,7 +269,7 @@
continue;
}
Window *window = static_cast<WindowItem *>(item)->window();
- if (!window->isOnOutput(output)) {
+ if (window->moveResizeOutput() != output) {
continue;
}
if (auto surface = window->surface()) {
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/wayland/datadevice.cpp kwin-6.3.6/src/wayland/datadevice.cpp
--- kwin-6.3.5/src/wayland/datadevice.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/wayland/datadevice.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -96,6 +96,9 @@
const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->isSurfaceTouched(focusSurface);
if (!touchGrab) {
// Client neither has pointer nor touch grab. No drag start allowed.
+ if (dataSource) {
+ dataSource->dndCancelled();
+ }
return;
}
}
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/wayland/layershell_v1.cpp kwin-6.3.6/src/wayland/layershell_v1.cpp
--- kwin-6.3.5/src/wayland/layershell_v1.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/layershell_v1.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -111,11 +111,6 @@
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
OutputInterface *output = OutputInterface::get(output_resource);
- if (surface->buffer()) {
- wl_resource_post_error(resource->handle, error_already_constructed, "the wl_surface already has a buffer attached");
- return;
- }
-
if (layer > layer_overlay) {
wl_resource_post_error(resource->handle, error_invalid_layer, "invalid layer %d", layer);
return;
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/wayland/outputmanagement_v2.cpp kwin-6.3.6/src/wayland/outputmanagement_v2.cpp
--- kwin-6.3.5/src/wayland/outputmanagement_v2.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/outputmanagement_v2.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -324,6 +324,14 @@
if (invalid) {
return;
}
+ if (max_peak_brightness != -1 && max_peak_brightness < 50) {
+ failureReason = QStringLiteral("Invalid peak brightness override requested");
+ return;
+ }
+ if (max_average_brightness != -1 && max_average_brightness < 50) {
+ failureReason = QStringLiteral("Invalid max average brightness override requested");
+ return;
+ }
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
config.changeSet(output->handle())->maxPeakBrightnessOverride = max_peak_brightness == -1 ? std::nullopt : std::optional<double>(max_peak_brightness);
config.changeSet(output->handle())->maxAverageBrightnessOverride = max_average_brightness == -1 ? std::nullopt : std::optional<double>(max_average_brightness);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/wayland/tablet_v2.cpp kwin-6.3.6/src/wayland/tablet_v2.cpp
--- kwin-6.3.5/src/wayland/tablet_v2.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/tablet_v2.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -76,7 +76,7 @@
{
}
- void update(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot)
+ void update(quint32 serial, SurfaceInterface *surface, const QPointF &hotspot)
{
const bool diff = m_serial != serial || m_surface != surface || m_hotspot != hotspot;
if (diff) {
@@ -92,7 +92,7 @@
quint32 m_serial = 0;
QPointer<SurfaceInterface> m_surface;
- QPoint m_hotspot;
+ QPointF m_hotspot;
};
TabletSurfaceCursorV2::TabletSurfaceCursorV2()
@@ -103,7 +103,7 @@
TabletSurfaceCursorV2::~TabletSurfaceCursorV2() = default;
-QPoint TabletSurfaceCursorV2::hotspot() const
+QPointF TabletSurfaceCursorV2::hotspot() const
{
return d->m_hotspot;
}
@@ -170,6 +170,7 @@
void zwp_tablet_tool_v2_set_cursor(Resource *resource, uint32_t serial, struct ::wl_resource *_surface, int32_t hotspot_x, int32_t hotspot_y) override
{
SurfaceInterface *surface = SurfaceInterface::get(_surface);
+ QPointF hotspot = QPointF(hotspot_x, hotspot_y);
if (surface) {
static SurfaceRole cursorRole(QByteArrayLiteral("tablet_cursor_v2"));
if (const SurfaceRole *role = surface->role()) {
@@ -181,10 +182,11 @@
} else {
surface->setRole(&cursorRole);
}
+ hotspot /= surface->client()->scaleOverride();
}
TabletSurfaceCursorV2 *c = m_cursors[resource->client()];
- c->d->update(serial, surface, {hotspot_x, hotspot_y});
+ c->d->update(serial, surface, hotspot);
const auto resources = targetResources();
if (std::any_of(resources.begin(), resources.end(), [resource](const Resource *res) {
return res->handle == resource->handle;
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/wayland/tablet_v2.h kwin-6.3.6/src/wayland/tablet_v2.h
--- kwin-6.3.5/src/wayland/tablet_v2.h 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/tablet_v2.h 2025-07-08 13:45:08.000000000 +0200
@@ -63,7 +63,7 @@
Q_OBJECT
public:
~TabletSurfaceCursorV2() override;
- QPoint hotspot() const;
+ QPointF hotspot() const;
quint32 enteredSerial() const;
SurfaceInterface *surface() const;
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/wayland/xdgshell.cpp kwin-6.3.6/src/wayland/xdgshell.cpp
--- kwin-6.3.5/src/wayland/xdgshell.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/wayland/xdgshell.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -85,11 +85,6 @@
{
SurfaceInterface *surface = SurfaceInterface::get(surfaceResource);
- if (surface->buffer()) {
- wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation");
- return;
- }
-
wl_resource *xdgSurfaceResource = wl_resource_create(resource->client(), &xdg_surface_interface, resource->version(), id);
XdgSurfaceInterface *xdgSurface = new XdgSurfaceInterface(q, surface, xdgSurfaceResource);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/window.cpp kwin-6.3.6/src/window.cpp
--- kwin-6.3.5/src/window.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/window.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -2128,21 +2128,9 @@
break;
case Options::MouseActivateAndRaise: {
replay = isActive(); // for clickraise mode
- bool mustReplay = !rules()->checkAcceptFocus(acceptsFocus());
- if (mustReplay) {
- auto it = workspace()->stackingOrder().constEnd(),
- begin = workspace()->stackingOrder().constBegin();
- while (mustReplay && --it != begin && *it != this) {
- auto c = *it;
- if (!c->isClient() || (c->keepAbove() && !keepAbove()) || (keepBelow() && !c->keepBelow())) {
- continue; // can never raise above "it"
- }
- mustReplay = !(c->isOnCurrentDesktop() && c->isOnCurrentActivity() && c->frameGeometry().intersects(frameGeometry()));
- }
- }
workspace()->takeActivity(this, Workspace::ActivityFocus | Workspace::ActivityRaise);
workspace()->setActiveOutput(globalPos);
- replay = replay || mustReplay;
+ replay = replay || !rules()->checkAcceptFocus(acceptsFocus());
break;
}
case Options::MouseActivateAndLower:
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/workspace.cpp kwin-6.3.6/src/workspace.cpp
--- kwin-6.3.5/src/workspace.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/workspace.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -955,14 +955,7 @@
void Workspace::slotCurrentDesktopChanged(VirtualDesktop *oldDesktop, VirtualDesktop *newDesktop)
{
- closeActivePopup();
- ++block_focus;
- StackingUpdatesBlocker blocker(this);
- updateWindowVisibilityOnDesktopChange(newDesktop);
- // Restore the focus on this desktop
- --block_focus;
-
- activateWindowOnDesktop(newDesktop);
+ updateWindowVisibilityAndActivateOnDesktopChange(newDesktop);
Q_EMIT currentDesktopChanged(oldDesktop, m_moveResizeWindow);
}
@@ -985,7 +978,7 @@
if (!c) {
continue;
}
- if (!c->isOnDesktop(newDesktop) && c != m_moveResizeWindow && c->isOnCurrentActivity()) {
+ if (!(c->isOnDesktop(newDesktop) && c->isOnCurrentActivity()) && c != m_moveResizeWindow) {
(c)->updateVisibility();
}
}
@@ -995,6 +988,8 @@
}
#endif
+ // FIXME: Keep Move/Resize window across activities
+
if (m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop(newDesktop)) {
m_moveResizeWindow->setDesktops({newDesktop});
}
@@ -1015,6 +1010,18 @@
}
}
+void Workspace::updateWindowVisibilityAndActivateOnDesktopChange(VirtualDesktop *newDesktop)
+{
+ closeActivePopup();
+ ++block_focus;
+ StackingUpdatesBlocker blocker(this);
+ updateWindowVisibilityOnDesktopChange(newDesktop);
+ // Restore the focus on this desktop
+ --block_focus;
+
+ activateWindowOnDesktop(newDesktop);
+}
+
void Workspace::activateWindowOnDesktop(VirtualDesktop *desktop)
{
Window *window = nullptr;
@@ -1024,7 +1031,7 @@
// If "unreasonable focus policy" and m_activeWindow is on_all_desktops and
// under mouse (Hence == old_active_window), conserve focus.
// (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
- else if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) {
+ else if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) {
window = m_activeWindow;
}
@@ -1045,7 +1052,7 @@
Window *Workspace::findWindowToActivateOnDesktop(VirtualDesktop *desktop)
{
- if (m_moveResizeWindow != nullptr && m_activeWindow == m_moveResizeWindow && m_focusChain->contains(m_activeWindow, desktop) && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) {
+ if (m_moveResizeWindow != nullptr && m_activeWindow == m_moveResizeWindow && m_focusChain->contains(m_activeWindow, desktop) && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) {
// A requestFocus call will fail, as the window is already active
return m_activeWindow;
}
@@ -1086,77 +1093,8 @@
if (!m_activities) {
return;
}
- // closeActivePopup();
- ++block_focus;
- // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
- StackingUpdatesBlocker blocker(this);
-
- // Optimized Desktop switching: unmapping done from back to front
- // mapping done from front to back => less exposure events
- // Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
-
-#if KWIN_BUILD_X11
- for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
- X11Window *window = qobject_cast<X11Window *>(*it);
- if (!window) {
- continue;
- }
- if (!window->isOnActivity(new_activity) && window != m_moveResizeWindow && window->isOnCurrentDesktop()) {
- window->updateVisibility();
- }
- }
-
- // Now propagate the change, after hiding, before showing
- // rootInfo->setCurrentDesktop( currentDesktop() );
- /* TODO someday enable dragging windows to other activities
- if ( m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop( new_desktop ))
- {
- m_moveResizeWindow->setDesktop( new_desktop );
- */
-
- for (int i = stacking_order.size() - 1; i >= 0; --i) {
- X11Window *window = qobject_cast<X11Window *>(stacking_order.at(i));
- if (!window) {
- continue;
- }
- if (window->isOnActivity(new_activity)) {
- window->updateVisibility();
- }
- }
-#endif
-
- // FIXME not sure if I should do this either
- if (showingDesktop()) { // Do this only after desktop change to avoid flicker
- setShowingDesktop(false);
- }
-
- // Restore the focus on this desktop
- --block_focus;
- Window *window = nullptr;
-
- // FIXME below here is a lot of focuschain stuff, probably all wrong now
- // Keep active window focused if it's on the new activity
- if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) {
- window = m_activeWindow;
- } else if (options->focusPolicyIsReasonable()) {
- // Search in focus chain
- window = m_focusChain->getForActivation(VirtualDesktopManager::self()->currentDesktop());
- }
-
- if (!window) {
- window = findDesktop(VirtualDesktopManager::self()->currentDesktop(), activeOutput());
- }
-
- if (window != m_activeWindow) {
- setActiveWindow(nullptr);
- }
-
- if (window) {
- requestFocus(window);
- } else {
- focusToNull();
- }
+ updateWindowVisibilityAndActivateOnDesktopChange(VirtualDesktopManager::self()->currentDesktop());
Q_EMIT currentActivityChanged();
#endif
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/workspace.h kwin-6.3.6/src/workspace.h
--- kwin-6.3.5/src/workspace.h 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/workspace.h 2025-07-08 13:45:08.000000000 +0200
@@ -637,6 +637,7 @@
void closeActivePopup();
void updateWindowVisibilityOnDesktopChange(VirtualDesktop *newDesktop);
+ void updateWindowVisibilityAndActivateOnDesktopChange(VirtualDesktop *newDesktop);
void activateWindowOnDesktop(VirtualDesktop *desktop);
Window *findWindowToActivateOnDesktop(VirtualDesktop *desktop);
void removeWindow(Window *window);
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/xdgshellwindow.cpp kwin-6.3.6/src/xdgshellwindow.cpp
--- kwin-6.3.5/src/xdgshellwindow.cpp 2025-05-06 19:57:40.000000000 +0200
+++ kwin-6.3.6/src/xdgshellwindow.cpp 2025-07-20 16:38:09.000000000 +0200
@@ -126,6 +126,9 @@
configureEvent->flags |= m_configureFlags;
configureEvent->scale = m_nextTargetScale;
m_configureFlags = {};
+ if (!isInteractiveMoveResize()) {
+ m_nextGravity = Gravity::None;
+ }
m_configureEvents.append(configureEvent);
}
@@ -269,8 +272,12 @@
// and current client sizes have to be rounded to integers
const QSizeF requestedFrameSize = snapToPixels(rect.size(), nextTargetScale());
const QSizeF requestedClientSize = nextFrameSizeToClientSize(requestedFrameSize);
- if (requestedClientSize.toSize() == clientSize().toSize()) {
- updateGeometry(QRectF(rect.topLeft(), requestedFrameSize));
+ const QSize roundedRequestedClientSize = requestedClientSize.toSize();
+
+ const QSize roundedClientSize = clientSize().toSize();
+ if (roundedRequestedClientSize == roundedClientSize) {
+ const QRectF snappedRect = QRectF(rect.topLeft(), nextClientSizeToFrameSize(snapToPixels(roundedClientSize, nextTargetScale())));
+ updateGeometry(gravitateGeometry(snappedRect, rect, m_nextGravity));
} else {
m_configureFlags |= XdgSurfaceConfigure::ConfigurePosition;
scheduleConfigure();
diff -ur '--exclude=po' '--exclude=patches' '--exclude=autotests' kwin-6.3.5/src/xwayland/xwayland.cpp kwin-6.3.6/src/xwayland/xwayland.cpp
--- kwin-6.3.5/src/xwayland/xwayland.cpp 2025-07-20 16:38:08.000000000 +0200
+++ kwin-6.3.6/src/xwayland/xwayland.cpp 2025-07-08 13:45:08.000000000 +0200
@@ -307,6 +307,11 @@
if (!m_filterMouse) {
return false;
}
+ if (event->button == Qt::MouseButton::LeftButton
+ || event->button == Qt::MouseButton::RightButton
+ || event->button == Qt::MouseButton::MiddleButton) {
+ return false;
+ }
auto pointer = waylandServer()->seat()->pointer();
auto surface = pointer->focusedSurface();
--- End Message ---