From c77a732d737b8142a6e5ae376d48dda7cf38022e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Szalecki?= Date: Sat, 2 Dec 2023 01:27:01 +0100 Subject: [PATCH 01/76] style: add missing whitespace in magiclamp config --- plugins/effects/magiclamp/magiclamp_config.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/effects/magiclamp/magiclamp_config.ui b/plugins/effects/magiclamp/magiclamp_config.ui index a142acb33..d73c58200 100644 --- a/plugins/effects/magiclamp/magiclamp_config.ui +++ b/plugins/effects/magiclamp/magiclamp_config.ui @@ -43,7 +43,7 @@ SPDX-License-Identifier: GPL-2.0-or-later Default - milliseconds + milliseconds 5000 From b137498b32c83a6e45a4d3ab59a76c5bdd353ccf Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 5 Dec 2023 12:18:39 +0100 Subject: [PATCH 02/76] fix(plugin): use floating point offscreen texture With color management, everything would get clamped to 1 nit otherwise. BUG: 477841 --- plugins/effects/magnifier/magnifier.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/effects/magnifier/magnifier.cpp b/plugins/effects/magnifier/magnifier.cpp index 31de2d2a5..a3353d9bf 100644 --- a/plugins/effects/magnifier/magnifier.cpp +++ b/plugins/effects/magnifier/magnifier.cpp @@ -221,7 +221,7 @@ void MagnifierEffect::zoomIn() if (effects->isOpenGLCompositing() && !m_texture) { effects->makeOpenGLContextCurrent(); m_texture = std::make_unique( - GL_RGBA8, m_magnifierSize.width(), m_magnifierSize.height()); + GL_RGBA16F, m_magnifierSize.width(), m_magnifierSize.height()); m_texture->set_content_transform(effect::transform_type::normal); m_fbo = std::make_unique(m_texture.get()); } @@ -261,7 +261,7 @@ void MagnifierEffect::toggle() if (effects->isOpenGLCompositing() && !m_texture) { effects->makeOpenGLContextCurrent(); m_texture = std::make_unique( - GL_RGBA8, m_magnifierSize.width(), m_magnifierSize.height()); + GL_RGBA16F, m_magnifierSize.width(), m_magnifierSize.height()); m_texture->set_content_transform(effect::transform_type::normal); m_fbo = std::make_unique(m_texture.get()); } From af2648c32c1dd6bfce2c3c3b73bf792033788d8a Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 4 Dec 2023 18:02:35 +0200 Subject: [PATCH 03/76] fix(x11): fix MouseButtonPress events sent to decoration ButtonPressEvent::state includes keys and buttons prior to the button press has been generated. On the other hand, it appears that QMouseEvent::buttons() should include currently pressed buttons, i.e. the button that has been just pressed should be in that bitfield. This is important for QQuickDeliveryAgent, which checks QMouseEvent::button() and QMouseEvent::buttons() to decide whether to send the button event only to tap handlers or both tap handlers and mouse areas. BUG: 476653 --- como/win/x11/event.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/como/win/x11/event.h b/como/win/x11/event.h index e835a105e..b22227333 100644 --- a/como/win/x11/event.h +++ b/como/win/x11/event.h @@ -500,7 +500,8 @@ bool button_press_event(Win* win, QPoint(x, y), QPoint(x_root, y_root), base::x11::xcb::to_qt_mouse_button(button), - base::x11::xcb::to_qt_mouse_buttons(state), + base::x11::xcb::to_qt_mouse_buttons(state) + | base::x11::xcb::to_qt_mouse_buttons(button), Qt::KeyboardModifiers()); return win::process_decoration_button_press(win, &ev, true); } @@ -535,7 +536,8 @@ bool button_press_event(Win* win, QPointF(x, y), QPointF(x_root, y_root), base::x11::xcb::to_qt_mouse_button(button), - base::x11::xcb::to_qt_mouse_buttons(state), + base::x11::xcb::to_qt_mouse_buttons(state) + | base::x11::xcb::to_qt_mouse_buttons(button), key_server::to_qt_keyboard_modifiers(state)); event.setAccepted(false); QCoreApplication::sendEvent(win::decoration(win), &event); From 65c4dc19ec506607f239fc8b9abadccb655d4712 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Wed, 6 Dec 2023 10:50:08 +0000 Subject: [PATCH 04/76] fix(plugin): avoid relaying text during overview animation There is a window title beneat the stock WindowHeapDelegate. The label was bound to the size of the window, but because this animates our label resizes every frame too. Given the label is only at full opacity when the window is filling the ExpoCell, we can set the width of our label to that directly. This looks less glitchy as we don't change wrapping during animation, but also should have a noticable performance impact as we avoid re-laying out multiple times. --- plugins/effects/private/qml/WindowHeapDelegate.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index bdf1c3c9d..5dc36c5e9 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -171,7 +171,7 @@ Item { PlasmaExtras.ShadowedLabel { id: caption visible: thumb.windowTitleVisible - width: thumb.width + width: cell.width anchors.top: parent.bottom anchors.horizontalCenter: parent.horizontalCenter elide: Text.ElideRight From e9fe6d5d992c0da1fb8a5161d5fab84d3ef1d4a4 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Wed, 6 Dec 2023 13:23:16 +0200 Subject: [PATCH 05/76] fix: fix "Drag down to close" label visibility It's visible by default but has 0 opacity, which results in unnecessary relayouting. The opacity is vice versa as well. --- plugins/effects/private/qml/WindowHeapDelegate.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 5dc36c5e9..8827a3c3d 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -153,8 +153,8 @@ Item { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: i18nd("kwin", "Drag Down To Close") - opacity: 1 - thumbSource.opacity - visible: !thumb.activeHidden + opacity: thumbSource.opacity + visible: !thumb.activeHidden && activeDragHandler.active } Kirigami.Icon { From 982fcf84752bb999f9a290c5cbef6166ccae2573 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Wed, 6 Dec 2023 16:04:13 +0000 Subject: [PATCH 06/76] fix: have less concurrent animations Overview was trying always to animate the opening with 2 animations at the same moment: * the transition from initial to active in WindowHeapDelegate * the animation on the partialactivationfactor was always ran, partial or not this resulted in jankier animations than they could be. now base only on partialanimationfactor, so we a re sure there is one single way to trigger the effect --- .../private/qml/WindowHeapDelegate.qml | 11 +---------- plugins/effects/windowview/qml/main.qml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 8827a3c3d..6a374be27 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -81,7 +81,7 @@ Item { } component TweenBehavior : Behavior { - enabled: thumb.state !== "partial" && thumb.windowHeap.animationEnabled && thumb.animationEnabled && !thumb.activeDragHandler.active + enabled: thumb.state === "active-normal" && thumb.windowHeap.animationEnabled && thumb.animationEnabled && !thumb.activeDragHandler.active NumberAnimation { duration: thumb.windowHeap.animationDuration easing.type: Easing.OutCubic @@ -338,15 +338,6 @@ Item { properties: "x, y, width, height" easing.type: Easing.OutCubic } - }, - Transition { - to: "initial, initial-hidden, active-normal, active-hidden" - enabled: thumb.windowHeap.animationEnabled - NumberAnimation { - duration: thumb.windowHeap.animationDuration - properties: "x, y, width, height, opacity" - easing.type: Easing.OutCubic - } } ] diff --git a/plugins/effects/windowview/qml/main.qml b/plugins/effects/windowview/qml/main.qml index d1ec985ea..adacb47b5 100644 --- a/plugins/effects/windowview/qml/main.qml +++ b/plugins/effects/windowview/qml/main.qml @@ -200,8 +200,27 @@ Item { ~KWinComponents.WindowFilterModel.CriticalNotification } delegate: WindowHeapDelegate { + id: delegate windowHeap: heap partialActivationFactor: container.organized ? 1 : 0 + Behavior on partialActivationFactor { + SequentialAnimation { + PropertyAction { + target: delegate + property: "gestureInProgress" + value: true + } + NumberAnimation { + duration: container.effect.animationDuration + easing.type: Easing.OutCubic + } + PropertyAction { + target: delegate + property: "gestureInProgress" + value: false + } + } + } opacity: 1 - downGestureProgress onDownGestureTriggered: window.closeWindow() From dbd467407920602b5d448be28c316821c4aca724 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Wed, 6 Dec 2023 16:18:57 +0200 Subject: [PATCH 07/76] fix(plugin): make transition between overview-grid modes longer Otherwise it's barely visible. --- plugins/effects/overview/qml/main.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index accf7d144..6522d4062 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -118,7 +118,7 @@ FocusScope { transitions: Transition { to: "initial, grid, overview" NumberAnimation { - duration: Kirigami.Units.shortDuration + duration: effect.animationDuration properties: "gridVal, overviewVal" easing.type: Easing.OutCubic } From a7257ee5c0535b2c786d064a451eafcc2515cc78 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Wed, 6 Dec 2023 16:55:04 +0200 Subject: [PATCH 08/76] feat: assign top-left screen corner to overview by default --- plugins/effects/overview/metadata.json | 1 - plugins/effects/overview/overviewconfig.kcfg | 4 +++- plugins/effects/overview/overviewconfig.kcfgc | 1 + plugins/effects/windowview/windowviewconfig.kcfg | 4 +--- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/effects/overview/metadata.json b/plugins/effects/overview/metadata.json index 45b2d3748..22382f927 100644 --- a/plugins/effects/overview/metadata.json +++ b/plugins/effects/overview/metadata.json @@ -7,7 +7,6 @@ "Name": "Overview" }, "X-KDE-ConfigModule": "kwin_overview_config", - "X-KWin-Border-Activate": true, "org.kde.kwin.effect": { "video": "https://files.kde.org/plasma/kwin/effect-videos/present_windows.mp4" } diff --git a/plugins/effects/overview/overviewconfig.kcfg b/plugins/effects/overview/overviewconfig.kcfg index 13be0341b..db6bebbdb 100644 --- a/plugins/effects/overview/overviewconfig.kcfg +++ b/plugins/effects/overview/overviewconfig.kcfg @@ -23,7 +23,9 @@ true - + + ElectricTopLeft + diff --git a/plugins/effects/overview/overviewconfig.kcfgc b/plugins/effects/overview/overviewconfig.kcfgc index 120a52b48..fd1666b14 100644 --- a/plugins/effects/overview/overviewconfig.kcfgc +++ b/plugins/effects/overview/overviewconfig.kcfgc @@ -7,3 +7,4 @@ ClassName=OverviewConfig NameSpace=como Singleton=true Mutators=true +IncludeFiles=como/render/effect/interface/types.h diff --git a/plugins/effects/windowview/windowviewconfig.kcfg b/plugins/effects/windowview/windowviewconfig.kcfg index ec2fc3657..90feb7de8 100644 --- a/plugins/effects/windowview/windowviewconfig.kcfg +++ b/plugins/effects/windowview/windowviewconfig.kcfg @@ -18,9 +18,7 @@ - - QList<int>() << int(win::electric_border::top_left) - + From 74d235e37104219b5ec20bc3dbdd647411d8ab69 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 7 Dec 2023 12:29:37 +0200 Subject: [PATCH 09/76] perf: don't block client until acquire fence is signaled in WindowThumbnailItem We just need to ensure that qtquick rendering commands don't use the window thumbnail until it's ready. The CPU can continue executing its commands. --- como/script/window_thumbnail_item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/como/script/window_thumbnail_item.cpp b/como/script/window_thumbnail_item.cpp index 1ab9c6449..a2546c274 100644 --- a/como/script/window_thumbnail_item.cpp +++ b/como/script/window_thumbnail_item.cpp @@ -214,7 +214,7 @@ QSGNode* window_thumbnail_item::updatePaintNode(QSGNode* oldNode, QQuickItem::Up // Wait for rendering commands to the offscreen texture complete if there are any. if (m_acquireFence) { - glClientWaitSync(m_acquireFence, GL_SYNC_FLUSH_COMMANDS_BIT, 5000); + glWaitSync(m_acquireFence, 0, GL_TIMEOUT_IGNORED); glDeleteSync(m_acquireFence); m_acquireFence = nullptr; } From 9eca6338bad56f736509149e5c4f9096ea67aad5 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Wed, 6 Dec 2023 13:53:00 +0200 Subject: [PATCH 10/76] feat: change window highlight style in WindowHeapDelegate Currently FrameSvgItem is used to highlight selected or hovered windows. But the problem with it is that it's a bit heavyweight. As a way around it, we could put it in a Loader, but that's going to be bring a set of other challenges. As an alternative solution, this change replaces FrameSvgItem with a simple outline. It still produces decent visuals and it's simpler. --- .../private/qml/WindowHeapDelegate.qml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 6a374be27..105523125 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -127,17 +127,14 @@ Item { thumb.windowHeap.deleteDND(thumb.window.internalId); } - KSvg.FrameSvgItem { - anchors { - fill: parent - topMargin: -Kirigami.Units.smallSpacing * 2 - leftMargin: -Kirigami.Units.smallSpacing * 2 - rightMargin: -Kirigami.Units.smallSpacing * 2 - bottomMargin: -(Math.round(icon.height / 4) + (thumb.windowTitleVisible ? caption.height : 0) + (Kirigami.Units.smallSpacing * 2)) - } - imagePath: "widgets/viewitem" - prefix: "hover" - z: -1 + // Not using FrameSvg hover element intentionally for stylistic reasons + Rectangle { + border.width: Kirigami.Units.largeSpacing + border.color: Kirigami.Theme.highlightColor + anchors.fill: parent + anchors.margins: -border.width + radius: border.width + color: "transparent" visible: !thumb.windowHeap.dragActive && (hoverHandler.hovered || (thumb.selected && Window.window.activeFocusItem)) && windowHeap.effectiveOrganized } From c325c12ae802f9c532a5453b10e06b3742c04740 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 7 Dec 2023 16:51:43 +0200 Subject: [PATCH 11/76] perf: load thumbnail grid Plasma.Dialog on demand This avoids keeping old and creating new window thumbnails after the task switcher is hidden. --- .../thumbnail_grid/contents/ui/main.qml | 373 +++++++++--------- 1 file changed, 188 insertions(+), 185 deletions(-) diff --git a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml index 1cef20505..afbd8799e 100644 --- a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml +++ b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml @@ -17,205 +17,208 @@ KWin.TabBoxSwitcher { id: tabBox currentIndex: thumbnailGridView.currentIndex - PlasmaCore.Dialog { - location: PlasmaCore.Types.Floating - visible: tabBox.visible - flags: Qt.X11BypassWindowManagerHint - x: tabBox.screenGeometry.x + tabBox.screenGeometry.width * 0.5 - dialogMainItem.width * 0.5 - y: tabBox.screenGeometry.y + tabBox.screenGeometry.height * 0.5 - dialogMainItem.height * 0.5 - - mainItem: Item { - id: dialogMainItem - - focus: true - - property int maxWidth: tabBox.screenGeometry.width * 0.9 - property int maxHeight: tabBox.screenGeometry.height * 0.7 - property real screenFactor: tabBox.screenGeometry.width / tabBox.screenGeometry.height - property int maxGridColumnsByWidth: Math.floor(maxWidth / thumbnailGridView.cellWidth) - - property int gridColumns: { // Simple greedy algorithm - // respect screenGeometry - const c = Math.min(thumbnailGridView.count, maxGridColumnsByWidth); - const residue = thumbnailGridView.count % c; - if (residue == 0) { - return c; - } - // start greedy recursion - return columnCountRecursion(c, c, c - residue); - } - - property int gridRows: Math.ceil(thumbnailGridView.count / gridColumns) - property int optimalWidth: thumbnailGridView.cellWidth * gridColumns - property int optimalHeight: thumbnailGridView.cellHeight * gridRows - width: Math.min(Math.max(thumbnailGridView.cellWidth, optimalWidth), maxWidth) - height: Math.min(Math.max(thumbnailGridView.cellHeight, optimalHeight), maxHeight) - - clip: true - - // Step for greedy algorithm - function columnCountRecursion(prevC, prevBestC, prevDiff) { - const c = prevC - 1; - - // don't increase vertical extent more than horizontal - // and don't exceed maxHeight - if (prevC * prevC <= thumbnailGridView.count + prevDiff || - maxHeight < Math.ceil(thumbnailGridView.count / c) * thumbnailGridView.cellHeight) { - return prevBestC; - } - const residue = thumbnailGridView.count % c; - // halts algorithm at some point - if (residue == 0) { - return c; + Instantiator { + active: tabBox.visible + delegate: PlasmaCore.Dialog { + location: PlasmaCore.Types.Floating + visible: true + flags: Qt.X11BypassWindowManagerHint + x: tabBox.screenGeometry.x + tabBox.screenGeometry.width * 0.5 - dialogMainItem.width * 0.5 + y: tabBox.screenGeometry.y + tabBox.screenGeometry.height * 0.5 - dialogMainItem.height * 0.5 + + mainItem: Item { + id: dialogMainItem + + focus: true + + property int maxWidth: tabBox.screenGeometry.width * 0.9 + property int maxHeight: tabBox.screenGeometry.height * 0.7 + property real screenFactor: tabBox.screenGeometry.width / tabBox.screenGeometry.height + property int maxGridColumnsByWidth: Math.floor(maxWidth / thumbnailGridView.cellWidth) + + property int gridColumns: { // Simple greedy algorithm + // respect screenGeometry + const c = Math.min(thumbnailGridView.count, maxGridColumnsByWidth); + const residue = thumbnailGridView.count % c; + if (residue == 0) { + return c; + } + // start greedy recursion + return columnCountRecursion(c, c, c - residue); } - // empty slots - const diff = c - residue; - - // compare it to previous count of empty slots - if (diff < prevDiff) { - return columnCountRecursion(c, c, diff); - } else if (diff == prevDiff) { - // when it's the same try again, we'll stop early enough thanks to the landscape mode condition + + property int gridRows: Math.ceil(thumbnailGridView.count / gridColumns) + property int optimalWidth: thumbnailGridView.cellWidth * gridColumns + property int optimalHeight: thumbnailGridView.cellHeight * gridRows + width: Math.min(Math.max(thumbnailGridView.cellWidth, optimalWidth), maxWidth) + height: Math.min(Math.max(thumbnailGridView.cellHeight, optimalHeight), maxHeight) + + clip: true + + // Step for greedy algorithm + function columnCountRecursion(prevC, prevBestC, prevDiff) { + const c = prevC - 1; + + // don't increase vertical extent more than horizontal + // and don't exceed maxHeight + if (prevC * prevC <= thumbnailGridView.count + prevDiff || + maxHeight < Math.ceil(thumbnailGridView.count / c) * thumbnailGridView.cellHeight) { + return prevBestC; + } + const residue = thumbnailGridView.count % c; + // halts algorithm at some point + if (residue == 0) { + return c; + } + // empty slots + const diff = c - residue; + + // compare it to previous count of empty slots + if (diff < prevDiff) { + return columnCountRecursion(c, c, diff); + } else if (diff == prevDiff) { + // when it's the same try again, we'll stop early enough thanks to the landscape mode condition + return columnCountRecursion(c, prevBestC, diff); + } + // when we've found a local minimum choose this one (greedy) return columnCountRecursion(c, prevBestC, diff); } - // when we've found a local minimum choose this one (greedy) - return columnCountRecursion(c, prevBestC, diff); - } - - // Just to get the margin sizes - KSvg.FrameSvgItem { - id: hoverItem - imagePath: "widgets/viewitem" - prefix: "hover" - visible: false - } - - GridView { - id: thumbnailGridView - anchors.fill: parent - focus: true - model: tabBox.model - - readonly property int iconSize: Kirigami.Units.iconSizes.huge - readonly property int captionRowHeight: Kirigami.Units.gridUnit * 2 - readonly property int columnSpacing: Kirigami.Units.gridUnit - readonly property int thumbnailWidth: Kirigami.Units.gridUnit * 16 - readonly property int thumbnailHeight: thumbnailWidth * (1.0/dialogMainItem.screenFactor) - cellWidth: hoverItem.margins.left + thumbnailWidth + hoverItem.margins.right - cellHeight: hoverItem.margins.top + captionRowHeight + thumbnailHeight + hoverItem.margins.bottom - - keyNavigationWraps: true - highlightMoveDuration: 0 - - delegate: MouseArea { - id: thumbnailGridItem - width: thumbnailGridView.cellWidth - height: thumbnailGridView.cellHeight - focus: GridView.isCurrentItem - hoverEnabled: true - - Accessible.name: model.caption - Accessible.role: Accessible.ListItem - - onClicked: { - thumbnailGridView.currentIndex = index; - } - ColumnLayout { - id: columnLayout - z: 0 - spacing: thumbnailGridView.columnSpacing - anchors.fill: parent - anchors.leftMargin: hoverItem.margins.left * 2 - anchors.topMargin: hoverItem.margins.top * 2 - anchors.rightMargin: hoverItem.margins.right * 2 - anchors.bottomMargin: hoverItem.margins.bottom * 2 - - - // KWin.WindowThumbnail needs a container - // otherwise it will be drawn the same size as the parent ColumnLayout - Item { - Layout.fillWidth: true - Layout.fillHeight: true - - KWin.WindowThumbnail { - anchors.fill: parent - wId: windowId - } + // Just to get the margin sizes + KSvg.FrameSvgItem { + id: hoverItem + imagePath: "widgets/viewitem" + prefix: "hover" + visible: false + } - Kirigami.Icon { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.bottom - anchors.verticalCenterOffset: Math.round(-thumbnailGridView.iconSize / 4) - width: thumbnailGridView.iconSize - height: thumbnailGridView.iconSize + GridView { + id: thumbnailGridView + anchors.fill: parent + focus: true + model: tabBox.model + + readonly property int iconSize: Kirigami.Units.iconSizes.huge + readonly property int captionRowHeight: Kirigami.Units.gridUnit * 2 + readonly property int columnSpacing: Kirigami.Units.gridUnit + readonly property int thumbnailWidth: Kirigami.Units.gridUnit * 16 + readonly property int thumbnailHeight: thumbnailWidth * (1.0/dialogMainItem.screenFactor) + cellWidth: hoverItem.margins.left + thumbnailWidth + hoverItem.margins.right + cellHeight: hoverItem.margins.top + captionRowHeight + thumbnailHeight + hoverItem.margins.bottom + + keyNavigationWraps: true + highlightMoveDuration: 0 + + delegate: MouseArea { + id: thumbnailGridItem + width: thumbnailGridView.cellWidth + height: thumbnailGridView.cellHeight + focus: GridView.isCurrentItem + hoverEnabled: true + + Accessible.name: model.caption + Accessible.role: Accessible.ListItem + + onClicked: { + thumbnailGridView.currentIndex = index; + } - source: model.icon - } + ColumnLayout { + id: columnLayout + z: 0 + spacing: thumbnailGridView.columnSpacing + anchors.fill: parent + anchors.leftMargin: hoverItem.margins.left * 2 + anchors.topMargin: hoverItem.margins.top * 2 + anchors.rightMargin: hoverItem.margins.right * 2 + anchors.bottomMargin: hoverItem.margins.bottom * 2 + + + // KWin.WindowThumbnail needs a container + // otherwise it will be drawn the same size as the parent ColumnLayout + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + KWin.WindowThumbnail { + anchors.fill: parent + wId: windowId + } + + Kirigami.Icon { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.bottom + anchors.verticalCenterOffset: Math.round(-thumbnailGridView.iconSize / 4) + width: thumbnailGridView.iconSize + height: thumbnailGridView.iconSize - PlasmaComponents3.ToolButton { - id: closeButton - anchors { - right: parent.right - top: parent.top - // Deliberately touch the inner edges of the frame - rightMargin: -columnLayout.anchors.rightMargin - topMargin: -columnLayout.anchors.topMargin + source: model.icon } - visible: model.closeable && typeof tabBox.model.close !== 'undefined' && - (thumbnailGridItem.containsMouse - || closeButton.hovered - || thumbnailGridItem.focus - || Kirigami.Settings.tabletMode - || Kirigami.Settings.hasTransientTouchInput - ) - icon.name: 'window-close-symbolic' - onClicked: { - tabBox.model.close(index); + + PlasmaComponents3.ToolButton { + id: closeButton + anchors { + right: parent.right + top: parent.top + // Deliberately touch the inner edges of the frame + rightMargin: -columnLayout.anchors.rightMargin + topMargin: -columnLayout.anchors.topMargin + } + visible: model.closeable && typeof tabBox.model.close !== 'undefined' && + (thumbnailGridItem.containsMouse + || closeButton.hovered + || thumbnailGridItem.focus + || Kirigami.Settings.tabletMode + || Kirigami.Settings.hasTransientTouchInput + ) + icon.name: 'window-close-symbolic' + onClicked: { + tabBox.model.close(index); + } } } - } - PlasmaComponents3.Label { - Layout.fillWidth: true - text: model.caption - font.weight: thumbnailGridItem.focus ? Font.Bold : Font.Normal - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - textFormat: Text.PlainText - elide: Text.ElideRight + PlasmaComponents3.Label { + Layout.fillWidth: true + text: model.caption + font.weight: thumbnailGridItem.focus ? Font.Bold : Font.Normal + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + textFormat: Text.PlainText + elide: Text.ElideRight + } } - } - } // GridView.delegate + } // GridView.delegate - highlight: KSvg.FrameSvgItem { - imagePath: "widgets/viewitem" - prefix: "hover" - } + highlight: KSvg.FrameSvgItem { + imagePath: "widgets/viewitem" + prefix: "hover" + } - Connections { - target: tabBox - function onCurrentIndexChanged() { - thumbnailGridView.currentIndex = tabBox.currentIndex; + Connections { + target: tabBox + function onCurrentIndexChanged() { + thumbnailGridView.currentIndex = tabBox.currentIndex; + } + } + } // GridView + + Keys.onPressed: { + if (event.key == Qt.Key_Left) { + thumbnailGridView.moveCurrentIndexLeft(); + } else if (event.key == Qt.Key_Right) { + thumbnailGridView.moveCurrentIndexRight(); + } else if (event.key == Qt.Key_Up) { + thumbnailGridView.moveCurrentIndexUp(); + } else if (event.key == Qt.Key_Down) { + thumbnailGridView.moveCurrentIndexDown(); + } else { + return; } - } - } // GridView - - Keys.onPressed: { - if (event.key == Qt.Key_Left) { - thumbnailGridView.moveCurrentIndexLeft(); - } else if (event.key == Qt.Key_Right) { - thumbnailGridView.moveCurrentIndexRight(); - } else if (event.key == Qt.Key_Up) { - thumbnailGridView.moveCurrentIndexUp(); - } else if (event.key == Qt.Key_Down) { - thumbnailGridView.moveCurrentIndexDown(); - } else { - return; - } - thumbnailGridView.currentIndexChanged(thumbnailGridView.currentIndex); - } - } // Dialog.mainItem - } // Dialog + thumbnailGridView.currentIndexChanged(thumbnailGridView.currentIndex); + } + } // Dialog.mainItem + } // Dialog + } // Instantiator } From 4fa019c2075a1bb4efaee5181ad49570ab276c30 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 7 Dec 2023 16:53:45 +0200 Subject: [PATCH 12/76] fix: fix sync'ing currentIndex The task switcher doesn't initialize thumbnailGridView.currentIndex properly after the Plasma.Dialog has been just created. --- .../switchers/thumbnail_grid/contents/ui/main.qml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml index afbd8799e..cf7d92c9b 100644 --- a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml +++ b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml @@ -15,7 +15,6 @@ import org.kde.kirigami 2.20 as Kirigami KWin.TabBoxSwitcher { id: tabBox - currentIndex: thumbnailGridView.currentIndex Instantiator { active: tabBox.visible @@ -97,6 +96,7 @@ KWin.TabBoxSwitcher { anchors.fill: parent focus: true model: tabBox.model + currentIndex: tabBox.currentIndex readonly property int iconSize: Kirigami.Units.iconSizes.huge readonly property int captionRowHeight: Kirigami.Units.gridUnit * 2 @@ -120,7 +120,7 @@ KWin.TabBoxSwitcher { Accessible.role: Accessible.ListItem onClicked: { - thumbnailGridView.currentIndex = index; + tabBox.currentIndex = index; } ColumnLayout { @@ -195,12 +195,7 @@ KWin.TabBoxSwitcher { prefix: "hover" } - Connections { - target: tabBox - function onCurrentIndexChanged() { - thumbnailGridView.currentIndex = tabBox.currentIndex; - } - } + onCurrentIndexChanged: tabBox.currentIndex = thumbnailGridView.currentIndex; } // GridView Keys.onPressed: { From 329817783caa6fbe7bf36335004a5a877f3fa36c Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 7 Dec 2023 09:37:37 +0200 Subject: [PATCH 13/76] perf: load close button in WindowHeapDelegate on demand Most windows are typically not hovered, so we don't need to create a Button for every one of them. --- .../private/qml/WindowHeapDelegate.qml | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 105523125..6b7175fbc 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -431,28 +431,29 @@ Item { } } - PC3.Button { + Loader { id: closeButton + LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft anchors { right: thumbSource.right top: thumbSource.top margins: Kirigami.Units.smallSpacing } + active: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.window.closeable && !thumb.activeDragHandler.active - visible: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.window.closeable && !thumb.activeDragHandler.active - LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft - - text: i18ndc("kwin", "@info:tooltip as in: 'close this window'", "Close window") - icon.name: "window-close" - display: PC3.AbstractButton.IconOnly + sourceComponent: PC3.Button { + text: i18ndc("kwin", "@info:tooltip as in: 'close this window'", "Close window") + icon.name: "window-close" + display: PC3.AbstractButton.IconOnly - PC3.ToolTip.text: text - PC3.ToolTip.visible: hovered && display === PC3.AbstractButton.IconOnly - PC3.ToolTip.delay: Kirigami.Units.toolTipDelay - Accessible.name: text + PC3.ToolTip.text: text + PC3.ToolTip.visible: hovered && display === PC3.AbstractButton.IconOnly + PC3.ToolTip.delay: Kirigami.Units.toolTipDelay + Accessible.name: text - onClicked: thumb.window.closeWindow(); + onClicked: thumb.window.closeWindow(); + } } Component.onDestruction: { From 24cee4fd4fa31e757ce2d0439f53ec8b5b10a0b7 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Tue, 5 Dec 2023 12:29:29 +0200 Subject: [PATCH 14/76] perf: cache WindowThumbnail textures Effects like overview can create more than one thumbnail for the same window. For example, if you have 4 virtual desktops, the overview is going to create 9 window thumbnails for the desktop background, which in its turn means that each thumbnail is going to have its own texture. That's not great. WindowThumbnailItem.sourceSize has been dropped because it's unused and will complicate texture caching. CCBUG: 455780 --- como/script/window_thumbnail_item.cpp | 356 +++++++++++++------------- como/script/window_thumbnail_item.h | 57 +++-- 2 files changed, 220 insertions(+), 193 deletions(-) diff --git a/como/script/window_thumbnail_item.cpp b/como/script/window_thumbnail_item.cpp index a2546c274..7a017971b 100644 --- a/como/script/window_thumbnail_item.cpp +++ b/como/script/window_thumbnail_item.cpp @@ -25,18 +25,158 @@ SPDX-License-Identifier: GPL-2.0-or-later namespace como::scripting { + +namespace +{ +bool use_gl_thumbnails() +{ + static bool qt_quick_is_software + = QStringList({QStringLiteral("software"), QStringLiteral("softwarecontext")}) + .contains(QQuickWindow::sceneGraphBackend()); + return effects && effects->isOpenGLCompositing() && !qt_quick_is_software; +} +} + +window_thumbnail_source::window_thumbnail_source(QQuickWindow* view, + scripting::window* handle, + QUuid wId) + : m_view(view) + , m_handle(handle) + , wId{wId} +{ + connect(handle, &scripting::window::frameGeometryChanged, this, [this]() { + m_dirty = true; + Q_EMIT changed(); + }); + connect(handle, &scripting::window::damaged, this, [this]() { + m_dirty = true; + Q_EMIT changed(); + }); + + connect(effects, &EffectsHandler::frameRendered, this, &window_thumbnail_source::update); +} + +window_thumbnail_source::~window_thumbnail_source() +{ + if (!m_offscreenTexture) { + return; + } + if (effects) { + effects->makeOpenGLContextCurrent(); + m_offscreenTarget.reset(); + m_offscreenTexture.reset(); + + if (m_acquireFence) { + glDeleteSync(m_acquireFence); + m_acquireFence = nullptr; + } + effects->doneOpenGLContextCurrent(); + } +} + +std::shared_ptr +window_thumbnail_source::getOrCreate(QQuickWindow* window, scripting::window* handle, QUuid wId) +{ + using window_thumbnail_sourceKey = std::pair; + const window_thumbnail_sourceKey key{window, handle}; + + static std::map> sources; + auto& source = sources[key]; + if (!source.expired()) { + return source.lock(); + } + + auto s = std::make_shared(window, handle, wId); + source = s; + + QObject::connect(handle, &scripting::window::destroyed, [key]() { sources.erase(key); }); + QObject::connect(window, &QQuickWindow::destroyed, [key]() { sources.erase(key); }); + return s; +} + +window_thumbnail_source::Frame window_thumbnail_source::acquire() +{ + return Frame{ + .texture = m_offscreenTexture, + .fence = std::exchange(m_acquireFence, nullptr), + }; +} + +void window_thumbnail_source::update(effect::screen_paint_data& data) +{ + if (m_acquireFence || !m_dirty || !m_handle) { + return; + } + Q_ASSERT(m_view); + + auto const geometry = m_handle->visibleRect(); + auto const dpi = m_view->devicePixelRatio(); + auto const textureSize = dpi * geometry.size(); + + if (!m_offscreenTexture || m_offscreenTexture->size() != textureSize) { + m_offscreenTexture.reset(new GLTexture(GL_RGBA8, textureSize)); + m_offscreenTexture->setFilter(GL_LINEAR); + m_offscreenTexture->setWrapMode(GL_CLAMP_TO_EDGE); + m_offscreenTarget.reset(new GLFramebuffer(m_offscreenTexture.get())); + } + + QMatrix4x4 view; + view.ortho(geometry.x(), + geometry.x() + geometry.width(), + geometry.y(), + geometry.y() + geometry.height(), + -1, + 1); + + QMatrix4x4 proj; + proj.scale(dpi); + + auto effectWindow = effects->findWindow(wId); + + effect::window_paint_data win_data{ + *effectWindow, + { + .mask = Effect::PAINT_WINDOW_TRANSFORMED, + .region = infiniteRegion(), + }, + { + .targets = data.render.targets, + .view = view, + .projection = proj, + .viewport = geometry, + }, + }; + + render::push_framebuffer(win_data.render, m_offscreenTarget.get()); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + // The thumbnail must be rendered using kwin's opengl context as VAOs are not + // shared across contexts. Unfortunately, this also introduces a latency of 1 + // frame, which is not ideal, but it is acceptable for things such as thumbnails. + effects->drawWindow(win_data); + render::pop_framebuffer(win_data.render); + + // The fence is needed to avoid the case where qtquick renderer starts using + // the texture while all rendering commands to it haven't completed yet. + m_dirty = false; + m_acquireFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + Q_EMIT changed(); +} + class ThumbnailTextureProvider : public QSGTextureProvider { public: explicit ThumbnailTextureProvider(QQuickWindow* window); QSGTexture* texture() const override; - void setTexture(QSharedPointer const& nativeTexture); + void setTexture(std::shared_ptr const& nativeTexture); void setTexture(QSGTexture* texture); private: QQuickWindow* m_window; - QSharedPointer m_nativeTexture; + std::shared_ptr m_nativeTexture; QScopedPointer m_texture; }; @@ -50,7 +190,7 @@ QSGTexture* ThumbnailTextureProvider::texture() const return m_texture.data(); } -void ThumbnailTextureProvider::setTexture(QSharedPointer const& nativeTexture) +void ThumbnailTextureProvider::setTexture(std::shared_ptr const& nativeTexture) { if (m_nativeTexture != nativeTexture) { auto const textureId = nativeTexture->texture(); @@ -94,23 +234,20 @@ window_thumbnail_item::window_thumbnail_item(QQuickItem* parent) : QQuickItem(parent) { setFlag(ItemHasContents); - update_render_notifier(); connect(render::singleton_interface::compositor, &render::compositor_qobject::aboutToToggleCompositing, this, - &window_thumbnail_item::destroyOffscreenTexture); + &window_thumbnail_item::reset_source); connect(render::singleton_interface::compositor, &render::compositor_qobject::compositingToggled, this, - &window_thumbnail_item::update_render_notifier); - connect(this, &QQuickItem::windowChanged, this, &window_thumbnail_item::update_render_notifier); + &window_thumbnail_item::update_source); + connect(this, &QQuickItem::windowChanged, this, &window_thumbnail_item::update_source); } window_thumbnail_item::~window_thumbnail_item() { - destroyOffscreenTexture(); - if (m_provider) { if (window()) { window()->scheduleRenderJob(new ThumbnailTextureProviderCleanupJob(m_provider), @@ -147,97 +284,61 @@ QSGTextureProvider* window_thumbnail_item::textureProvider() const return m_provider; } -void window_thumbnail_item::update_render_notifier() +void window_thumbnail_item::reset_source() { - disconnect(render_notifier); - - if (!window()) { - return; - } - - if (!use_gl_thumbnails()) { - return; - } - - render_notifier = connect(effects, - &EffectsHandler::frameRendered, - this, - &window_thumbnail_item::updateOffscreenTexture); + m_source.reset(); } -bool window_thumbnail_item::use_gl_thumbnails() const +void window_thumbnail_item::update_source() { - static bool qt_quick_is_software - = QStringList({QStringLiteral("software"), QStringLiteral("softwarecontext")}) - .contains(QQuickWindow::sceneGraphBackend()); - return effects && effects->isOpenGLCompositing() && !qt_quick_is_software; -} - -QSize window_thumbnail_item::sourceSize() const -{ - return m_sourceSize; -} - -void window_thumbnail_item::setSourceSize(const QSize& sourceSize) -{ - if (m_sourceSize != sourceSize) { - m_sourceSize = sourceSize; - invalidateOffscreenTexture(); - Q_EMIT sourceSizeChanged(); - } -} - -void window_thumbnail_item::destroyOffscreenTexture() -{ - if (!use_gl_thumbnails()) { - return; - } - - if (m_offscreenTexture) { - effects->makeOpenGLContextCurrent(); - m_offscreenTarget.reset(); - m_offscreenTexture.reset(); - - if (m_acquireFence) { - glDeleteSync(m_acquireFence); - m_acquireFence = nullptr; - } - effects->doneOpenGLContextCurrent(); + if (use_gl_thumbnails() && window() && m_client) { + m_source = window_thumbnail_source::getOrCreate(window(), m_client, m_wId); + connect(m_source.get(), + &window_thumbnail_source::changed, + this, + &window_thumbnail_item::update); + } else { + m_source.reset(); } } QSGNode* window_thumbnail_item::updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData*) { - if (effects && !m_offscreenTexture) { - return oldNode; - } + if (effects) { + if (!m_source) { + return oldNode; + } - // Wait for rendering commands to the offscreen texture complete if there are any. - if (m_acquireFence) { - glWaitSync(m_acquireFence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(m_acquireFence); - m_acquireFence = nullptr; - } + auto [texture, acquireFence] = m_source->acquire(); + if (!texture) { + return oldNode; + } - if (!m_provider) { - m_provider = new ThumbnailTextureProvider(window()); - } + // Wait for rendering commands to the offscreen texture complete if there are any. + if (acquireFence) { + glWaitSync(acquireFence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(acquireFence); + } - if (m_offscreenTexture) { - m_provider->setTexture(m_offscreenTexture); + if (!m_provider) { + m_provider = new ThumbnailTextureProvider(window()); + } + m_provider->setTexture(texture); } else { + if (!m_provider) { + m_provider = new ThumbnailTextureProvider(window()); + } + auto const placeholderImage = fallbackImage(); m_provider->setTexture(window()->createTextureFromImage(placeholderImage)); - m_devicePixelRatio = placeholderImage.devicePixelRatio(); } - auto node = static_cast(oldNode); + QSGImageNode* node = static_cast(oldNode); if (!node) { node = window()->createImageNode(); node->setFiltering(QSGTexture::Linear); } node->setTexture(m_provider->texture()); - node->setTextureCoordinatesTransform(QSGImageNode::NoTransform); node->setRect(paintedRect()); @@ -271,6 +372,7 @@ void window_thumbnail_item::setWId(const QUuid& wId) } else { if (m_client) { m_client = nullptr; + update_source(); updateImplicitSize(); Q_EMIT clientChanged(); } @@ -289,14 +391,6 @@ void window_thumbnail_item::setClient(scripting::window* client) return; } if (m_client) { - disconnect(m_client, - &scripting::window::frameGeometryChanged, - this, - &window_thumbnail_item::invalidateOffscreenTexture); - disconnect(m_client, - &scripting::window::damaged, - this, - &window_thumbnail_item::invalidateOffscreenTexture); disconnect(m_client, &scripting::window::frameGeometryChanged, this, @@ -304,14 +398,6 @@ void window_thumbnail_item::setClient(scripting::window* client) } m_client = client; if (m_client) { - connect(m_client, - &scripting::window::frameGeometryChanged, - this, - &window_thumbnail_item::invalidateOffscreenTexture); - connect(m_client, - &scripting::window::damaged, - this, - &window_thumbnail_item::invalidateOffscreenTexture); connect(m_client, &scripting::window::frameGeometryChanged, this, @@ -320,7 +406,7 @@ void window_thumbnail_item::setClient(scripting::window* client) } else { setWId(QUuid()); } - invalidateOffscreenTexture(); + update_source(); updateImplicitSize(); Q_EMIT clientChanged(); } @@ -355,7 +441,7 @@ QRectF window_thumbnail_item::paintedRect() const if (!m_client) { return QRectF(); } - if (!m_offscreenTexture) { + if (!effects) { auto const iconSize = m_client->icon().actualSize(boundingRect().size().toSize()); return centeredSize(boundingRect(), iconSize); } @@ -379,82 +465,4 @@ QRectF window_thumbnail_item::paintedRect() const return paintedRect; } -void window_thumbnail_item::invalidateOffscreenTexture() -{ - m_dirty = true; - update(); -} - -void window_thumbnail_item::updateOffscreenTexture(effect::screen_paint_data& data) -{ - if (m_acquireFence || !m_dirty || !m_client) { - return; - } - Q_ASSERT(window()); - - auto const geometry = m_client->visibleRect(); - QSize textureSize = geometry.size(); - if (sourceSize().width() > 0) { - textureSize.setWidth(sourceSize().width()); - } - if (sourceSize().height() > 0) { - textureSize.setHeight(sourceSize().height()); - } - - m_devicePixelRatio = window()->devicePixelRatio(); - textureSize *= m_devicePixelRatio; - - if (!m_offscreenTexture || m_offscreenTexture->size() != textureSize) { - m_offscreenTexture.reset(new GLTexture(GL_RGBA8, textureSize)); - m_offscreenTexture->setFilter(GL_LINEAR); - m_offscreenTexture->setWrapMode(GL_CLAMP_TO_EDGE); - m_offscreenTarget.reset(new GLFramebuffer(m_offscreenTexture.data())); - } - - QMatrix4x4 view; - view.ortho(geometry.x(), - geometry.x() + geometry.width(), - geometry.y(), - geometry.y() + geometry.height(), - -1, - 1); - - QMatrix4x4 proj; - proj.scale(m_devicePixelRatio); - - auto effectWindow = effects->findWindow(m_wId); - - effect::window_paint_data win_data{ - *effectWindow, - { - .mask = Effect::PAINT_WINDOW_TRANSFORMED, - .region = infiniteRegion(), - }, - { - .targets = data.render.targets, - .view = view, - .projection = proj, - .viewport = geometry, - }, - }; - - render::push_framebuffer(win_data.render, m_offscreenTarget.data()); - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - - // The thumbnail must be rendered using kwin's opengl context as VAOs are not - // shared across contexts. Unfortunately, this also introduces a latency of 1 - // frame, which is not ideal, but it is acceptable for things such as thumbnails. - effects->drawWindow(win_data); - render::pop_framebuffer(win_data.render); - - // The fence is needed to avoid the case where qtquick renderer starts using - // the texture while all rendering commands to it haven't completed yet. - m_dirty = false; - m_acquireFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - // We know that the texture has changed, so schedule an item update. - update(); -} - } diff --git a/como/script/window_thumbnail_item.h b/como/script/window_thumbnail_item.h index d962ca962..e8309789a 100644 --- a/como/script/window_thumbnail_item.h +++ b/como/script/window_thumbnail_item.h @@ -18,20 +18,53 @@ SPDX-License-Identifier: GPL-2.0-or-later namespace como { -class EffectWindow; class GLFramebuffer; class GLTexture; namespace scripting { + class ThumbnailTextureProvider; +class window_thumbnail_source : public QObject +{ + Q_OBJECT + +public: + window_thumbnail_source(QQuickWindow* view, scripting::window* handle, QUuid wId); + ~window_thumbnail_source() override; + + static std::shared_ptr + getOrCreate(QQuickWindow* window, scripting::window* handle, QUuid wId); + + struct Frame { + std::shared_ptr texture; + GLsync fence; + }; + + Frame acquire(); + +Q_SIGNALS: + void changed(); + +private: + void update(como::effect::screen_paint_data& data); + + QPointer m_view; + QPointer m_handle; + + std::shared_ptr m_offscreenTexture; + std::unique_ptr m_offscreenTarget; + GLsync m_acquireFence{nullptr}; + bool m_dirty = true; + QUuid wId; +}; + class COMO_EXPORT window_thumbnail_item : public QQuickItem { Q_OBJECT Q_PROPERTY(QUuid wId READ wId WRITE setWId NOTIFY wIdChanged) Q_PROPERTY(como::scripting::window* client READ client WRITE setClient NOTIFY clientChanged) - Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize NOTIFY sourceSizeChanged) public: explicit window_thumbnail_item(QQuickItem* parent = nullptr); @@ -43,9 +76,6 @@ class COMO_EXPORT window_thumbnail_item : public QQuickItem scripting::window* client() const; void setClient(scripting::window* window); - QSize sourceSize() const; - void setSourceSize(const QSize& sourceSize); - QSGTextureProvider* textureProvider() const override; bool isTextureProvider() const override; QSGNode* updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData*) override; @@ -56,30 +86,19 @@ class COMO_EXPORT window_thumbnail_item : public QQuickItem Q_SIGNALS: void wIdChanged(); void clientChanged(); - void sourceSizeChanged(); private: - bool use_gl_thumbnails() const; QImage fallbackImage() const; QRectF paintedRect() const; - void invalidateOffscreenTexture(); - void updateOffscreenTexture(como::effect::screen_paint_data& data); - void destroyOffscreenTexture(); void updateImplicitSize(); - void update_render_notifier(); + void update_source(); + void reset_source(); - QSize m_sourceSize; QUuid m_wId; QPointer m_client; - bool m_dirty = false; mutable ThumbnailTextureProvider* m_provider = nullptr; - QSharedPointer m_offscreenTexture; - QScopedPointer m_offscreenTarget; - GLsync m_acquireFence{nullptr}; - qreal m_devicePixelRatio = 1; - - QMetaObject::Connection render_notifier; + std::shared_ptr m_source; }; } From 90e20234d02d1a725ca0c8611bf5bf44288b5a8b Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Thu, 22 Feb 2024 23:48:20 +0100 Subject: [PATCH 15/76] build: bump Qt version requirement to 6.6.0 In lockstep with KWin. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63171670e..7b337666e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.23 FATAL_ERROR) project("The Compositor Modules" VERSION 0.1.0) -set(QT_MIN_VERSION "6.4.0") +set(QT_MIN_VERSION "6.6.0") set(KF6_MIN_VERSION "5.240.0") set(KDE_PLASMA_VERSION "5.27") From 49606e1576dc4e891775aeb6a4ae3315d2c233b7 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 7 Dec 2023 22:44:58 +0200 Subject: [PATCH 16/76] perf: load screen delegate asynchronously The compilation step still takes a significant amount of time on the first launch. On my machine, it's around 1s. Even if it happens just once, freezing the session for 1 second is not great. This change makes the overview effect load main.qml asynchronously when plasma session starts. By the time the session is loaded, it should be ready. CCBUG: 455780 --- como/render/effect/interface/quick_scene.cpp | 4 ++++ plugins/effects/overview/overvieweffect.cpp | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/como/render/effect/interface/quick_scene.cpp b/como/render/effect/interface/quick_scene.cpp index ee8fffaec..ec7a170e8 100644 --- a/como/render/effect/interface/quick_scene.cpp +++ b/como/render/effect/interface/quick_scene.cpp @@ -506,6 +506,10 @@ void QuickSceneEffect::startInternal() Q_EMIT delegateChanged(); } + if (!d->delegate->isReady()) { + return; + } + effects->setActiveFullScreenEffect(this); d->running = true; diff --git a/plugins/effects/overview/overvieweffect.cpp b/plugins/effects/overview/overvieweffect.cpp index 631f9a5f5..2a9e40ae4 100644 --- a/plugins/effects/overview/overvieweffect.cpp +++ b/plugins/effects/overview/overvieweffect.cpp @@ -199,7 +199,15 @@ OverviewEffect::OverviewEffect() OverviewConfig::instance(effects->config()); reconfigure(ReconfigureAll); - setSource(QUrl(QStringLiteral("qrc:/overview/qml/main.qml"))); + auto delegate = new QQmlComponent(effects->qmlEngine()); + connect(delegate, &QQmlComponent::statusChanged, this, [delegate]() { + if (delegate->isError()) { + qWarning() << "Failed to load overview:" << delegate->errorString(); + } + }); + delegate->loadUrl(QUrl(QStringLiteral("qrc:/overview/qml/main.qml")), + QQmlComponent::Asynchronous); + setDelegate(delegate); } OverviewEffect::~OverviewEffect() From eead865c5485a44b113e64da85541a9c50337c83 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Fri, 8 Dec 2023 17:56:17 +0100 Subject: [PATCH 17/76] fix(plugin): disable acessibility integration on Wayland It causes hangs, and thus doesn't actually help with accessibility but makes it worse. Until that's fixed, it needs to stay disabled BUG: 450940 --- plugins/effects/zoom/zoom.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/plugins/effects/zoom/zoom.cpp b/plugins/effects/zoom/zoom.cpp index 10b5cf315..606308986 100644 --- a/plugins/effects/zoom/zoom.cpp +++ b/plugins/effects/zoom/zoom.cpp @@ -110,11 +110,14 @@ ZoomEffect::ZoomEffect() connect(effects, &EffectsHandler::screenRemoved, this, &ZoomEffect::slotScreenRemoved); #if HAVE_ACCESSIBILITY - m_accessibilityIntegration = new ZoomAccessibilityIntegration(this); - connect(m_accessibilityIntegration, - &ZoomAccessibilityIntegration::focusPointChanged, - this, - &ZoomEffect::moveFocus); + if (!effects->waylandDisplay()) { + // on Wayland, the accessibility integration can cause KWin to hang + m_accessibilityIntegration = new ZoomAccessibilityIntegration(this); + connect(m_accessibilityIntegration, + &ZoomAccessibilityIntegration::focusPointChanged, + this, + &ZoomEffect::moveFocus); + } #endif auto const windows = effects->stackingOrder(); @@ -138,7 +141,7 @@ ZoomEffect::~ZoomEffect() bool ZoomEffect::isFocusTrackingEnabled() const { #if HAVE_ACCESSIBILITY - return m_accessibilityIntegration->isFocusTrackingEnabled(); + return m_accessibilityIntegration && m_accessibilityIntegration->isFocusTrackingEnabled(); #else return false; #endif @@ -147,7 +150,7 @@ bool ZoomEffect::isFocusTrackingEnabled() const bool ZoomEffect::isTextCaretTrackingEnabled() const { #if HAVE_ACCESSIBILITY - return m_accessibilityIntegration->isTextCaretTrackingEnabled(); + return m_accessibilityIntegration && m_accessibilityIntegration->isTextCaretTrackingEnabled(); #else return false; #endif @@ -218,10 +221,13 @@ void ZoomEffect::reconfigure(ReconfigureFlags) // Track moving of the mouse. mouseTracking = MouseTrackingType(ZoomConfig::mouseTracking()); #if HAVE_ACCESSIBILITY - // Enable tracking of the focused location. - m_accessibilityIntegration->setFocusTrackingEnabled(ZoomConfig::enableFocusTracking()); - // Enable tracking of the text caret. - m_accessibilityIntegration->setTextCaretTrackingEnabled(ZoomConfig::enableTextCaretTracking()); + if (m_accessibilityIntegration) { + // Enable tracking of the focused location. + m_accessibilityIntegration->setFocusTrackingEnabled(ZoomConfig::enableFocusTracking()); + // Enable tracking of the text caret. + m_accessibilityIntegration->setTextCaretTrackingEnabled( + ZoomConfig::enableTextCaretTracking()); + } #endif // The time in milliseconds to wait before a focus-event takes away a mouse-move. focusDelay = qMax(uint(0), ZoomConfig::focusDelay()); From 4a7aabdfb05282fa1290fcf89b142e06e4057819 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Fri, 8 Dec 2023 22:55:32 +0000 Subject: [PATCH 18/76] perf: avoid additional FBO rendering the blurred background WindowThumbnails are image providers meaning it can provide textures directly to ShaderEffects layer.enabled is effectively a ShaderEffectSource meaning we render our single desktop background item into an offscreen texture to ultimately contain our desktop background. We can cut out the middle-man and use the DesktopBackground as a source directly saving a massive texture being generated. --- plugins/effects/overview/qml/main.qml | 42 ++++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index 6522d4062..9548c3d99 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -242,14 +242,21 @@ FocusScope { } Keys.priority: Keys.AfterItem - KWinComponents.DesktopBackground { - id: backgroundItem - activity: KWinComponents.Workspace.currentActivity - desktop: KWinComponents.Workspace.currentDesktop - outputName: targetScreen.name - - layer.enabled: true - layer.effect: FastBlur {radius: 64} + Item { + width: backgroundItem.width + height: backgroundItem.height + KWinComponents.DesktopBackground { + id: backgroundItem + activity: KWinComponents.Workspace.currentActivity + desktop: KWinComponents.Workspace.currentDesktop + outputName: targetScreen.name + visible: false + } + FastBlur { + anchors.fill: parent + source: backgroundItem + radius: 64 + } } Rectangle { @@ -515,15 +522,16 @@ FocusScope { activity: KWinComponents.Workspace.currentActivity desktop: KWinComponents.Workspace.currentDesktop outputName: targetScreen.name - - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - anchors.centerIn: parent - width: desktopElement.width - height: desktopElement.height - radius: mainBackground.radius - } + visible: false + } + OpacityMask { + anchors.fill: parent + source: desktopElement + maskSource: Rectangle { + anchors.centerIn: parent + width: desktopElement.width + height: desktopElement.height + radius: mainBackground.radius } } From c574105809b282d197f05389777b4de2efa96889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Venerandi?= Date: Tue, 12 Dec 2023 13:55:41 +0000 Subject: [PATCH 19/76] fix(plugin): make window captions in Overview 2 lines at most Captions are set to elide with ElideRight, which does allow for multi-line captions. However, this means that the height of the captions depends on their width (since a shorter width results in more lines), and the width of the captions depends on the size of the window thumbnail, which depends on the height of the caption to be positioned. If the caption is really long, this can cause visual glitches. Having a maximum line value avoids that. BUG: 477103 --- plugins/effects/private/expolayout.cpp | 12 ++++++++++-- plugins/effects/private/qml/WindowHeapDelegate.qml | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/effects/private/expolayout.cpp b/plugins/effects/private/expolayout.cpp index 49ab3f6e5..46ed230ed 100644 --- a/plugins/effects/private/expolayout.cpp +++ b/plugins/effects/private/expolayout.cpp @@ -403,7 +403,10 @@ void ExpoLayout::calculateWindowTransformationsClosest() area.y() + (slot / columns) * slotHeight, slotWidth, slotHeight); - target.adjust(m_spacing, m_spacing, -m_spacing, -m_spacing); // Borders + QRect adjustedTarget = target.adjusted(m_spacing, m_spacing, -m_spacing, -m_spacing); + if (adjustedTarget.isValid()) { + target = adjustedTarget; // Borders + } target = target.marginsRemoved(cell->margins()); qreal scale; @@ -709,7 +712,12 @@ void ExpoLayout::calculateWindowTransformationsNatural() } for (ExpoCell* cell : std::as_const(m_cells)) { - const QRect rect = centered(cell, targets.value(cell).marginsRemoved(cell->margins())); + const QRect& cellRect = targets.value(cell); + QRect cellRectWithoutMargins = cellRect.marginsRemoved(cell->margins()); + if (!cellRectWithoutMargins.isValid()) { + cellRectWithoutMargins = cellRect; + } + const QRect rect = centered(cell, cellRectWithoutMargins); cell->setX(rect.x()); cell->setY(rect.y()); diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 6b7175fbc..6e5b7ba8d 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -169,6 +169,7 @@ Item { id: caption visible: thumb.windowTitleVisible width: cell.width + maximumLineCount: 1 anchors.top: parent.bottom anchors.horizontalCenter: parent.horizontalCenter elide: Text.ElideRight From 0b71abcb44ebcf1d1b89d5cdc4181c58ac2e595f Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 11 Dec 2023 11:55:14 +0200 Subject: [PATCH 20/76] fix(plugin): load milou on demand Milou.ResultsView loads runners regardless whether the query string is empty. It's not clear what milou should do. There are valid arguments both in favor and against preloading runners. This change puts Milou.ResultsView behind a Loader so milou is loaded when it's actually needed and not when starting overview. CCBUG: 455780 --- plugins/effects/overview/qml/main.qml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index 9548c3d99..fc99dc483 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -703,28 +703,29 @@ FocusScope { } - Column { + Loader { + id: searchResults + active: effect.searchText.length > 0 && (allDesktopHeaps.currentHeap.count === 0 || !effect.filterWindows) anchors.left: container.verticalDesktopBar ? desktopBar.right : parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.top: topBar.bottom - Item { + sourceComponent: Item { width: parent.width height: parent.height - topBar.height - visible: effect.searchText.length > 0 && (allDesktopHeaps.currentHeap.count === 0 || !effect.filterWindows) opacity: overviewVal PlasmaExtras.PlaceholderMessage { id: placeholderMessage - visible: effect.searchText.length > 0 && allDesktopHeaps.currentHeap.count === 0 && effect.filterWindows + visible: allDesktopHeaps.currentHeap.count === 0 && effect.filterWindows anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter text: i18ndc("kwin", "@info:placeholder", "No matching windows") } Milou.ResultsView { - id: searchResults + id: milouView anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: parent.width / 2 @@ -735,7 +736,11 @@ FocusScope { effect.deactivate(); } } + + Keys.forwardTo: milouView } + + Keys.forwardTo: item } Repeater { From 913ecb27cacbb0519518a68dc5f66deb3e69dce0 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Tue, 12 Dec 2023 23:35:13 +0200 Subject: [PATCH 21/76] fix(plugin): cancel animations when screen is locked/unlocked BUG: 412104 --- plugins/effects/slidingpopups/slidingpopups.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/effects/slidingpopups/slidingpopups.cpp b/plugins/effects/slidingpopups/slidingpopups.cpp index 94ae651e5..016d81be9 100644 --- a/plugins/effects/slidingpopups/slidingpopups.cpp +++ b/plugins/effects/slidingpopups/slidingpopups.cpp @@ -112,6 +112,8 @@ SlidingPopupsEffect::SlidingPopupsEffect() &EffectsHandler::activeFullScreenEffectChanged, this, &SlidingPopupsEffect::stopAnimations); + connect( + effects, &EffectsHandler::screenLockingChanged, this, &SlidingPopupsEffect::stopAnimations); auto const windows = effects->stackingOrder(); for (auto window : windows) { From ee3dc8f01e0a038729352d4131735631c5329ac3 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 14 Dec 2023 12:38:13 +0100 Subject: [PATCH 22/76] refactor(plugin): replace OpacityMask with ShadowedTexture The OpacityMask is used purely for rounding the corners. We can get rid of it by using ShadowedTexture which does the same thing while also drawing the shadow, meaning we can also eliminate the ShadowedRectangle. --- plugins/effects/overview/qml/main.qml | 28 +++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index fc99dc483..94241712d 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -409,9 +409,9 @@ FocusScope { property Item currentHeap property Item currentBackgroundItem - Kirigami.ShadowedRectangle { + Item { id: mainBackground - color: Kirigami.Theme.highlightColor + visible: gridVal > 0 || nearCurrent anchors.fill: parent property bool shouldBeVisibleInOverview: !(effect.searchText.length > 0 && current) || (heap.count !== 0 && effect.filterWindows) @@ -431,13 +431,6 @@ FocusScope { z: dragActive ? 1 : 0 readonly property bool dragActive: heap.dragActive || dragHandler.active - shadow { - size: Kirigami.Units.gridUnit * 2 - color: Qt.rgba(0, 0, 0, 0.3) - yOffset: 3 - } - radius: Kirigami.Units.largeSpacing * 2 * (overviewVal + gridVal * 2) - property int gridSize: Math.max(rows, columns) property real row: (index - column) / columns property real column: index % columns @@ -524,14 +517,19 @@ FocusScope { outputName: targetScreen.name visible: false } - OpacityMask { + + Kirigami.ShadowedTexture { anchors.fill: parent + + color: Kirigami.Theme.highlightColor source: desktopElement - maskSource: Rectangle { - anchors.centerIn: parent - width: desktopElement.width - height: desktopElement.height - radius: mainBackground.radius + + radius: Kirigami.Units.largeSpacing * 2 * (overviewVal + gridVal * 2) + + shadow { + size: Kirigami.Units.gridUnit * 2 + color: Qt.rgba(0, 0, 0, 0.3) + yOffset: 3 } } From 06b0b41405b3e9f847f2b1bb1162ab376b3b6413 Mon Sep 17 00:00:00 2001 From: Yifan Zhu Date: Fri, 15 Dec 2023 23:39:09 -0800 Subject: [PATCH 23/76] fix: match Shift+Backtab against Shift+Tab When users simultaneously press Shift and Tab, the keys are sometimes registered as Shift+Tab, and sometimes as Shift+Backtab. So we need to match received Shift+Tab against shortcuts containing Shift+Backtab, and vice versa. Previously the code only checks for the first case. This commit adds checks for the second case. BUG: 438991 FIXED-IN: 6.0 --- como/win/tabbox/tabbox.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/como/win/tabbox/tabbox.h b/como/win/tabbox/tabbox.h index 69a278dd7..72b1e4a20 100644 --- a/como/win/tabbox/tabbox.h +++ b/como/win/tabbox/tabbox.h @@ -435,15 +435,17 @@ class tabbox return Steady; // Before testing the unshifted key (Ctrl+A vs. Ctrl+Shift+a etc.), see whether this is - // +Shift+Tab and check that against +Shift+Backtab (as well) + // +Shift+Tab/Backtab and test that against +Shift+Backtab/Tab as well. Qt::KeyboardModifiers mods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier | Qt::GroupSwitchModifier; mods &= keyQt; - if ((keyQt & ~mods) == Qt::Key_Tab) { - if (contains(forward, QKeyCombination(mods, Qt::Key_Backtab).toCombined())) { + if (((keyQt & ~mods) == Qt::Key_Tab) || ((keyQt & ~mods) == Qt::Key_Backtab)) { + if (contains(forward, QKeyCombination(mods, Qt::Key_Backtab).toCombined()) + || contains(forward, QKeyCombination(mods, Qt::Key_Tab))) { return Forward; } - if (contains(backward, QKeyCombination(mods, Qt::Key_Backtab).toCombined())) { + if (contains(backward, QKeyCombination(mods, Qt::Key_Backtab).toCombined()) + || contains(backward, QKeyCombination(mods, Qt::Key_Tab))) { return Backward; } } From 2d932d789911c77373384549a1bdf7b78972e3a3 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Fri, 23 Feb 2024 14:57:33 +0100 Subject: [PATCH 24/76] refactor: avoid QtDBus module include Reduces the compile time. --- como/data/update_default_rules.cpp | 4 +++- como/desktop/kde/dbus/kwin.h | 4 +++- como/input/wayland/kglobalaccel/runtime/global_accel_d.cpp | 1 + como/input/wayland/kglobalaccel/runtime/global_accel_d.h | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/como/data/update_default_rules.cpp b/como/data/update_default_rules.cpp index ec8214721..f405fa9cb 100644 --- a/como/data/update_default_rules.cpp +++ b/como/data/update_default_rules.cpp @@ -6,9 +6,11 @@ SPDX-License-Identifier: GPL-2.0-or-later // read additional window rules and add them to kwinrulesrc +#include +#include +#include #include #include -#include #include #include diff --git a/como/desktop/kde/dbus/kwin.h b/como/desktop/kde/dbus/kwin.h index 1f62299c3..4a0ee40d2 100644 --- a/como/desktop/kde/dbus/kwin.h +++ b/como/desktop/kde/dbus/kwin.h @@ -14,8 +14,10 @@ #include #include +#include +#include +#include #include -#include namespace como { diff --git a/como/input/wayland/kglobalaccel/runtime/global_accel_d.cpp b/como/input/wayland/kglobalaccel/runtime/global_accel_d.cpp index 9b39aac3e..670a9483f 100644 --- a/como/input/wayland/kglobalaccel/runtime/global_accel_d.cpp +++ b/como/input/wayland/kglobalaccel/runtime/global_accel_d.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include diff --git a/como/input/wayland/kglobalaccel/runtime/global_accel_d.h b/como/input/wayland/kglobalaccel/runtime/global_accel_d.h index 4f6b81d2e..d989963fe 100644 --- a/como/input/wayland/kglobalaccel/runtime/global_accel_d.h +++ b/como/input/wayland/kglobalaccel/runtime/global_accel_d.h @@ -7,9 +7,9 @@ #include #include +#include #include #include -#include struct KGlobalAccelDPrivate; From 647a1d5820bffe01c80701cf6f5b5bbea529cc65 Mon Sep 17 00:00:00 2001 From: Yifan Zhu Date: Mon, 18 Dec 2023 13:04:30 -0800 Subject: [PATCH 25/76] feat: change Shift+Backtab to Shift+Tab for tabbox In default shortcuts, change +Shift+Backtab to +Shift+Tab. Functionally the behavior doesn't change. But Shift+Tab is better since it is - easier to understand (not everyone knows what Backtab is) - more consistent with other shortcuts containing Shift: we use Alt+Shift+1, instead of Alt+Shift+! - more consistent with how user defined shortcuts with Shift and Tab are displayed and stored BUG: 422713 FIXED-IN: 6.0 --- como/win/tabbox/tabbox.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/como/win/tabbox/tabbox.h b/como/win/tabbox/tabbox.h index 72b1e4a20..85221af54 100644 --- a/como/win/tabbox/tabbox.h +++ b/como/win/tabbox/tabbox.h @@ -352,7 +352,7 @@ class tabbox key( s_windowsRev, [this] { slot_walk_back_through_windows(); }, - Qt::ALT | Qt::SHIFT | Qt::Key_Backtab); + Qt::ALT | Qt::SHIFT | Qt::Key_Tab); key( s_app, [this] { slot_walk_through_current_app_windows(); }, From ae2212658322472e399d162f2ecdd7532cbbd95d Mon Sep 17 00:00:00 2001 From: Yifan Zhu Date: Tue, 19 Dec 2023 19:30:42 -0800 Subject: [PATCH 26/76] fix(plugin): use correct type to match The overview effect uses the type of the dragged object to determine if a single window is dragged or the entires desktop. Commit 0ff4f84a changed the type of mainBackground, but did not update the code for matching the type. BUG: 478746 FIXED-IN: 6.0 --- plugins/effects/overview/qml/main.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index 94241712d..3e96601c9 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -409,6 +409,7 @@ FocusScope { property Item currentHeap property Item currentBackgroundItem + // Later we use its type to check whether the user is dragging a desktop or a window Item { id: mainBackground @@ -540,7 +541,7 @@ FocusScope { } onDropped: (drop) => { drop.accepted = true; - if (drag.source instanceof Kirigami.ShadowedRectangle) { + if (drag.source instanceof Item) { // dragging a desktop as a whole if (drag.source === mainBackground) { drop.action = Qt.IgnoreAction; From 8c1fc5ab4edaaf0d2ebe993bcb09df2215ec4653 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 20 Dec 2023 12:45:54 +0100 Subject: [PATCH 27/76] refactor(plugin): don't rely on item type to determine drop behavior Item type is incredibly fragile as a way to determine what was dropped, as proven by recent breakage. Instead, we can use `Drag.keys` to provide metadata about what is being dragged/dropped. --- plugins/effects/overview/qml/main.qml | 3 ++- plugins/effects/private/qml/WindowHeapDelegate.qml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index 3e96601c9..89851eb13 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -541,7 +541,7 @@ FocusScope { } onDropped: (drop) => { drop.accepted = true; - if (drag.source instanceof Item) { + if (drop.keys.includes("kwin-desktop")) { // dragging a desktop as a whole if (drag.source === mainBackground) { drop.action = Qt.IgnoreAction; @@ -607,6 +607,7 @@ FocusScope { Drag.supportedActions: Qt.MoveAction Drag.source: mainBackground Drag.hotSpot: Qt.point(width * 0.5, height * 0.5) + Drag.keys: ["kwin-desktop"] layout.mode: effect.layout focus: current diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 6e5b7ba8d..bcf9bac79 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -103,6 +103,7 @@ Item { Drag.hotSpot: Qt.point( thumb.activeDragHandler.centroid.pressPosition.x * thumb.targetScale, thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale) + Drag.keys: ["kwin-window"] onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource) onYChanged: effect.checkItemDraggedOutOfScreen(thumbSource) From 2b35fb6832cb6bb1d3e4d458224e30e2c44cf347 Mon Sep 17 00:00:00 2001 From: Yifan Zhu Date: Wed, 20 Dec 2023 09:41:51 -0800 Subject: [PATCH 28/76] docs(plugin): remove obsolete comment --- plugins/effects/overview/qml/main.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index 89851eb13..7fcb680fc 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -409,7 +409,6 @@ FocusScope { property Item currentHeap property Item currentBackgroundItem - // Later we use its type to check whether the user is dragging a desktop or a window Item { id: mainBackground From 82bf6faa923e467f4af389125d2e3b81c5046a8f Mon Sep 17 00:00:00 2001 From: Akseli Lahtinen Date: Thu, 21 Dec 2023 16:48:40 +0000 Subject: [PATCH 29/76] fix(plugin): if window is set to "skip switcher", skip it from window heap Without this, windows like xwaylandvideobridge would show up. BUG: 463542 FIXED-IN: 6.0 --- plugins/effects/private/qml/WindowHeapDelegate.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index bcf9bac79..8be975ec2 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -30,7 +30,9 @@ Item { readonly property bool presentOnCurrentDesktop: !window.desktops.length || window.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1 readonly property bool initialHidden: window.minimized || !presentOnCurrentDesktop readonly property bool activeHidden: { - if (windowHeap.showOnly === "activeClass") { + if (window.skipSwitcher) { + return true; + } else if (windowHeap.showOnly === "activeClass") { if (!KWinComponents.Workspace.activeWindow) { return true; } else { From e491654e54f2cad793219a7d26fcb882dd364acb Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Wed, 10 Jan 2024 20:56:15 +0100 Subject: [PATCH 30/76] fix: always use GL_RGBA8 in offscreen quick view GL_RGB8 isn't supported by OpenGL ES BUG: 479055 --- como/render/effect/interface/offscreen_quick_view.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/como/render/effect/interface/offscreen_quick_view.cpp b/como/render/effect/interface/offscreen_quick_view.cpp index 23eac93af..55b277ef9 100644 --- a/como/render/effect/interface/offscreen_quick_view.cpp +++ b/como/render/effect/interface/offscreen_quick_view.cpp @@ -246,7 +246,7 @@ void OffscreenQuickView::update() QOpenGLFramebufferObjectFormat fboFormat; fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - fboFormat.setInternalTextureFormat(d->m_hasAlphaChannel ? GL_RGBA8 : GL_RGB8); + fboFormat.setInternalTextureFormat(GL_RGBA8); d->m_fbo.reset(new QOpenGLFramebufferObject(nativeSize, fboFormat)); if (!d->m_fbo->isValid()) { From eac99e4d4e5497105ff6e565bfe987ae67e74e62 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 15 Jan 2024 15:35:03 +0200 Subject: [PATCH 31/76] fix(plugin): hide "Drag Down to Close" when using a pointing device BUG: 479802 --- plugins/effects/private/qml/WindowHeapDelegate.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 8be975ec2..16ff36760 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -154,7 +154,7 @@ Item { verticalAlignment: Text.AlignVCenter text: i18nd("kwin", "Drag Down To Close") opacity: thumbSource.opacity - visible: !thumb.activeHidden && activeDragHandler.active + visible: !thumb.activeHidden && touchDragHandler.active } Kirigami.Icon { From 52474a5f3154b33bfdc4251bc816e9ec0b538893 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 15 Jan 2024 12:34:56 +0200 Subject: [PATCH 32/76] fix: fix glitches in mouseclick Effect frame geometry is not properly synchronized with the quick scene geometry. BUG: 477892 --- como/render/effect/frame.cpp | 6 ++---- como/render/effect/frame.h | 3 +-- como/render/effect/interface/effect_frame.h | 2 +- plugins/effects/mouseclick/mouseclick.cpp | 3 +-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/como/render/effect/frame.cpp b/como/render/effect/frame.cpp index f2741453e..c6b5f8d33 100644 --- a/como/render/effect/frame.cpp +++ b/como/render/effect/frame.cpp @@ -250,7 +250,6 @@ effect_frame_impl::effect_frame_impl(EffectsHandler& effects, this, [this](const QRect& oldGeometry, const QRect& newGeometry) { this->effects.addRepaint(oldGeometry); - m_geometry = newGeometry; this->effects.addRepaint(newGeometry); }); } @@ -289,10 +288,9 @@ void effect_frame_impl::free() m_view->hide(); } -const QRect& effect_frame_impl::geometry() const +QRect effect_frame_impl::geometry() const { - // Can't forward to OffscreenQuickView::geometry() because we return a reference. - return m_geometry; + return m_view->geometry(); } void effect_frame_impl::setGeometry(const QRect& geometry, bool force) diff --git a/como/render/effect/frame.h b/como/render/effect/frame.h index 0e2657051..1b2f4d4d0 100644 --- a/como/render/effect/frame.h +++ b/como/render/effect/frame.h @@ -117,7 +117,7 @@ class COMO_EXPORT effect_frame_impl : public QObject, public EffectFrame void setAlignment(Qt::Alignment alignment) override; const QFont& font() const override; void setFont(const QFont& font) override; - const QRect& geometry() const override; + QRect geometry() const override; void setGeometry(const QRect& geometry, bool force = false) override; const QIcon& icon() const override; void setIcon(const QIcon& icon) override; @@ -139,7 +139,6 @@ class COMO_EXPORT effect_frame_impl : public QObject, public EffectFrame Q_DISABLE_COPY(effect_frame_impl) effect_frame_quick_scene* m_view; - QRect m_geometry; }; } diff --git a/como/render/effect/interface/effect_frame.h b/como/render/effect/interface/effect_frame.h index 1d7f374db..6c76c7830 100644 --- a/como/render/effect/interface/effect_frame.h +++ b/como/render/effect/interface/effect_frame.h @@ -53,7 +53,7 @@ class COMO_EXPORT EffectFrame virtual void setAlignment(Qt::Alignment alignment) = 0; virtual Qt::Alignment alignment() const = 0; virtual void setGeometry(const QRect& geometry, bool force = false) = 0; - virtual const QRect& geometry() const = 0; + virtual QRect geometry() const = 0; virtual void setText(const QString& text) = 0; virtual const QString& text() const = 0; diff --git a/plugins/effects/mouseclick/mouseclick.cpp b/plugins/effects/mouseclick/mouseclick.cpp index 46b9aae57..0a8bd2150 100644 --- a/plugins/effects/mouseclick/mouseclick.cpp +++ b/plugins/effects/mouseclick/mouseclick.cpp @@ -200,8 +200,7 @@ void MouseClickEffect::repaint() dirtyRegion |= QRect( click->m_pos.x() - radius, click->m_pos.y() - radius, 2 * radius, 2 * radius); if (click->m_frame) { - // we grant the plasma style 32px padding for stuff like shadows... - dirtyRegion |= click->m_frame->geometry().adjusted(-32, -32, 32, 32); + dirtyRegion |= click->m_frame->geometry(); } } effects->addRepaint(dirtyRegion); From 445a9102734df7a2d1b714db14366c4085a6f5c3 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 15 Jan 2024 12:37:26 +0200 Subject: [PATCH 33/76] perf: remove const refs in EffectFrame It's a micro optimization. --- como/render/effect/frame.cpp | 8 ++++---- como/render/effect/frame.h | 8 ++++---- como/render/effect/interface/effect_frame.h | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/como/render/effect/frame.cpp b/como/render/effect/frame.cpp index c6b5f8d33..62ef71c2f 100644 --- a/como/render/effect/frame.cpp +++ b/como/render/effect/frame.cpp @@ -273,7 +273,7 @@ void effect_frame_impl::setAlignment(Qt::Alignment alignment) m_view->setAlignment(alignment); } -const QFont& effect_frame_impl::font() const +QFont effect_frame_impl::font() const { return m_view->font(); } @@ -299,7 +299,7 @@ void effect_frame_impl::setGeometry(const QRect& geometry, bool force) m_view->setGeometry(geometry); } -const QIcon& effect_frame_impl::icon() const +QIcon effect_frame_impl::icon() const { return m_view->icon(); } @@ -314,7 +314,7 @@ void effect_frame_impl::setIcon(const QIcon& icon) } } -const QSize& effect_frame_impl::iconSize() const +QSize effect_frame_impl::iconSize() const { return m_view->iconSize(); } @@ -345,7 +345,7 @@ void effect_frame_impl::render(const QRegion& region, double opacity, double fra effects.renderOffscreenQuickView(m_view); } -const QString& effect_frame_impl::text() const +QString effect_frame_impl::text() const { return m_view->text(); } diff --git a/como/render/effect/frame.h b/como/render/effect/frame.h index 1b2f4d4d0..15041d807 100644 --- a/como/render/effect/frame.h +++ b/como/render/effect/frame.h @@ -115,16 +115,16 @@ class COMO_EXPORT effect_frame_impl : public QObject, public EffectFrame double frameOpacity = 1.0) override; Qt::Alignment alignment() const override; void setAlignment(Qt::Alignment alignment) override; - const QFont& font() const override; + QFont font() const override; void setFont(const QFont& font) override; QRect geometry() const override; void setGeometry(const QRect& geometry, bool force = false) override; - const QIcon& icon() const override; + QIcon icon() const override; void setIcon(const QIcon& icon) override; - const QSize& iconSize() const override; + QSize iconSize() const override; void setIconSize(const QSize& size) override; void setPosition(const QPoint& point) override; - const QString& text() const override; + QString text() const override; void setText(const QString& text) override; EffectFrameStyle style() const override; bool isCrossFade() const override; diff --git a/como/render/effect/interface/effect_frame.h b/como/render/effect/interface/effect_frame.h index 6c76c7830..b27c20047 100644 --- a/como/render/effect/interface/effect_frame.h +++ b/como/render/effect/interface/effect_frame.h @@ -56,16 +56,16 @@ class COMO_EXPORT EffectFrame virtual QRect geometry() const = 0; virtual void setText(const QString& text) = 0; - virtual const QString& text() const = 0; + virtual QString text() const = 0; virtual void setFont(const QFont& font) = 0; - virtual const QFont& font() const = 0; + virtual QFont font() const = 0; /** * Set the icon that will appear on the left-hand size of the frame. */ virtual void setIcon(const QIcon& icon) = 0; - virtual const QIcon& icon() const = 0; + virtual QIcon icon() const = 0; virtual void setIconSize(const QSize& size) = 0; - virtual const QSize& iconSize() const = 0; + virtual QSize iconSize() const = 0; /** * @returns The style of this EffectFrame. From 3811dd368186fb79cd6e4e76136ec9e243dd755d Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Fri, 23 Feb 2024 17:32:12 +0100 Subject: [PATCH 34/76] build: find epoxy dependency It is required by consumers. --- como-config.cmake.in | 1 + 1 file changed, 1 insertion(+) diff --git a/como-config.cmake.in b/como-config.cmake.in index 359e182d7..42af56d37 100644 --- a/como-config.cmake.in +++ b/como-config.cmake.in @@ -4,6 +4,7 @@ include(CMakeFindDependencyMacro) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/modules/") find_dependency(Wrapland) +find_dependency(epoxy) find_dependency(Pixman) find_dependency(wlroots @WLROOTS_MIN_VERSION@) find_dependency(Qt6Gui @QT_MIN_VERSION@) From 8675aa419df8e66cca441a0460936a2456f349fe Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Fri, 23 Feb 2024 17:41:56 +0100 Subject: [PATCH 35/76] refactor: use STL pointers Instead of using raw or Qt pointers. --- .../effect/interface/offscreen_quick_view.cpp | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/como/render/effect/interface/offscreen_quick_view.cpp b/como/render/effect/interface/offscreen_quick_view.cpp index 55b277ef9..cb51b05fc 100644 --- a/como/render/effect/interface/offscreen_quick_view.cpp +++ b/como/render/effect/interface/offscreen_quick_view.cpp @@ -37,15 +37,15 @@ namespace como class Q_DECL_HIDDEN OffscreenQuickView::Private { public: - QQuickWindow* m_view; - QQuickRenderControl* m_renderControl; - QScopedPointer m_offscreenSurface; - QScopedPointer m_glcontext; - QScopedPointer m_fbo; + std::unique_ptr m_view; + std::unique_ptr m_renderControl; + std::unique_ptr m_offscreenSurface; + std::unique_ptr m_glcontext; + std::unique_ptr m_fbo; QTimer* m_repaintTimer; QImage m_image; - QScopedPointer m_textureExport; + std::unique_ptr m_textureExport; // if we should capture a QImage after rendering into our BO. // Used for either software QtQuick rendering and nonGL kwin rendering bool m_useBlit = false; @@ -71,16 +71,16 @@ class Q_DECL_HIDDEN OffscreenQuickScene::Private { } - QScopedPointer qmlComponent; - QScopedPointer quickItem; + std::unique_ptr qmlComponent; + std::unique_ptr quickItem; }; OffscreenQuickView::OffscreenQuickView(ExportMode exportMode, bool alpha) : d(new OffscreenQuickView::Private) { - d->m_renderControl = new QQuickRenderControl(); + d->m_renderControl = std::make_unique(); - d->m_view = new QQuickWindow(d->m_renderControl); + d->m_view = std::make_unique(d->m_renderControl.get()); Q_ASSERT(d->m_view->setProperty("_KWIN_WINDOW_IS_OFFSCREEN", true) || true); d->m_view->setFlags(Qt::FramelessWindowHint); d->m_view->setColor(Qt::transparent); @@ -125,7 +125,7 @@ OffscreenQuickView::OffscreenQuickView(ExportMode exportMode, bool alpha) d->m_offscreenSurface->setFormat(d->m_glcontext->format()); d->m_offscreenSurface->create(); - d->m_glcontext->makeCurrent(d->m_offscreenSurface.data()); + d->m_glcontext->makeCurrent(d->m_offscreenSurface.get()); d->m_view->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(d->m_glcontext.get())); d->m_renderControl->initialize(); d->m_glcontext->doneCurrent(); @@ -141,19 +141,19 @@ OffscreenQuickView::OffscreenQuickView(ExportMode exportMode, bool alpha) auto updateSize = [this]() { contentItem()->setSize(d->m_view->size()); }; updateSize(); - connect(d->m_view, &QWindow::widthChanged, this, updateSize); - connect(d->m_view, &QWindow::heightChanged, this, updateSize); + connect(d->m_view.get(), &QWindow::widthChanged, this, updateSize); + connect(d->m_view.get(), &QWindow::heightChanged, this, updateSize); d->m_repaintTimer = new QTimer(this); d->m_repaintTimer->setSingleShot(true); d->m_repaintTimer->setInterval(10); connect(d->m_repaintTimer, &QTimer::timeout, this, &OffscreenQuickView::update); - connect(d->m_renderControl, + connect(d->m_renderControl.get(), &QQuickRenderControl::renderRequested, this, &OffscreenQuickView::handleRenderRequested); - connect(d->m_renderControl, + connect(d->m_renderControl.get(), &QQuickRenderControl::sceneChanged, this, &OffscreenQuickView::handleSceneChanged); @@ -169,25 +169,22 @@ OffscreenQuickView::OffscreenQuickView(ExportMode exportMode, bool alpha) OffscreenQuickView::~OffscreenQuickView() { - disconnect(d->m_renderControl, + disconnect(d->m_renderControl.get(), &QQuickRenderControl::renderRequested, this, &OffscreenQuickView::handleRenderRequested); - disconnect(d->m_renderControl, + disconnect(d->m_renderControl.get(), &QQuickRenderControl::sceneChanged, this, &OffscreenQuickView::handleSceneChanged); if (d->m_glcontext) { // close the view whilst we have an active GL context - d->m_glcontext->makeCurrent(d->m_offscreenSurface.data()); + d->m_glcontext->makeCurrent(d->m_offscreenSurface.get()); } - delete d->m_renderControl; // Always delete render control first. - d->m_renderControl = nullptr; - - delete d->m_view; - d->m_view = nullptr; + d->m_view.reset(); + d->m_renderControl.reset(); } bool OffscreenQuickView::automaticRepaint() const @@ -235,13 +232,13 @@ void OffscreenQuickView::update() bool usingGl = d->m_glcontext != nullptr; if (usingGl) { - if (!d->m_glcontext->makeCurrent(d->m_offscreenSurface.data())) { + if (!d->m_glcontext->makeCurrent(d->m_offscreenSurface.get())) { // probably a context loss event, kwin is about to reset all the effects anyway return; } const QSize nativeSize = d->m_view->size() * d->m_view->effectiveDevicePixelRatio(); - if (d->m_fbo.isNull() || d->m_fbo->size() != nativeSize) { + if (!d->m_fbo || d->m_fbo->size() != nativeSize) { d->m_textureExport.reset(nullptr); QOpenGLFramebufferObjectFormat fboFormat; @@ -298,7 +295,7 @@ void OffscreenQuickView::forwardMouseEvent(QEvent* e) const QPoint widgetPos = d->m_view->mapFromGlobal(me->pos()); QMouseEvent cloneEvent( me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers()); - QCoreApplication::sendEvent(d->m_view, &cloneEvent); + QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent); e->setAccepted(cloneEvent.isAccepted()); if (e->type() == QEvent::MouseButtonPress) { @@ -317,7 +314,7 @@ void OffscreenQuickView::forwardMouseEvent(QEvent* e) me->button(), me->buttons(), me->modifiers()); - QCoreApplication::sendEvent(d->m_view, &doubleClickEvent); + QCoreApplication::sendEvent(d->m_view.get(), &doubleClickEvent); } } @@ -330,7 +327,7 @@ void OffscreenQuickView::forwardMouseEvent(QEvent* e) auto const widgetPos = d->m_view->mapFromGlobal(he->position()); const QPointF oldWidgetPos = d->m_view->mapFromGlobal(he->oldPos()); QHoverEvent cloneEvent(he->type(), widgetPos, widgetPos, oldWidgetPos, he->modifiers()); - QCoreApplication::sendEvent(d->m_view, &cloneEvent); + QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent); e->setAccepted(cloneEvent.isAccepted()); return; } @@ -345,7 +342,7 @@ void OffscreenQuickView::forwardMouseEvent(QEvent* e) we->modifiers(), we->phase(), we->inverted()); - QCoreApplication::sendEvent(d->m_view, &cloneEvent); + QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent); e->setAccepted(cloneEvent.isAccepted()); return; } @@ -359,7 +356,7 @@ void OffscreenQuickView::forwardKeyEvent(QKeyEvent* keyEvent) if (!d->m_visible) { return; } - QCoreApplication::sendEvent(d->m_view, keyEvent); + QCoreApplication::sendEvent(d->m_view.get(), keyEvent); } bool OffscreenQuickView::forwardTouchDown(qint32 id, const QPointF& pos, quint32 time) @@ -369,7 +366,7 @@ bool OffscreenQuickView::forwardTouchDown(qint32 id, const QPointF& pos, quint32 d->updateTouchState(Qt::TouchPointPressed, id, pos); QTouchEvent event(QEvent::TouchBegin, d->touchDevice, Qt::NoModifier, d->touchPoints); - QCoreApplication::sendEvent(d->m_view, &event); + QCoreApplication::sendEvent(d->m_view.get(), &event); return event.isAccepted(); } @@ -381,7 +378,7 @@ bool OffscreenQuickView::forwardTouchMotion(qint32 id, const QPointF& pos, quint d->updateTouchState(Qt::TouchPointMoved, id, pos); QTouchEvent event(QEvent::TouchUpdate, d->touchDevice, Qt::NoModifier, d->touchPoints); - QCoreApplication::sendEvent(d->m_view, &event); + QCoreApplication::sendEvent(d->m_view.get(), &event); return event.isAccepted(); } @@ -393,7 +390,7 @@ bool OffscreenQuickView::forwardTouchUp(qint32 id, quint32 time) d->updateTouchState(Qt::TouchPointReleased, id, QPointF{}); QTouchEvent event(QEvent::TouchEnd, d->touchDevice, Qt::NoModifier, d->touchPoints); - QCoreApplication::sendEvent(d->m_view, &event); + QCoreApplication::sendEvent(d->m_view.get(), &event); return event.isAccepted(); } @@ -425,7 +422,7 @@ QQuickItem* OffscreenQuickView::contentItem() const QQuickWindow* OffscreenQuickView::window() const { - return d->m_view; + return d->m_view.get(); } void OffscreenQuickView::setVisible(bool visible) @@ -474,7 +471,7 @@ GLTexture* OffscreenQuickView::bufferAsTexture() d->m_fbo->texture(), d->m_fbo->format().internalTextureFormat(), d->m_fbo->size())); } } - return d->m_textureExport.data(); + return d->m_textureExport.get(); } QImage OffscreenQuickView::bufferAsImage() const @@ -497,7 +494,7 @@ void OffscreenQuickView::setGeometry(const QRect& rect) void OffscreenQuickView::Private::releaseResources() { if (m_glcontext) { - m_glcontext->makeCurrent(m_offscreenSurface.data()); + m_glcontext->makeCurrent(m_offscreenSurface.get()); m_view->releaseResources(); m_glcontext->doneCurrent(); } else { @@ -633,7 +630,7 @@ void OffscreenQuickScene::setSource(const QUrl& source, const QVariantMap& initi QQuickItem* OffscreenQuickScene::rootItem() const { - return d->quickItem.data(); + return d->quickItem.get(); } } From 342580a42350b813ffaabd5a97519bb4dee1e5b1 Mon Sep 17 00:00:00 2001 From: David Redondo Date: Tue, 16 Jan 2024 13:28:01 +0100 Subject: [PATCH 36/76] fix: make sure window thumbnails and Qt Quick resources are destroyed properly Drops the doneCurrent as it was preventing proper cleanUp because no context was current when textures were deleted. Also avoid manipulating the context when Qt has the current one, as various Qt classes have guards around their cleanup handlers which rely on a current Qt context. Despite the comment the order of render control and view destruction needs to be switched as the QQuickWindow destructor calls into the render control to notify if of window destruction. BUG:478770 BUG:479846 FIXED-IN:6.0 --- como/script/window_thumbnail_item.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/como/script/window_thumbnail_item.cpp b/como/script/window_thumbnail_item.cpp index 7a017971b..460885c4a 100644 --- a/como/script/window_thumbnail_item.cpp +++ b/como/script/window_thumbnail_item.cpp @@ -18,6 +18,7 @@ SPDX-License-Identifier: GPL-2.0-or-later #include #include +#include #include #include #include @@ -61,16 +62,17 @@ window_thumbnail_source::~window_thumbnail_source() if (!m_offscreenTexture) { return; } - if (effects) { + + if (!QOpenGLContext::currentContext()) { effects->makeOpenGLContextCurrent(); - m_offscreenTarget.reset(); - m_offscreenTexture.reset(); + } - if (m_acquireFence) { - glDeleteSync(m_acquireFence); - m_acquireFence = nullptr; - } - effects->doneOpenGLContextCurrent(); + m_offscreenTarget.reset(); + m_offscreenTexture.reset(); + + if (m_acquireFence) { + glDeleteSync(m_acquireFence); + m_acquireFence = nullptr; } } From 12f3e4cf5a2d5e8a1ba77f9787c84f1614626f37 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Fri, 15 Dec 2023 16:21:16 +0100 Subject: [PATCH 37/76] fix(plugin): retarget fullscreen animation instead of restarting it BUG: 474488 FIXED-IN: 6.0 --- .../fullscreen/package/contents/code/main.js | 74 ++++++++++++------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/plugins/effects/fullscreen/package/contents/code/main.js b/plugins/effects/fullscreen/package/contents/code/main.js index 7baae3d77..25099fb42 100644 --- a/plugins/effects/fullscreen/package/contents/code/main.js +++ b/plugins/effects/fullscreen/package/contents/code/main.js @@ -41,33 +41,55 @@ class FullScreenEffect { oldGeometry = window.olderGeometry; window.olderGeometry = Object.assign({}, window.oldGeometry); window.oldGeometry = Object.assign({}, newGeometry); - window.fullScreenAnimation1 = animate({ - window: window, - duration: this.duration, - animations: [{ - type: Effect.Size, - to: { + + let couldRetarget = false; + if (window.fullScreenAnimation1) { + if (window.fullScreenAnimation1[0]) { + couldRetarget = retarget(window.fullScreenAnimation1[0], { value1: newGeometry.width, value2: newGeometry.height - }, - from: { - value1: oldGeometry.width, - value2: oldGeometry.height - }, - curve: QEasingCurve.OutCubic - }, { - type: Effect.Translation, - to: { - value1: 0, - value2: 0 - }, - from: { - value1: oldGeometry.x - newGeometry.x - (newGeometry.width / 2 - oldGeometry.width / 2), - value2: oldGeometry.y - newGeometry.y - (newGeometry.height / 2 - oldGeometry.height / 2) - }, - curve: QEasingCurve.OutCubic - }] - }); + }, this.duration); + } + if (window.fullScreenAnimation1[1]) { + couldRetarget = retarget(window.fullScreenAnimation1[1], { + value1: newGeometry.x + newGeometry.width / 2, + value2: newGeometry.y + newGeometry.height / 2 + }, this.duration); + } + } + if (!couldRetarget) { + if (window.fullScreenAnimation1) { + cancel(window.fullScreenAnimation1); + delete window.fullScreenAnimation1; + } + window.fullScreenAnimation1 = animate({ + window: window, + duration: this.duration, + animations: [{ + type: Effect.Size, + to: { + value1: newGeometry.width, + value2: newGeometry.height + }, + from: { + value1: oldGeometry.width, + value2: oldGeometry.height + }, + curve: QEasingCurve.OutCubic + }, { + type: Effect.Position, + to: { + value1: newGeometry.x + newGeometry.width / 2, + value2: newGeometry.y + newGeometry.height / 2 + }, + from: { + value1: oldGeometry.x + oldGeometry.width / 2, + value2: oldGeometry.y + oldGeometry.height / 2 + }, + curve: QEasingCurve.OutCubic + }] + }); + } if (!window.resize) { window.fullScreenAnimation2 =animate({ window: window, @@ -90,8 +112,6 @@ class FullScreenEffect { if (window.fullScreenAnimation1) { if (window.geometry.width != window.oldGeometry.width || window.geometry.height != window.oldGeometry.height) { - cancel(window.fullScreenAnimation1); - delete window.fullScreenAnimation1; if (window.fullScreenAnimation2) { cancel(window.fullScreenAnimation2); delete window.fullScreenAnimation2; From a8bb62f1cdb12286116c2767c385cba9061e317a Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 18 Jan 2024 14:56:33 +0000 Subject: [PATCH 38/76] fix(plugin): sse SmoothPixmapTransform when stitching area screenshots CCBUG: 478426 --- plugins/effects/screenshot/screenshot.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/effects/screenshot/screenshot.cpp b/plugins/effects/screenshot/screenshot.cpp index 1bb080f94..b3619d150 100644 --- a/plugins/effects/screenshot/screenshot.cpp +++ b/plugins/effects/screenshot/screenshot.cpp @@ -332,6 +332,7 @@ bool ScreenShotEffect::takeScreenShot(effect::render_data& render_data, screenshot->area.size() * screenshot->result.devicePixelRatio()); QPainter painter(&screenshot->result); + painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.setWindow(nativeArea); painter.drawImage(sourceRect, snapshot); painter.end(); From a9fd142bb43ebcc6a3d176d37bebfae4e67434af Mon Sep 17 00:00:00 2001 From: Yifan Zhu Date: Fri, 19 Jan 2024 08:03:54 -0800 Subject: [PATCH 39/76] fix(plugin): use InOutCubic easing All transitions correspond to visible to visible changes, and should have InOutCubic easing as per HIG. --- plugins/effects/private/qml/WindowHeapDelegate.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/effects/private/qml/WindowHeapDelegate.qml b/plugins/effects/private/qml/WindowHeapDelegate.qml index 16ff36760..77d6ba92a 100644 --- a/plugins/effects/private/qml/WindowHeapDelegate.qml +++ b/plugins/effects/private/qml/WindowHeapDelegate.qml @@ -86,7 +86,7 @@ Item { enabled: thumb.state === "active-normal" && thumb.windowHeap.animationEnabled && thumb.animationEnabled && !thumb.activeDragHandler.active NumberAnimation { duration: thumb.windowHeap.animationDuration - easing.type: Easing.OutCubic + easing.type: Easing.InOutCubic } } @@ -337,7 +337,7 @@ Item { NumberAnimation { duration: thumb.windowHeap.animationDuration properties: "x, y, width, height" - easing.type: Easing.OutCubic + easing.type: Easing.InOutCubic } } ] From 0c31875a3fb7e0805f367dc16297b9c007411062 Mon Sep 17 00:00:00 2001 From: Yifan Zhu Date: Fri, 19 Jan 2024 09:32:40 -0800 Subject: [PATCH 40/76] fix(plugin): use InCubic easing This visual effect corresponds to visible to invisible. So use InCubic easing for opacity and speed. --- plugins/effects/fallapart/fallapart.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/effects/fallapart/fallapart.cpp b/plugins/effects/fallapart/fallapart.cpp index 5e38c8841..c0ca56c5d 100644 --- a/plugins/effects/fallapart/fallapart.cpp +++ b/plugins/effects/fallapart/fallapart.cpp @@ -13,7 +13,9 @@ SPDX-License-Identifier: GPL-2.0-or-later #include #include +#include #include + #include Q_LOGGING_CATEGORY(KWIN_FALLAPART, "kwin_effect_fallapart", QtWarningMsg) @@ -76,7 +78,8 @@ void FallApartEffect::apply(effect::window_paint_data& data, WindowQuadList& qua { auto animationIt = windows.constFind(&data.window); if (animationIt != windows.constEnd() && isRealWindow(&data.window)) { - const qreal t = animationIt->progress; + QEasingCurve easing(QEasingCurve::InCubic); + auto const t = easing.valueForProgress(animationIt->progress); // Request the window to be divided into cells quads = quads.makeGrid(blockSize); @@ -101,8 +104,7 @@ void FallApartEffect::apply(effect::window_paint_data& data, WindowQuadList& qua if (p1.y() > data.window.height() / 2) { ydiff = (p1.y() - data.window.height() / 2) / data.window.height() * 100; } - - double modif = t * t * 64; + double modif = t * 64; srandom(cnt); // change direction randomly but consistently xdiff += (rand() % 21 - 10); ydiff += (rand() % 21 - 10); From 219c16a351fb3794624ec065e2623354294a1a4c Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Tue, 23 Jan 2024 10:05:01 +0200 Subject: [PATCH 41/76] fix: update kconf_update version kconf_expects version 6. --- kconf_update/kwin.upd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kconf_update/kwin.upd b/kconf_update/kwin.upd index e726be9cb..adc2cdcf4 100644 --- a/kconf_update/kwin.upd +++ b/kconf_update/kwin.upd @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2023 Niccolò Venerandi # SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only -Version=5 +Version=6 # Changes the default Activities shortcut from Meta+Tab to Meta+A, # so that the Overview can take its place From fd5d8bbd50602c58409b9820cc62fa8ae432235b Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Wed, 24 Jan 2024 01:51:49 +0100 Subject: [PATCH 42/76] fix: consider Qt::KeypadModifier relevant for global shortcuts Otherwise kglobalaccel can't distinguish between numbers on the num block and other numbers. BUG: 453423 BUG: 446389 --- como/input/xkb/keyboard.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/como/input/xkb/keyboard.cpp b/como/input/xkb/keyboard.cpp index b78225c05..1fbf7fca8 100644 --- a/como/input/xkb/keyboard.cpp +++ b/como/input/xkb/keyboard.cpp @@ -268,6 +268,9 @@ Qt::KeyboardModifiers keyboard::modifiers_relevant_for_global_shortcuts(uint32_t if (is_active(state, modifiers_indices.meta, XKB_STATE_MODS_EFFECTIVE) == 1) { mods |= Qt::MetaModifier; } + if (keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_9) { + mods |= Qt::KeypadModifier; + } auto consumed_mods = qt_modifiers_consumed; if ((mods & Qt::ShiftModifier) && (consumed_mods == Qt::ShiftModifier)) { From c320e86b2944cf4ce1ef939dc1ed05a4c977a5f4 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Fri, 23 Feb 2024 18:13:40 +0100 Subject: [PATCH 43/76] test: add keypad global shortcuts section Similar to [1] to test keypad keys with global shortcuts. [1] https://invent.kde.org/plasma/kwin/-/commit/d6202dcc --- autotests/integration/global_shortcuts.cpp | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/autotests/integration/global_shortcuts.cpp b/autotests/integration/global_shortcuts.cpp index a774bb1e9..4cede6df7 100644 --- a/autotests/integration/global_shortcuts.cpp +++ b/autotests/integration/global_shortcuts.cpp @@ -257,6 +257,52 @@ TEST_CASE("global shortcuts", "[input]") QTRY_COMPARE(triggeredSpy.count(), 0); } + SECTION("keypad") + { + auto zero_action = std::make_unique(); + zero_action->setProperty("componentName", QStringLiteral("kwin")); + zero_action->setObjectName(QStringLiteral("globalshortcuts-test-keypad-0")); + + QSignalSpy zero_action_spy(zero_action.get(), &QAction::triggered); + KGlobalAccel::self()->setShortcut( + zero_action.get(), + QList{Qt::MetaModifier | Qt::KeypadModifier | Qt::Key_0}, + KGlobalAccel::NoAutoloading); + + auto insert_action = std::make_unique(); + insert_action->setProperty("componentName", QStringLiteral("kwin")); + insert_action->setObjectName(QStringLiteral("globalshortcuts-test-keypad-ins")); + + QSignalSpy insert_action_spy(insert_action.get(), &QAction::triggered); + KGlobalAccel::self()->setShortcut( + insert_action.get(), + QList{Qt::MetaModifier | Qt::KeypadModifier | Qt::Key_Insert}, + KGlobalAccel::NoAutoloading); + + // Turn on numlock + quint32 timestamp = 0; + keyboard_key_pressed(KEY_NUMLOCK, timestamp++); + keyboard_key_released(KEY_NUMLOCK, timestamp++); + + keyboard_key_pressed(KEY_LEFTMETA, timestamp++); + keyboard_key_pressed(KEY_KP0, timestamp++); + keyboard_key_released(KEY_KP0, timestamp++); + keyboard_key_released(KEY_LEFTMETA, timestamp++); + TRY_REQUIRE(zero_action_spy.size() == 1); + REQUIRE(insert_action_spy.empty()); + + // Turn off numlock + keyboard_key_pressed(KEY_NUMLOCK, timestamp++); + keyboard_key_released(KEY_NUMLOCK, timestamp++); + + keyboard_key_pressed(KEY_LEFTMETA, timestamp++); + keyboard_key_pressed(KEY_KP0, timestamp++); + keyboard_key_released(KEY_KP0, timestamp++); + keyboard_key_released(KEY_LEFTMETA, timestamp++); + TRY_REQUIRE(insert_action_spy.size() == 1); + REQUIRE(zero_action_spy.size() == 1); + } + SECTION("x11 window shortcut") { auto c = xcb_connection_create(); From 5b32495f2b0c71eb6e142d6577c9f9f1d2671d29 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Wed, 24 Jan 2024 16:46:15 +0200 Subject: [PATCH 44/76] feat: add script to drop old desktop switching shortcuts BUG: 479984 --- kconf_update/CMakeLists.txt | 5 +++ ...6.0-delete-desktop-switching-shortcuts.cpp | 33 +++++++++++++++++++ kconf_update/kwin.upd | 4 +++ 3 files changed, 42 insertions(+) create mode 100644 kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp diff --git a/kconf_update/CMakeLists.txt b/kconf_update/CMakeLists.txt index 7cae94248..da7d0a2ee 100644 --- a/kconf_update/CMakeLists.txt +++ b/kconf_update/CMakeLists.txt @@ -1,6 +1,11 @@ # SPDX-FileCopyrightText: 2023 Niccolò Venerandi # SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only +add_executable(kwin-6.0-delete-desktop-switching-shortcuts) +target_sources(kwin-6.0-delete-desktop-switching-shortcuts PRIVATE kwin-6.0-delete-desktop-switching-shortcuts.cpp) +target_link_libraries(kwin-6.0-delete-desktop-switching-shortcuts PRIVATE KF6::GlobalAccel) +install(TARGETS kwin-6.0-delete-desktop-switching-shortcuts DESTINATION ${KDE_INSTALL_LIBDIR}/kconf_update_bin/) + install(FILES kwin.upd DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) install(PROGRAMS kwin-6.0-overview-activities-shortcuts.py diff --git a/kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp b/kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp new file mode 100644 index 000000000..5aba1bab7 --- /dev/null +++ b/kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp @@ -0,0 +1,33 @@ +/* + SPDX-FileCopyrightText: 2023 Marco Martin + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + const QStringList actionNames{ + QStringLiteral("Walk Through Desktops"), + QStringLiteral("Walk Through Desktops (Reverse)"), + QStringLiteral("Walk Through Desktop List"), + QStringLiteral("Walk Through Desktop List (Reverse)"), + }; + + for (const QString &actionName : actionNames) { + QAction action; + action.setObjectName(actionName); + action.setProperty("componentName", QStringLiteral("kwin")); + KGlobalAccel::self()->setShortcut(&action, {QKeySequence()}, KGlobalAccel::NoAutoloading); + KGlobalAccel::self()->removeAllShortcuts(&action); + } + + return 0; +} diff --git a/kconf_update/kwin.upd b/kconf_update/kwin.upd index adc2cdcf4..573b28fc5 100644 --- a/kconf_update/kwin.upd +++ b/kconf_update/kwin.upd @@ -3,6 +3,10 @@ Version=6 +# Delete old desktop switching shortcuts. +Id=kwin-6.0-delete-desktop-switching-shortcuts +Script=kwin-6.0-delete-desktop-switching-shortcuts + # Changes the default Activities shortcut from Meta+Tab to Meta+A, # so that the Overview can take its place Id=change-activities-overview-shortcuts From 4e71b931e504649afacea04b0040e1bcd4722904 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 25 Jan 2024 15:10:18 +0200 Subject: [PATCH 45/76] perf(plugin): unset PAINT_SCREEN_TRANSFORMED in zoom It's not needed because of the offscreen texture. BUG: 480216 --- plugins/effects/zoom/zoom.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/effects/zoom/zoom.cpp b/plugins/effects/zoom/zoom.cpp index 606308986..3d348833b 100644 --- a/plugins/effects/zoom/zoom.cpp +++ b/plugins/effects/zoom/zoom.cpp @@ -266,7 +266,6 @@ void ZoomEffect::prePaintScreen(effect::screen_prepaint_data& data) showCursor(); } else { hideCursor(); - data.paint.mask |= PAINT_SCREEN_TRANSFORMED; } effects->prePaintScreen(data); From da475c5bd099d0318f1406c24cb9dcc02f0f5315 Mon Sep 17 00:00:00 2001 From: Fushan Wen Date: Thu, 25 Jan 2024 23:05:33 +0800 Subject: [PATCH 46/76] fix: use FocusScope as main item of tabbox switcher By default the focus item in a tabbox is always the main item, so if the main item is not a focus scope, the inner item will not get focused. BUG: 477286 FIXED-IN: 6.0 --- como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml index cf7d92c9b..02cdd6417 100644 --- a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml +++ b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml @@ -25,7 +25,7 @@ KWin.TabBoxSwitcher { x: tabBox.screenGeometry.x + tabBox.screenGeometry.width * 0.5 - dialogMainItem.width * 0.5 y: tabBox.screenGeometry.y + tabBox.screenGeometry.height * 0.5 - dialogMainItem.height * 0.5 - mainItem: Item { + mainItem: FocusScope { id: dialogMainItem focus: true From 0239a9b08714374f7e8280252a23105fad217aba Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 25 Jan 2024 12:19:57 +0200 Subject: [PATCH 47/76] fix: allocate an offscreen fbo with correct scale in OffscreenQuickView QQuickWindow::effectiveDevicePixelRatio() uses the device pixel ratio of the attached QQuickRenderTarget. Instead, the scale factor of the output should be used, which is what QWindow::devicePixelRatio() returns. --- como/render/effect/interface/offscreen_quick_view.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/como/render/effect/interface/offscreen_quick_view.cpp b/como/render/effect/interface/offscreen_quick_view.cpp index cb51b05fc..04c037e64 100644 --- a/como/render/effect/interface/offscreen_quick_view.cpp +++ b/como/render/effect/interface/offscreen_quick_view.cpp @@ -237,7 +237,7 @@ void OffscreenQuickView::update() return; } - const QSize nativeSize = d->m_view->size() * d->m_view->effectiveDevicePixelRatio(); + const QSize nativeSize = d->m_view->size() * d->m_view->devicePixelRatio(); if (!d->m_fbo || d->m_fbo->size() != nativeSize) { d->m_textureExport.reset(nullptr); From f79ebcccb0ea4681b5e87aeeced0f58c0812b8d3 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 25 Jan 2024 12:22:13 +0200 Subject: [PATCH 48/76] fix: overwrite the output in OffscreenQuickView::setGeometry() It seems like QWindow::setGeometry() won't update the associated output if no platform window has been created, which is the case when running overview or any other qml effect. It's not clear whether this is a Qt bug or intended behavior. qwindow.cpp contains comments assuming that the window is on the primary output if no platform window exists. --- como/render/effect/interface/offscreen_quick_view.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/como/render/effect/interface/offscreen_quick_view.cpp b/como/render/effect/interface/offscreen_quick_view.cpp index 04c037e64..0bb4812ee 100644 --- a/como/render/effect/interface/offscreen_quick_view.cpp +++ b/como/render/effect/interface/offscreen_quick_view.cpp @@ -488,6 +488,8 @@ void OffscreenQuickView::setGeometry(const QRect& rect) { const QRect oldGeometry = d->m_view->geometry(); d->m_view->setGeometry(rect); + // QWindow::setGeometry() won't sync output if there's no platform window. + d->m_view->setScreen(QGuiApplication::screenAt(rect.center())); Q_EMIT geometryChanged(oldGeometry, rect); } From 450d3ad86e59314c37435d20bae5b0754a6948f3 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Fri, 23 Feb 2024 18:32:01 +0100 Subject: [PATCH 49/76] feat(wl): implement closeable window rule Similar to [1]. [1] https://invent.kde.org/plasma/kwin/-/commit/e241e3eb --- autotests/integration/xdg-shell_rules.cpp | 92 +++++++++++++++++++++++ como/win/wayland/window.h | 4 +- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/autotests/integration/xdg-shell_rules.cpp b/autotests/integration/xdg-shell_rules.cpp index 6a4ca2dc5..176434286 100644 --- a/autotests/integration/xdg-shell_rules.cpp +++ b/autotests/integration/xdg-shell_rules.cpp @@ -4025,6 +4025,98 @@ TEST_CASE("xdg-shell rules", "[win]") QVERIFY(wait_for_destroyed(client)); } + SECTION("closeable dont affect") + { + // Initialize RuleBook with the test rule. + auto [config, group] = get_config(); + group.writeEntry("closeable", false); + group.writeEntry("closeablerule", enum_index(win::rules::action::dont_affect)); + group.writeEntry("wmclass", "org.kde.foo"); + group.writeEntry("wmclasscomplete", false); + group.writeEntry("wmclassmatch", enum_index(win::rules::name_match::exact)); + group.sync(); + setup.base->mod.space->rule_book->config = config; + win::space_reconfigure(*setup.base->mod.space); + + // Create the test client. + wayland_window* client; + std::unique_ptr surface; + std::unique_ptr shellSurface; + std::tie(client, surface, shellSurface) = createWindow("org.kde.foo"); + QVERIFY(client); + QVERIFY(client->control->active); + REQUIRE(client->isCloseable()); + + // Destroy the client. + shellSurface.reset(); + surface.reset(); + QVERIFY(wait_for_destroyed(client)); + } + + SECTION("closeable force") + { + // Initialize RuleBook with the test rule. + auto [config, group] = get_config(); + group.writeEntry("closeable", false); + group.writeEntry("closeablerule", enum_index(win::rules::action::force)); + group.writeEntry("wmclass", "org.kde.foo"); + group.writeEntry("wmclasscomplete", false); + group.writeEntry("wmclassmatch", enum_index(win::rules::name_match::exact)); + group.sync(); + setup.base->mod.space->rule_book->config = config; + win::space_reconfigure(*setup.base->mod.space); + + // Create the test client. + wayland_window* client; + std::unique_ptr surface; + std::unique_ptr shellSurface; + std::tie(client, surface, shellSurface) = createWindow("org.kde.foo"); + QVERIFY(client); + QVERIFY(client->control->active); + REQUIRE(!client->isCloseable()); + + // Destroy the client. + shellSurface.reset(); + surface.reset(); + QVERIFY(wait_for_destroyed(client)); + } + + SECTION("closeable force temporarily") + { + // Initialize RuleBook with the test rule. + auto [config, group] = get_config(); + group.writeEntry("closeable", false); + group.writeEntry("closeablerule", enum_index(win::rules::action::force_temporarily)); + group.writeEntry("wmclass", "org.kde.foo"); + group.writeEntry("wmclasscomplete", false); + group.writeEntry("wmclassmatch", enum_index(win::rules::name_match::exact)); + group.sync(); + setup.base->mod.space->rule_book->config = config; + win::space_reconfigure(*setup.base->mod.space); + + // Create the test client. + wayland_window* client; + std::unique_ptr surface; + std::unique_ptr shellSurface; + std::tie(client, surface, shellSurface) = createWindow("org.kde.foo"); + QVERIFY(client); + QVERIFY(client->control->active); + REQUIRE(!client->isCloseable()); + + // The rule should be discarded when the client is closed. + shellSurface.reset(); + surface.reset(); + QVERIFY(wait_for_destroyed(client)); + std::tie(client, surface, shellSurface) = createWindow("org.kde.foo"); + QVERIFY(client); + REQUIRE(client->isCloseable()); + + // Destroy the client. + shellSurface.reset(); + surface.reset(); + QVERIFY(wait_for_destroyed(client)); + } + SECTION("match after name change") { auto [config, group] = get_config(); diff --git a/como/win/wayland/window.h b/como/win/wayland/window.h index c2c11b18a..31a8ec8f0 100644 --- a/como/win/wayland/window.h +++ b/como/win/wayland/window.h @@ -896,7 +896,9 @@ class window bool isCloseable() const { - return toplevel && window_type != win_type::desktop && window_type != win_type::dock; + return toplevel + && this->control->rules.checkCloseable(window_type != win_type::desktop + && window_type != win_type::dock); } bool isMaximizable() const From 97178c391efc76f06d031e9029410b1197e01af8 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 26 Jan 2024 18:17:16 +0200 Subject: [PATCH 50/76] test: disable outline in breeze Some our tests assume that with "none" border size, the decoration has no borders. When breeze paints an outline, that's not the case. --- autotests/integration/lib/setup.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/autotests/integration/lib/setup.cpp b/autotests/integration/lib/setup.cpp index 5da033b7f..4122ee245 100644 --- a/autotests/integration/lib/setup.cpp +++ b/autotests/integration/lib/setup.cpp @@ -65,6 +65,11 @@ setup::setup(std::string const& test_name, qunsetenv("XKB_DEFAULT_VARIANT"); qunsetenv("XKB_DEFAULT_OPTIONS"); + auto breezerc = KSharedConfig::openConfig(QStringLiteral("breezerc")); + breezerc->group(QStringLiteral("Common")) + .writeEntry(QStringLiteral("OutlineIntensity"), QStringLiteral("OutlineOff")); + breezerc->sync(); + base = std::make_unique(base::wayland::platform_arguments{ .config = base::config(KConfig::OpenFlag::SimpleConfig, ""), .socket_name = socket_name, From ca7d6dfaf2c266449dcaad04156c8328612c4398 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 1 Feb 2024 20:47:59 +0200 Subject: [PATCH 51/76] fix: fix initialization of QEvent::isAccepted() in cloned events in OffscreenQuickView QEvent::isAccepted() is initialized to true by default. BUG: 480538 --- como/render/effect/interface/offscreen_quick_view.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/como/render/effect/interface/offscreen_quick_view.cpp b/como/render/effect/interface/offscreen_quick_view.cpp index 0bb4812ee..d505c4183 100644 --- a/como/render/effect/interface/offscreen_quick_view.cpp +++ b/como/render/effect/interface/offscreen_quick_view.cpp @@ -295,6 +295,7 @@ void OffscreenQuickView::forwardMouseEvent(QEvent* e) const QPoint widgetPos = d->m_view->mapFromGlobal(me->pos()); QMouseEvent cloneEvent( me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers()); + cloneEvent.setAccepted(false); QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent); e->setAccepted(cloneEvent.isAccepted()); @@ -327,6 +328,7 @@ void OffscreenQuickView::forwardMouseEvent(QEvent* e) auto const widgetPos = d->m_view->mapFromGlobal(he->position()); const QPointF oldWidgetPos = d->m_view->mapFromGlobal(he->oldPos()); QHoverEvent cloneEvent(he->type(), widgetPos, widgetPos, oldWidgetPos, he->modifiers()); + cloneEvent.setAccepted(false); QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent); e->setAccepted(cloneEvent.isAccepted()); return; @@ -342,6 +344,7 @@ void OffscreenQuickView::forwardMouseEvent(QEvent* e) we->modifiers(), we->phase(), we->inverted()); + cloneEvent.setAccepted(false); QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent); e->setAccepted(cloneEvent.isAccepted()); return; @@ -366,6 +369,7 @@ bool OffscreenQuickView::forwardTouchDown(qint32 id, const QPointF& pos, quint32 d->updateTouchState(Qt::TouchPointPressed, id, pos); QTouchEvent event(QEvent::TouchBegin, d->touchDevice, Qt::NoModifier, d->touchPoints); + event.setAccepted(false); QCoreApplication::sendEvent(d->m_view.get(), &event); return event.isAccepted(); @@ -378,6 +382,7 @@ bool OffscreenQuickView::forwardTouchMotion(qint32 id, const QPointF& pos, quint d->updateTouchState(Qt::TouchPointMoved, id, pos); QTouchEvent event(QEvent::TouchUpdate, d->touchDevice, Qt::NoModifier, d->touchPoints); + event.setAccepted(false); QCoreApplication::sendEvent(d->m_view.get(), &event); return event.isAccepted(); @@ -390,6 +395,7 @@ bool OffscreenQuickView::forwardTouchUp(qint32 id, quint32 time) d->updateTouchState(Qt::TouchPointReleased, id, QPointF{}); QTouchEvent event(QEvent::TouchEnd, d->touchDevice, Qt::NoModifier, d->touchPoints); + event.setAccepted(false); QCoreApplication::sendEvent(d->m_view.get(), &event); return event.isAccepted(); From 600f173c279dd9ea9b2bfa69c801d95b6f02c740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Venerandi?= Date: Thu, 1 Feb 2024 23:26:53 +0100 Subject: [PATCH 52/76] fix: register touch action to activate Overview instead of toggling it This is because the Overview will activate itself at the end of a 1:1 gesture, and a toggle action might actually deactivate it rather than activate it. --- como/render/effect/interface/effect_togglable_state.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/como/render/effect/interface/effect_togglable_state.cpp b/como/render/effect/interface/effect_togglable_state.cpp index f7866a71a..4b22763dd 100644 --- a/como/render/effect/interface/effect_togglable_state.cpp +++ b/como/render/effect/interface/effect_togglable_state.cpp @@ -234,14 +234,14 @@ EffectTogglableTouchBorder::EffectTogglableTouchBorder(EffectTogglableState* sta EffectTogglableTouchBorder::~EffectTogglableTouchBorder() { for (const ElectricBorder& border : std::as_const(m_touchBorderActivate)) { - effects->unregisterTouchBorder(border, m_state->toggleAction()); + effects->unregisterTouchBorder(border, m_state->activateAction()); } } void EffectTogglableTouchBorder::setBorders(const QList& touchActivateBorders) { for (const ElectricBorder& border : std::as_const(m_touchBorderActivate)) { - effects->unregisterTouchBorder(border, m_state->toggleAction()); + effects->unregisterTouchBorder(border, m_state->activateAction()); } m_touchBorderActivate.clear(); @@ -249,7 +249,7 @@ void EffectTogglableTouchBorder::setBorders(const QList& touchActivateBorde m_touchBorderActivate.append(ElectricBorder(border)); effects->registerRealtimeTouchBorder( ElectricBorder(border), - m_state->toggleAction(), + m_state->activateAction(), [this](ElectricBorder border, QSizeF const& deltaProgress, EffectScreen const* /*screen*/) { From 550478f7d147a688ffeeb182353a2d616fcec16e Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Fri, 23 Feb 2024 22:56:20 +0100 Subject: [PATCH 53/76] fix: add cursor default shape fallback Some cursor themes don't have a "default" cursor. Add a fallback to "left_ptr" for such themes. --- como/win/cursor_shape.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/como/win/cursor_shape.h b/como/win/cursor_shape.h index fd5316aac..b578e416b 100644 --- a/como/win/cursor_shape.h +++ b/como/win/cursor_shape.h @@ -70,6 +70,12 @@ inline std::vector cursor_shape_get_alternative_names(std::string c "cross-reverse", }, }, + { + "default", + { + "left_ptr", + }, + }, { "up_arrow", { From 2491513a46d3fd27b06f6e2eb9a61d3c99643644 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 5 Feb 2024 16:24:48 +0200 Subject: [PATCH 54/76] fix(plugin): cache screenshot attributes When a QPromise reports results, it's not necessary that the QFutureWatcher is going to report it immediately. That can happen at some point in the future, which is okay according to the QFuture api contract. Due to that, we cannot assume that the stored Output and EffectWindow objects pointers are valid when the QFutureWatcher::finished is emitted. --- .../effects/screenshot/screenshotdbusinterface2.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/effects/screenshot/screenshotdbusinterface2.cpp b/plugins/effects/screenshot/screenshotdbusinterface2.cpp index 1940733cf..e81c3af18 100644 --- a/plugins/effects/screenshot/screenshotdbusinterface2.cpp +++ b/plugins/effects/screenshot/screenshotdbusinterface2.cpp @@ -186,7 +186,7 @@ class ScreenShotSourceScreen2 : public ScreenShotSource2 QVariantMap attributes() const override; private: - EffectScreen* m_screen; + QString m_name; }; class ScreenShotSourceArea2 : public ScreenShotSource2 @@ -207,7 +207,7 @@ class ScreenShotSourceWindow2 : public ScreenShotSource2 QVariantMap attributes() const override; private: - EffectWindow* m_window; + QUuid m_internalId; }; class ScreenShotSinkPipe2 : public QObject @@ -259,14 +259,14 @@ ScreenShotSourceScreen2::ScreenShotSourceScreen2(ScreenShotEffect* effect, EffectScreen* screen, ScreenShotFlags flags) : ScreenShotSource2(effect->scheduleScreenShot(screen, flags)) - , m_screen(screen) + , m_name(screen->name()) { } QVariantMap ScreenShotSourceScreen2::attributes() const { return QVariantMap{ - {QStringLiteral("screen"), m_screen->name()}, + {QStringLiteral("screen"), m_name}, }; } @@ -281,14 +281,14 @@ ScreenShotSourceWindow2::ScreenShotSourceWindow2(ScreenShotEffect* effect, EffectWindow* window, ScreenShotFlags flags) : ScreenShotSource2(effect->scheduleScreenShot(window, flags)) - , m_window(window) + , m_internalId(window->internalId()) { } QVariantMap ScreenShotSourceWindow2::attributes() const { return QVariantMap{ - {QStringLiteral("windowId"), m_window->internalId().toString()}, + {QStringLiteral("windowId"), m_internalId.toString()}, }; } From 5b952fba429100c4af7c1bb227d6bdd41bc39c20 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 5 Feb 2024 15:07:15 +0200 Subject: [PATCH 55/76] fix(plugin): always ref window when sliding it offscreen Imagine the following case: - the window is hidden, slideOut() is called but no deleted reference is created because the window is not deleted - the window is closed, slideOut() won't be called because the window is hidden and SlidingPopupsEffect::slotWindowClosed() ignores closed windows that are already hidden - the window is deleted in meanwhile - the sliding popups effects attempt to delete m_animations[w] entry, but since "w" is a dangling pointer at this point, visibleRef is going to access released memory To fix that, make slideOut() always ref the window. --- plugins/effects/slidingpopups/slidingpopups.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/effects/slidingpopups/slidingpopups.cpp b/plugins/effects/slidingpopups/slidingpopups.cpp index 016d81be9..1df067eee 100644 --- a/plugins/effects/slidingpopups/slidingpopups.cpp +++ b/plugins/effects/slidingpopups/slidingpopups.cpp @@ -316,9 +316,7 @@ void SlidingPopupsEffect::slide_out(EffectWindow* win) auto& animation = animations[win]; - if (win->isDeleted()) { - animation.deletedRef = EffectWindowDeletedRef(win); - } + animation.deletedRef = EffectWindowDeletedRef(win); animation.visibleRef = EffectWindowVisibleRef( win, EffectWindow::PAINT_DISABLED | EffectWindow::PAINT_DISABLED_BY_DELETE); From 1b8afb4387edfe4528a65396f74ef5d1f0a7c628 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 2 Feb 2024 19:28:59 +0200 Subject: [PATCH 56/76] fix(plugin): fix previous desktop indicator in desktopchangeosd BUG: 480630 --- como/script/space.h | 2 +- .../package/contents/ui/main.qml | 3 +- .../package/contents/ui/osd.qml | 55 ++++++++----------- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/como/script/space.h b/como/script/space.h index 78e6a7da9..adc59dd81 100644 --- a/como/script/space.h +++ b/como/script/space.h @@ -421,7 +421,7 @@ public Q_SLOTS: * @since 5.0 */ void virtualScreenGeometryChanged(); - void currentDesktopChanged(); + void currentDesktopChanged(como::win::subspace* prev); protected: space() = default; diff --git a/plugins/scripts/desktopchangeosd/package/contents/ui/main.qml b/plugins/scripts/desktopchangeosd/package/contents/ui/main.qml index ef5ffb550..7301d638d 100644 --- a/plugins/scripts/desktopchangeosd/package/contents/ui/main.qml +++ b/plugins/scripts/desktopchangeosd/package/contents/ui/main.qml @@ -15,10 +15,11 @@ Item { Connections { target: Workspace - function onCurrentDesktopChanged() { + function onCurrentDesktopChanged(previous) { if (!mainItemLoader.item) { mainItemLoader.source = "osd.qml"; } + mainItemLoader.item.show(previous); } } } diff --git a/plugins/scripts/desktopchangeosd/package/contents/ui/osd.qml b/plugins/scripts/desktopchangeosd/package/contents/ui/osd.qml index a5dfb368b..f3eaef917 100644 --- a/plugins/scripts/desktopchangeosd/package/contents/ui/osd.qml +++ b/plugins/scripts/desktopchangeosd/package/contents/ui/osd.qml @@ -27,31 +27,6 @@ PlasmaCore.Dialog { } } - function show() { - const index = Workspace.desktops.indexOf(Workspace.currentDesktop); - if (dialogItem.currentIndex === index) { - return; - } - dialogItem.previousIndex = dialogItem.currentIndex; - timer.stop(); - dialogItem.currentIndex = index; - // screen geometry might have changed - var screen = Workspace.clientArea(KWin.FullScreenArea, Workspace.activeScreen, Workspace.currentDesktop); - dialogItem.screenWidth = screen.width; - dialogItem.screenHeight = screen.height; - if (dialogItem.showGrid) { - // non dependable properties might have changed - view.columns = Workspace.desktopGridWidth; - view.rows = Workspace.desktopGridHeight; - } - dialog.visible = true; - // position might have changed - dialog.x = screen.x + screen.width/2 - dialogItem.width/2; - dialog.y = screen.y + screen.height/2 - dialogItem.height/2; - // start the hide timer - timer.start(); - } - id: dialogItem property int screenWidth: 0 property int screenHeight: 0 @@ -269,12 +244,6 @@ PlasmaCore.Dialog { onTriggered: dialog.visible = false } - Connections { - target: Workspace - function onCurrentDesktopChanged() { - dialogItem.show() - } - } Connections { target: Options function onConfigChanged() { @@ -285,7 +254,29 @@ PlasmaCore.Dialog { view.columns = Workspace.desktopGridWidth; view.rows = Workspace.desktopGridHeight; dialogItem.loadConfig(); - dialogItem.show(); } } + + function show(previous) { + if (Workspace.isEffectActive("overview")) { + return; + } + dialogItem.previousIndex = Workspace.desktops.indexOf(previous); + dialogItem.currentIndex = Workspace.desktops.indexOf(Workspace.currentDesktop); + // screen geometry might have changed + var screen = Workspace.clientArea(KWin.FullScreenArea, Workspace.activeScreen, Workspace.currentDesktop); + dialogItem.screenWidth = screen.width; + dialogItem.screenHeight = screen.height; + if (dialogItem.showGrid) { + // non dependable properties might have changed + view.columns = Workspace.desktopGridWidth; + view.rows = Workspace.desktopGridHeight; + } + dialog.visible = true; + // position might have changed + dialog.x = screen.x + screen.width/2 - dialogItem.width/2; + dialog.y = screen.y + screen.height/2 - dialogItem.height/2; + // start the hide timer + timer.restart(); + } } From 08384adde3704cff7a35b199f373b3206d9e1ce4 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 5 Feb 2024 11:00:17 +0200 Subject: [PATCH 57/76] feat: hide "active mouse screen" option In the late Plasma 5 times we agreed that it would be better to drop "active mouse screen" option and stick with last interacted screen approach instead. However, it was forgotten and nobody has pursued this goal, so let's hide the option in the system settings ui at least. The option is not completely removed because some parts of kwin would need adjustments. Note that this only changes the config values. --- kconf_update/CMakeLists.txt | 5 ++++ .../kwin-6.0-reset-active-mouse-screen.cpp | 26 +++++++++++++++++++ kconf_update/kwin.upd | 4 +++ 3 files changed, 35 insertions(+) create mode 100644 kconf_update/kwin-6.0-reset-active-mouse-screen.cpp diff --git a/kconf_update/CMakeLists.txt b/kconf_update/CMakeLists.txt index da7d0a2ee..216a37ad9 100644 --- a/kconf_update/CMakeLists.txt +++ b/kconf_update/CMakeLists.txt @@ -6,6 +6,11 @@ target_sources(kwin-6.0-delete-desktop-switching-shortcuts PRIVATE kwin-6.0-dele target_link_libraries(kwin-6.0-delete-desktop-switching-shortcuts PRIVATE KF6::GlobalAccel) install(TARGETS kwin-6.0-delete-desktop-switching-shortcuts DESTINATION ${KDE_INSTALL_LIBDIR}/kconf_update_bin/) +add_executable(kwin-6.0-reset-active-mouse-screen) +target_sources(kwin-6.0-reset-active-mouse-screen PRIVATE kwin-6.0-reset-active-mouse-screen.cpp) +target_link_libraries(kwin-6.0-reset-active-mouse-screen PRIVATE KF6::ConfigCore) +install(TARGETS kwin-6.0-reset-active-mouse-screen DESTINATION ${KDE_INSTALL_LIBDIR}/kconf_update_bin/) + install(FILES kwin.upd DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) install(PROGRAMS kwin-6.0-overview-activities-shortcuts.py diff --git a/kconf_update/kwin-6.0-reset-active-mouse-screen.cpp b/kconf_update/kwin-6.0-reset-active-mouse-screen.cpp new file mode 100644 index 000000000..0efcc1dd6 --- /dev/null +++ b/kconf_update/kwin-6.0-reset-active-mouse-screen.cpp @@ -0,0 +1,26 @@ +/* + SPDX-FileCopyrightText: 2024 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include +#include + +int main() +{ + auto config = KSharedConfig::openConfig(QStringLiteral("kwinrc")); + + KConfigGroup windows = config->group(QStringLiteral("Windows")); + if (!windows.exists()) { + return EXIT_SUCCESS; + } + + if (!windows.hasKey(QStringLiteral("ActiveMouseScreen"))) { + return EXIT_SUCCESS; + } + + windows.deleteEntry(QStringLiteral("ActiveMouseScreen")); + + return windows.sync() ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/kconf_update/kwin.upd b/kconf_update/kwin.upd index 573b28fc5..ea93ef2be 100644 --- a/kconf_update/kwin.upd +++ b/kconf_update/kwin.upd @@ -3,6 +3,10 @@ Version=6 +# Reset ActiveMouseScreen config option. +Id=kwin-6.0-reset-active-mouse-screen +Script=kwin-6.0-reset-active-mouse-screen + # Delete old desktop switching shortcuts. Id=kwin-6.0-delete-desktop-switching-shortcuts Script=kwin-6.0-delete-desktop-switching-shortcuts From c9445844439bcb3554a92e386befa38ec90c890f Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 8 Feb 2024 13:01:27 +0000 Subject: [PATCH 58/76] fix: don't scale WindowHeap in overview mode when in overview mode, don't scale down WindowHeap, as this will cause ugly glitches, but resize it down instead. Still use transforms when it goes in desktop grid mode, at least for now probably future further refactor can still help things Before: ![image](/uploads/7ca83e7e9292bd8489faaf76d4c12693/image.png) After: ![image](/uploads/27b970d056c89486661d6695d09813ff/image.png) CCBUG:475682 --- plugins/effects/overview/qml/main.qml | 155 +++++++++++++------------- 1 file changed, 78 insertions(+), 77 deletions(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index 7fcb680fc..1ecc23021 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -492,14 +492,6 @@ FocusScope { x: column * (width / columns) * gridVal y: row * (height / rows) * gridVal }, - // Scales down the preview slighly when in Overview mode - Scale { - origin.x: width / 2 - origin.y: height / 2 - property real scale: Math.min(maxWidth / width, maxHeight / height) - xScale: 1 + (scale - 1) * overviewVal - yScale:1 + (scale - 1) * overviewVal - }, // Initially places transition desktops in a grid around the current one, // and moves them slighly to avoid overlapping the UI Translate { @@ -508,97 +500,105 @@ FocusScope { } ] - KWinComponents.DesktopBackground { - id: desktopElement - anchors.fill: parent - anchors.margins: gridVal !== 0 ? Math.round(mainBackground.current * gridVal * (1.5 / gridScale.xScale)) : 0 - activity: KWinComponents.Workspace.currentActivity - desktop: KWinComponents.Workspace.currentDesktop - outputName: targetScreen.name - visible: false - } + Item { + id: backgroundArea + property real sizeAdjust: Math.min(maxWidth / parent.width, maxHeight / parent.height) + width: parent.width * (1 + (sizeAdjust - 1) * overviewVal) + height: parent.height * (1 + (sizeAdjust - 1) * overviewVal) + x: parent.width / 2 - width / 2 + y: parent.height / 2 - height / 2 + KWinComponents.DesktopBackground { + id: desktopElement + anchors.fill: parent + anchors.margins: gridVal !== 0 ? Math.round(mainBackground.current * gridVal * (1.5 / gridScale.xScale)) : 0 + activity: KWinComponents.Workspace.currentActivity + desktop: KWinComponents.Workspace.currentDesktop + outputName: targetScreen.name + visible: false + } - Kirigami.ShadowedTexture { - anchors.fill: parent + Kirigami.ShadowedTexture { + anchors.fill: parent - color: Kirigami.Theme.highlightColor - source: desktopElement + color: Kirigami.Theme.highlightColor + source: desktopElement - radius: Kirigami.Units.largeSpacing * 2 * (overviewVal + gridVal * 2) + radius: Kirigami.Units.largeSpacing * 2 * (overviewVal + gridVal * 2) - shadow { - size: Kirigami.Units.gridUnit * 2 - color: Qt.rgba(0, 0, 0, 0.3) - yOffset: 3 + shadow { + size: Kirigami.Units.gridUnit * 2 + color: Qt.rgba(0, 0, 0, 0.3) + yOffset: 3 + } } - } - DropArea { - anchors.fill: parent - onEntered: (drag) => { - drag.accepted = true; + DropArea { + anchors.fill: parent + onEntered: (drag) => { + drag.accepted = true; + } + onDropped: (drop) => { + drop.accepted = true; + if (drop.keys.includes("kwin-desktop")) { + // dragging a desktop as a whole + if (drag.source === mainBackground) { + drop.action = Qt.IgnoreAction; + return; + } + effect.swapDesktops(drag.source.desktop, desktop); + } else { + // dragging a KWin::Window + if (drag.source.desktops.length === 0 || drag.source.desktops.indexOf(mainBackground.desktop) !== -1) { + drop.action = Qt.IgnoreAction; + return; + } + drag.source.desktops = [mainBackground.desktop]; + } + } } - onDropped: (drop) => { - drop.accepted = true; - if (drop.keys.includes("kwin-desktop")) { - // dragging a desktop as a whole - if (drag.source === mainBackground) { - drop.action = Qt.IgnoreAction; + Connections { + target: effect + function onItemDroppedOutOfScreen(globalPos, item, screen) { + if (screen !== targetScreen) { return; } - effect.swapDesktops(drag.source.desktop, desktop); - } else { - // dragging a KWin::Window - if (drag.source.desktops.length === 0 || drag.source.desktops.indexOf(mainBackground.desktop) !== -1) { - drop.action = Qt.IgnoreAction; + const pos = screen.mapFromGlobal(globalPos); + if (!mainBackground.contains(mainBackground.mapFromItem(null, pos.x, pos.y))) { return; } - drag.source.desktops = [mainBackground.desktop]; - } - } - } - Connections { - target: effect - function onItemDroppedOutOfScreen(globalPos, item, screen) { - if (screen !== targetScreen) { - return; + item.client.desktops = [mainBackground.desktop]; } - const pos = screen.mapFromGlobal(globalPos); - if (!mainBackground.contains(mainBackground.mapFromItem(null, pos.x, pos.y))) { - return; - } - item.client.desktops = [mainBackground.desktop]; } - } - DragHandler { - id: dragHandler - target: heap - enabled: gridVal !== 0 - grabPermissions: PointerHandler.ApprovesTakeOverByHandlersOfSameType - onActiveChanged: { - if (!active) { - heap.Drag.drop(); - Qt.callLater(heap.resetPosition) + DragHandler { + id: dragHandler + target: heap + enabled: gridVal !== 0 + grabPermissions: PointerHandler.ApprovesTakeOverByHandlersOfSameType + onActiveChanged: { + if (!active) { + heap.Drag.drop(); + Qt.callLater(heap.resetPosition) + } } } - } - MouseArea { - anchors.fill: heap - acceptedButtons: Qt.NoButton - cursorShape: dragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor + MouseArea { + anchors.fill: heap + acceptedButtons: Qt.NoButton + cursorShape: dragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor + } } WindowHeap { id: heap - width: parent.width - height: parent.height - x: 0 - y: 0 + width: parent.width * (1 + (backgroundArea.sizeAdjust - 1)) + height: parent.height * (1 + (backgroundArea.sizeAdjust - 1)) + x: parent.width / 2 - width / 2 + y: parent.height / 2 - height / 2 function resetPosition() { - x = 0; - y = 0; + x = parent.width / 2 - width / 2; + y = parent.height / 2 - height / 2; } z: 9999 Drag.active: dragHandler.active @@ -683,6 +683,7 @@ FocusScope { container.effect.deactivate(); } } + onCurrentChanged: { if (current) { allDesktopHeaps.currentHeap = heap; From f5fdb10a1f7c2bf558f95a709484175f61e94591 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Tue, 6 Feb 2024 22:38:06 +0200 Subject: [PATCH 59/76] fix: drop kwin-6.0-overview-activities-shortcuts script There are a few issues: - it's incompatible with Version 6 format - activity shortcuts cannot be changed in kwin - overview shortcuts don't need to be touched BUG: 480758 --- kconf_update/CMakeLists.txt | 2 -- .../kwin-6.0-overview-activities-shortcuts.py | 20 ------------------- kconf_update/kwin.upd | 7 ------- 3 files changed, 29 deletions(-) delete mode 100644 kconf_update/kwin-6.0-overview-activities-shortcuts.py diff --git a/kconf_update/CMakeLists.txt b/kconf_update/CMakeLists.txt index 216a37ad9..a43f96f79 100644 --- a/kconf_update/CMakeLists.txt +++ b/kconf_update/CMakeLists.txt @@ -13,5 +13,3 @@ install(TARGETS kwin-6.0-reset-active-mouse-screen DESTINATION ${KDE_INSTALL_LIB install(FILES kwin.upd DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) -install(PROGRAMS kwin-6.0-overview-activities-shortcuts.py - DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) diff --git a/kconf_update/kwin-6.0-overview-activities-shortcuts.py b/kconf_update/kwin-6.0-overview-activities-shortcuts.py deleted file mode 100644 index e350a4be9..000000000 --- a/kconf_update/kwin-6.0-overview-activities-shortcuts.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2023 Niccolò Venerandi -# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only - -import fileinput - -for line in fileinput.input(): - if line.startswith('next activity'): - print(line.replace('Meta+Tab', 'Meta+A')) - elif line.startswith('previous activity'): - print(line.replace('Meta+Shift+Tab', 'Meta+Shift+A')) - elif line.startswith('ShowDesktopGrid'): - pass - elif line.startswith('Overview'): - print('Overview=Meta+W,Meta+W,Toggle Overview') - print('Cycle Overview=Meta+Tab,Meta+Tab,Cycle through Overview and Grid View') - print('Cycle Overview Opposite=Meta+Shift+Tab,Meta+Shift+Tab,Cycle through Grid View and Overview') - print('Grid View=Meta+G,Meta+G,Toggle Grid View') - else: - print(line) diff --git a/kconf_update/kwin.upd b/kconf_update/kwin.upd index ea93ef2be..d3ef84271 100644 --- a/kconf_update/kwin.upd +++ b/kconf_update/kwin.upd @@ -10,10 +10,3 @@ Script=kwin-6.0-reset-active-mouse-screen # Delete old desktop switching shortcuts. Id=kwin-6.0-delete-desktop-switching-shortcuts Script=kwin-6.0-delete-desktop-switching-shortcuts - -# Changes the default Activities shortcut from Meta+Tab to Meta+A, -# so that the Overview can take its place -Id=change-activities-overview-shortcuts -File=kglobalshortcutsrc -Group=plasmashell,kwin -Script=kwin-6.0-overview-activities-shortcuts.py,python3 From 611b46a26b506d7042eac1d114142e6c261fdd4d Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Tue, 6 Feb 2024 22:40:44 +0200 Subject: [PATCH 60/76] feat(plugin): revoke Meta+Tab and Meta+Shift+Tab shortcuts for overview These shortcuts make more sense to be used with Meta+Tab and Meta+Shift+Tab. Let's keep them reserved for the task switcher. Cycling between overview modes is not something that requires Meta+Tab to be assigned to it by default. --- plugins/effects/overview/kcm/overvieweffectkcm.cpp | 10 ++++------ plugins/effects/overview/overvieweffect.cpp | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/plugins/effects/overview/kcm/overvieweffectkcm.cpp b/plugins/effects/overview/kcm/overvieweffectkcm.cpp index d2e59f8d6..8f703ba8d 100644 --- a/plugins/effects/overview/kcm/overvieweffectkcm.cpp +++ b/plugins/effects/overview/kcm/overvieweffectkcm.cpp @@ -35,22 +35,20 @@ OverviewEffectConfig::OverviewEffectConfig(QObject* parent, const KPluginMetaDat actionCollection->setConfigGroup(QStringLiteral("Overview")); actionCollection->setConfigGlobal(true); - const QKeySequence defaultCycleShortcut = Qt::META | Qt::Key_Tab; QAction* cycleAction = actionCollection->addAction(QStringLiteral("Cycle Overview")); cycleAction->setText(i18nc("@action Overview and Grid View are the name of KWin effects", "Cycle through Overview and Grid View")); cycleAction->setProperty("isConfigurationAction", true); - KGlobalAccel::self()->setDefaultShortcut(cycleAction, {defaultCycleShortcut}); - KGlobalAccel::self()->setShortcut(cycleAction, {defaultCycleShortcut}); + KGlobalAccel::self()->setDefaultShortcut(cycleAction, {}); + KGlobalAccel::self()->setShortcut(cycleAction, {}); - const QKeySequence defaultUncycleShortcut = Qt::META | Qt::SHIFT | Qt::Key_Tab; QAction* reverseCycleAction = actionCollection->addAction(QStringLiteral("Cycle Overview Opposite")); reverseCycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Grid View and Overview")); reverseCycleAction->setProperty("isConfigurationAction", true); - KGlobalAccel::self()->setDefaultShortcut(reverseCycleAction, {defaultUncycleShortcut}); - KGlobalAccel::self()->setShortcut(reverseCycleAction, {defaultUncycleShortcut}); + KGlobalAccel::self()->setDefaultShortcut(reverseCycleAction, {}); + KGlobalAccel::self()->setShortcut(reverseCycleAction, {}); const QKeySequence defaultOverviewShortcut = Qt::META | Qt::Key_W; QAction* overviewAction = actionCollection->addAction(QStringLiteral("Overview")); diff --git a/plugins/effects/overview/overvieweffect.cpp b/plugins/effects/overview/overvieweffect.cpp index 2a9e40ae4..92af24f87 100644 --- a/plugins/effects/overview/overvieweffect.cpp +++ b/plugins/effects/overview/overvieweffect.cpp @@ -156,24 +156,22 @@ OverviewEffect::OverviewEffect() m_shutdownTimer->setSingleShot(true); connect(m_shutdownTimer, &QTimer::timeout, this, &OverviewEffect::realDeactivate); - const QKeySequence defaultCycleShortcut = Qt::META | Qt::Key_Tab; auto cycleAction = new QAction(this); connect(cycleAction, &QAction::triggered, this, &OverviewEffect::cycle); cycleAction->setObjectName(QStringLiteral("Cycle Overview")); cycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Overview and Grid View")); - KGlobalAccel::self()->setDefaultShortcut(cycleAction, {defaultCycleShortcut}); - KGlobalAccel::self()->setShortcut(cycleAction, {defaultCycleShortcut}); + KGlobalAccel::self()->setDefaultShortcut(cycleAction, {}); + KGlobalAccel::self()->setShortcut(cycleAction, {}); m_cycleShortcut = KGlobalAccel::self()->shortcut(cycleAction); - const QKeySequence defaultUncycleShortcut = Qt::META | Qt::SHIFT | Qt::Key_Tab; auto reverseCycleAction = new QAction(this); connect(reverseCycleAction, &QAction::triggered, this, &OverviewEffect::reverseCycle); reverseCycleAction->setObjectName(QStringLiteral("Cycle Overview Opposite")); reverseCycleAction->setText(i18nc("@action Grid View and Overview are the name of KWin effects", "Cycle through Grid View and Overview")); - KGlobalAccel::self()->setDefaultShortcut(reverseCycleAction, {defaultUncycleShortcut}); - KGlobalAccel::self()->setShortcut(reverseCycleAction, {defaultUncycleShortcut}); + KGlobalAccel::self()->setDefaultShortcut(reverseCycleAction, {}); + KGlobalAccel::self()->setShortcut(reverseCycleAction, {}); m_reverseCycleShortcut = KGlobalAccel::self()->shortcut(reverseCycleAction); const QKeySequence defaultOverviewShortcut = Qt::META | Qt::Key_W; From 8a5ed74196fd98b8c1e9535ee8d74d2f3f537bf5 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Wed, 7 Feb 2024 22:03:14 +0200 Subject: [PATCH 61/76] refactor: adapt color correct d-bus interface to Plasma Plasma expects new names and methods for the interface. --- .../kde/dbus/KWinDBusInterfaceConfig.cmake.in | 2 +- como/render/CMakeLists.txt | 4 +- ...orrect.xml => org.kde.KWin.NightLight.xml} | 46 +++++++++++++------ .../post/color_correct_dbus_interface.cpp | 26 +++++++---- .../post/color_correct_dbus_interface.h | 16 ++++--- 5 files changed, 63 insertions(+), 31 deletions(-) rename como/render/dbus/{org.kde.kwin.ColorCorrect.xml => org.kde.KWin.NightLight.xml} (73%) diff --git a/como/desktop/kde/dbus/KWinDBusInterfaceConfig.cmake.in b/como/desktop/kde/dbus/KWinDBusInterfaceConfig.cmake.in index 8c51bdfc7..96130bad5 100644 --- a/como/desktop/kde/dbus/KWinDBusInterfaceConfig.cmake.in +++ b/como/desktop/kde/dbus/KWinDBusInterfaceConfig.cmake.in @@ -8,7 +8,7 @@ set(KWIN_INTERFACE "@PACKAGE_KDE_INSTALL_DBUSINTERFACEDIR@/org.kde.KWin.xml") set(KWIN_COMPOSITING_INTERFACE "@PACKAGE_KDE_INSTALL_DBUSINTERFACEDIR@/org.kde.kwin.Compositing.xml") set(KWIN_EFFECTS_INTERFACE "@PACKAGE_KDE_INSTALL_DBUSINTERFACEDIR@/org.kde.kwin.Effects.xml") set(KWIN_INPUTDEVICE_INTERFACE "@PACKAGE_KDE_INSTALL_DBUSINTERFACEDIR@/org.kde.kwin.InputDevice.xml") -set(KWIN_COLORCORRECT_INTERFACE "@PACKAGE_KDE_INSTALL_DBUSINTERFACEDIR@/org.kde.kwin.ColorCorrect.xml") +set(KWIN_NIGHTLIGHT_INTERFACE "@PACKAGE_KDE_INSTALL_DBUSINTERFACEDIR@/org.kde.KWin.NightLight.xml") set(KWIN_WAYLAND_BIN_PATH "@CMAKE_INSTALL_FULL_BINDIR@/kwin_wayland") # Still needs to be set as plasma-workspace expects it at compile time. diff --git a/como/render/CMakeLists.txt b/como/render/CMakeLists.txt index 827c996f3..bfe06efe5 100644 --- a/como/render/CMakeLists.txt +++ b/como/render/CMakeLists.txt @@ -156,7 +156,7 @@ qt6_add_dbus_adaptor(render_dbus_SRCS como::render::dbus::compositing_qobject ) qt6_add_dbus_adaptor(render_dbus_SRCS - dbus/org.kde.kwin.ColorCorrect.xml + dbus/org.kde.KWin.NightLight.xml post/color_correct_dbus_interface.h como::render::post::color_correct_dbus_interface ) @@ -300,7 +300,7 @@ install( ) install( FILES - dbus/org.kde.kwin.ColorCorrect.xml + dbus/org.kde.KWin.NightLight.xml dbus/org.kde.kwin.Compositing.xml effect/interface/org.kde.kwin.Effects.xml DESTINATION diff --git a/como/render/dbus/org.kde.kwin.ColorCorrect.xml b/como/render/dbus/org.kde.KWin.NightLight.xml similarity index 73% rename from como/render/dbus/org.kde.kwin.ColorCorrect.xml rename to como/render/dbus/org.kde.KWin.NightLight.xml index 137f948e0..1589fc897 100644 --- a/como/render/dbus/org.kde.kwin.ColorCorrect.xml +++ b/como/render/dbus/org.kde.KWin.NightLight.xml @@ -1,15 +1,15 @@ - - + + + + + + + + + + + @@ -73,36 +91,36 @@ - + diff --git a/como/render/post/color_correct_dbus_interface.cpp b/como/render/post/color_correct_dbus_interface.cpp index 511ed66f0..028460255 100644 --- a/como/render/post/color_correct_dbus_interface.cpp +++ b/como/render/post/color_correct_dbus_interface.cpp @@ -5,8 +5,8 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "color_correct_dbus_interface.h" -#include "colorcorrectadaptor.h" #include "night_color_data.h" +#include "nightlightadaptor.h" #include @@ -15,12 +15,12 @@ namespace como::render::post static void send_changed_properties(QVariantMap const& props) { - auto message = QDBusMessage::createSignal(QStringLiteral("/ColorCorrect"), + auto message = QDBusMessage::createSignal(QStringLiteral("/org/kde/KWin/NightLight"), QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged")); message.setArguments({ - QStringLiteral("org.kde.kwin.ColorCorrect"), + QStringLiteral("org.kde.KWin.NightLight"), props, QStringList(), // invalidated_properties }); @@ -40,14 +40,14 @@ color_correct_dbus_interface::color_correct_dbus_interface( this, &color_correct_dbus_interface::removeInhibitorService); - new ColorCorrectAdaptor(this); - QDBusConnection::sessionBus().registerObject(QStringLiteral("/ColorCorrect"), this); - QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.NightColor")); + new NightLightAdaptor(this); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KWin/NightLight"), this); + QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.KWin.NightLight")); } color_correct_dbus_interface::~color_correct_dbus_interface() { - QDBusConnection::sessionBus().unregisterService(QStringLiteral("org.kde.NightColor")); + QDBusConnection::sessionBus().unregisterService(QStringLiteral("org.kde.KWin.NightLight")); } bool color_correct_dbus_interface::isInhibited() const @@ -179,7 +179,7 @@ void color_correct_dbus_interface::send_transition_timings() const send_changed_properties(props); } -void color_correct_dbus_interface::nightColorAutoLocationUpdate(double latitude, double longitude) +void color_correct_dbus_interface::setLocation(double latitude, double longitude) { integration.loc_update(latitude, longitude); } @@ -228,4 +228,14 @@ void color_correct_dbus_interface::removeInhibitorService(const QString& service } } +void color_correct_dbus_interface::preview(uint /*previewTemp*/) +{ + // TODO(romangg): implement +} + +void color_correct_dbus_interface::stopPreview() +{ + // TODO(romangg): implement +} + } diff --git a/como/render/post/color_correct_dbus_interface.h b/como/render/post/color_correct_dbus_interface.h index 088808abf..c65467cbf 100644 --- a/como/render/post/color_correct_dbus_interface.h +++ b/como/render/post/color_correct_dbus_interface.h @@ -36,7 +36,7 @@ struct color_correct_dbus_integration { class COMO_EXPORT color_correct_dbus_interface : public QObject, public QDBusContext { Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.ColorCorrect") + Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.NightLight") Q_PROPERTY(bool inhibited READ isInhibited) Q_PROPERTY(bool enabled READ isEnabled) Q_PROPERTY(bool running READ isRunning) @@ -79,21 +79,25 @@ class COMO_EXPORT color_correct_dbus_interface : public QObject, public QDBusCon public Q_SLOTS: /** * @brief For receiving auto location updates, primarily through the KDE Daemon - * @return void - * @since 5.12 */ - void nightColorAutoLocationUpdate(double latitude, double longitude); + void setLocation(double latitude, double longitude); /** * @brief Temporarily blocks Night Color. - * @since 5.18 */ uint inhibit(); /** * @brief Cancels the previous call to inhibit(). - * @since 5.18 */ void uninhibit(uint cookie); + /** + * @brief Previews a given temperature for a short time (15s). + */ + void preview(uint temperature); + /** + * @brief Stops an ongoing preview. + */ + void stopPreview(); private Q_SLOTS: void removeInhibitorService(const QString& serviceName); From 561238caf3665b640f732ab8bdddc387e6c03cc3 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Fri, 9 Feb 2024 09:03:18 +0000 Subject: [PATCH 62/76] fix: avoid double delete of QQuickViews Views are owned by the C++ backend, but also retrievable by invokables to get neighbouring screens from JS space. By default Qt then transfers ownership of the view to the QML collector. This results in double ownership. BUG: 480788 --- como/render/effect/interface/quick_scene.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/como/render/effect/interface/quick_scene.cpp b/como/render/effect/interface/quick_scene.cpp index ec7a170e8..edc006642 100644 --- a/como/render/effect/interface/quick_scene.cpp +++ b/como/render/effect/interface/quick_scene.cpp @@ -460,6 +460,9 @@ void QuickSceneEffect::addScreen(EffectScreen const* screen) view.get(), &QuickSceneView::scheduleRepaint); view->scheduleRepaint(); + + // view is returned via invokables elsewhere + QJSEngine::setObjectOwnership(view.get(), QJSEngine::CppOwnership); d->views[screen] = std::move(view); } else if (incubator->isError()) { qCWarning(KWIN_CORE) From b4c38eff52fb68c64eef6971f951ee4280abdd7c Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 12 Feb 2024 12:27:28 +0200 Subject: [PATCH 63/76] fix: fix a warning about incorrect anchor in overview The mouse area is no longer a sibling of the window heap, which produces a warning. BUG: 481106 --- plugins/effects/overview/qml/main.qml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index 1ecc23021..bd46abdf5 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -581,12 +581,6 @@ FocusScope { } } } - - MouseArea { - anchors.fill: heap - acceptedButtons: Qt.NoButton - cursorShape: dragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor - } } WindowHeap { @@ -674,6 +668,14 @@ FocusScope { } } } + + MouseArea { + anchors.fill: parent + z: -1 + acceptedButtons: Qt.NoButton + cursorShape: dragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor + } + onActivated: effect.deactivate() } TapHandler { From 5f93362f3cdfbbdf093863a609181cb72e7f5662 Mon Sep 17 00:00:00 2001 From: Ismael Asensio Date: Mon, 12 Feb 2024 20:58:12 +0100 Subject: [PATCH 64/76] fix: activate on thumbnail click when selected While tabbox switching is usually a keyboard operation, we offer also a mouse-friendly way to launch it via screen edges, and should allow to switch directly on mouse click. BUG: 481267 FIXED-IN: 6.0 --- como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml index 02cdd6417..33b306183 100644 --- a/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml +++ b/como/win/tabbox/switchers/thumbnail_grid/contents/ui/main.qml @@ -120,7 +120,7 @@ KWin.TabBoxSwitcher { Accessible.role: Accessible.ListItem onClicked: { - tabBox.currentIndex = index; + tabBox.model.activate(index); } ColumnLayout { From 80f4fc0d87e5c83c1b7e22c8d8343d7cf7dc09c3 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Tue, 13 Feb 2024 17:12:42 +0000 Subject: [PATCH 65/76] fix(plugin): explicitly reset parent on teradown Instantiators create objects when they're added to a model, and deference when when they're removed from the model. When we explicitly set a parent in onObjectAdded we're creating a second reference. This does get cleaned up later, but not in the same frame. This brings us in line to what QQmlRepeater (which works with items) does internally for items being added and removed. BUG: 478777 --- plugins/effects/private/qml/WindowHeap.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/effects/private/qml/WindowHeap.qml b/plugins/effects/private/qml/WindowHeap.qml index 3ebedb2b4..2f648dfa1 100644 --- a/plugins/effects/private/qml/WindowHeap.qml +++ b/plugins/effects/private/qml/WindowHeap.qml @@ -136,6 +136,12 @@ FocusScope { windowHeap: heap } + onObjectRemoved: (index, object) => { + // undo explicitly set parent in objectAdded so it can be + // removed from the scene immediately + object.parent = null + } + onObjectAdded: (index, object) => { object.parent = expoLayout var key = object.window.internalId; From eab1716ab3e9bb379d564a1014fbc9591fc74b77 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Sat, 24 Feb 2024 00:27:48 +0100 Subject: [PATCH 66/76] feat: implement additional _NET_WM_MOVERESIZE arguments They are currently unused, but might be useful in the future. --- autotests/integration/move_resize_window.cpp | 6 ++++-- como/win/x11/event.h | 6 +++++- como/win/x11/net/root_info.cpp | 19 +++++++++++++++---- como/win/x11/net/root_info.h | 16 ++++++++++++++-- como/win/x11/netinfo.h | 10 ++++++++-- 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/autotests/integration/move_resize_window.cpp b/autotests/integration/move_resize_window.cpp index 7838d0695..5eca534f1 100644 --- a/autotests/integration/move_resize_window.cpp +++ b/autotests/integration/move_resize_window.cpp @@ -679,7 +679,8 @@ TEST_CASE("move resize window", "[win]") // use NETRootInfo to trigger a move request win::x11::net::root_info root(c.get(), win::x11::net::Properties()); - root.moveResizeRequest(w, origGeo.center().x(), origGeo.center().y(), win::x11::net::Move); + root.moveResizeRequest( + w, origGeo.center().x(), origGeo.center().y(), win::x11::net::Move, XCB_BUTTON_INDEX_1); xcb_flush(c.get()); QVERIFY(moveStartSpy.wait()); @@ -697,7 +698,8 @@ TEST_CASE("move resize window", "[win]") root.moveResizeRequest(w, client->geo.frame.center().x(), client->geo.frame.center().y(), - win::x11::net::MoveResizeCancel); + win::x11::net::MoveResizeCancel, + XCB_BUTTON_INDEX_1); xcb_flush(c.get()); QVERIFY(moveEndSpy.wait()); diff --git a/como/win/x11/event.h b/como/win/x11/event.h index b22227333..c80098878 100644 --- a/como/win/x11/event.h +++ b/como/win/x11/event.h @@ -753,7 +753,11 @@ void focus_out_event(Win* win, xcb_focus_out_event_t* e) // performs _NET_WM_MOVERESIZE template -void net_move_resize(Win* win, int x_root, int y_root, net::Direction direction) +void net_move_resize(Win* win, + int x_root, + int y_root, + net::Direction direction, + xcb_button_t /*button*/) { auto& mov_res = win->control->move_resize; auto& cursor = win->space.input->cursor; diff --git a/como/win/x11/net/root_info.cpp b/como/win/x11/net/root_info.cpp index 4397d0d2c..7d4917f96 100644 --- a/como/win/x11/net/root_info.cpp +++ b/como/win/x11/net/root_info.cpp @@ -1359,9 +1359,18 @@ void root_info::closeWindowRequest(xcb_window_t window) p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_CLOSE_WINDOW), data); } -void root_info::moveResizeRequest(xcb_window_t window, int x_root, int y_root, Direction direction) -{ - uint32_t const data[5] = {uint32_t(x_root), uint32_t(y_root), uint32_t(direction), 0, 0}; +void root_info::moveResizeRequest(xcb_window_t window, + int x_root, + int y_root, + Direction direction, + xcb_button_t button, + RequestSource source) +{ + uint32_t const data[5] = {uint32_t(x_root), + uint32_t(y_root), + uint32_t(direction), + uint32_t(button), + uint32_t(source)}; send_client_message( p->conn, netwm_sendevent_mask, p->root, window, p->atom(_NET_WM_MOVERESIZE), data); @@ -1485,7 +1494,9 @@ void root_info::event(xcb_generic_event_t* event, moveResize(message->window, message->data.data32[0], message->data.data32[1], - message->data.data32[2]); + message->data.data32[2], + message->data.data32[3], + RequestSource(message->data.data32[4])); } else if (message->type == p->atom(_NET_MOVERESIZE_WINDOW)) { moveResizeWindow(message->window, message->data.data32[0], diff --git a/como/win/x11/net/root_info.h b/como/win/x11/net/root_info.h index 8f6101ed1..9397f4973 100644 --- a/como/win/x11/net/root_info.h +++ b/como/win/x11/net/root_info.h @@ -129,7 +129,12 @@ class COMO_EXPORT root_info const root_info& operator=(const root_info& rootinfo); void closeWindowRequest(xcb_window_t window); - void moveResizeRequest(xcb_window_t window, int x_root, int y_root, Direction direction); + void moveResizeRequest(xcb_window_t window, + int x_root, + int y_root, + Direction direction, + xcb_button_t button = XCB_BUTTON_INDEX_ANY, + net::RequestSource source = net::RequestSource::FromUnknown); void moveResizeWindowRequest(xcb_window_t window, int flags, int x, int y, int width, int height); void showWindowMenuRequest(xcb_window_t window, int device_id, int x_root, int y_root); @@ -183,12 +188,19 @@ class COMO_EXPORT root_info Q_UNUSED(window); } - virtual void moveResize(xcb_window_t window, int x_root, int y_root, unsigned long direction) + virtual void moveResize(xcb_window_t window, + int x_root, + int y_root, + unsigned long direction, + xcb_button_t button, + RequestSource source) { Q_UNUSED(window); Q_UNUSED(x_root); Q_UNUSED(y_root); Q_UNUSED(direction); + Q_UNUSED(button); + Q_UNUSED(source); } virtual void gotPing(xcb_window_t window, xcb_timestamp_t timestamp) diff --git a/como/win/x11/netinfo.h b/como/win/x11/netinfo.h index 3115a913a..b29db44f3 100644 --- a/como/win/x11/netinfo.h +++ b/como/win/x11/netinfo.h @@ -243,12 +243,18 @@ class root_info : public net::root_info } } - void moveResize(xcb_window_t w, int x_root, int y_root, unsigned long direction) override + void moveResize(xcb_window_t w, + int x_root, + int y_root, + unsigned long direction, + xcb_button_t button, + net::RequestSource /*source*/) override { if (auto win = find_controlled_window(space, predicate_match::window, w)) { // otherwise grabbing may have old timestamp - this message should include timestamp base::x11::update_time_from_clock(space.base); - x11::net_move_resize(win, x_root, y_root, static_cast(direction)); + x11::net_move_resize( + win, x_root, y_root, static_cast(direction), button); } } From d10bd08352d3167252e5509c9345e9f61e2dcb30 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 13 Feb 2024 22:16:35 +0100 Subject: [PATCH 67/76] fix: do caps lock is not shift lock Caps lock only locks capitalization of letters, making it artificially also trigger shift in the context of KWin only causes problems --- autotests/integration/modifier_only_shortcut.cpp | 5 ++--- autotests/integration/tabbox.cpp | 9 ++++----- como/input/xkb/keyboard.cpp | 3 +-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/autotests/integration/modifier_only_shortcut.cpp b/autotests/integration/modifier_only_shortcut.cpp index 2e4aaaed7..f0ec1b286 100644 --- a/autotests/integration/modifier_only_shortcut.cpp +++ b/autotests/integration/modifier_only_shortcut.cpp @@ -270,15 +270,14 @@ TEST_CASE("modifier only shortcut", "[input]") keyboard_key_pressed(KEY_CAPSLOCK, timestamp++); keyboard_key_released(KEY_CAPSLOCK, timestamp++); QTRY_COMPARE(input::xkb::get_active_keyboard_modifiers(*setup.base->mod.input), - Qt::ShiftModifier); + Qt::NoModifier); QTRY_COMPARE(triggeredSpy.count(), 1); // currently caps lock is on - // shift still triggers keyboard_key_pressed(modifier, timestamp++); keyboard_key_released(modifier, timestamp++); QTRY_COMPARE(input::xkb::get_active_keyboard_modifiers(*setup.base->mod.input), - Qt::ShiftModifier); + Qt::NoModifier); QTRY_COMPARE(triggeredSpy.count(), 2); // meta should also trigger diff --git a/autotests/integration/tabbox.cpp b/autotests/integration/tabbox.cpp index cd800407b..f9cbefb09 100644 --- a/autotests/integration/tabbox.cpp +++ b/autotests/integration/tabbox.cpp @@ -213,13 +213,12 @@ TEST_CASE("tabbox", "[win]") quint32 timestamp = 0; keyboard_key_pressed(KEY_CAPSLOCK, timestamp++); keyboard_key_released(KEY_CAPSLOCK, timestamp++); - QCOMPARE(input::xkb::get_active_keyboard_modifiers(*setup.base->mod.input), - Qt::ShiftModifier); + QCOMPARE(input::xkb::get_active_keyboard_modifiers(*setup.base->mod.input), Qt::NoModifier); // press alt+tab keyboard_key_pressed(KEY_LEFTALT, timestamp++); REQUIRE(input::xkb::get_active_keyboard_modifiers(*setup.base->mod.input) - == (Qt::ShiftModifier | Qt::AltModifier)); + == Qt::AltModifier); keyboard_key_pressed(KEY_TAB, timestamp++); keyboard_key_released(KEY_TAB, timestamp++); @@ -239,9 +238,9 @@ TEST_CASE("tabbox", "[win]") QCOMPARE(setup.base->mod.space->tabbox->is_grabbed(), false); // Has walked backwards to the previously lowest client in the stacking order. - QCOMPARE(get_wayland_window(setup.base->mod.space->stacking.active), c1); + QCOMPARE(get_wayland_window(setup.base->mod.space->stacking.active), c2); QCOMPARE(setup.base->mod.space->stacking.order.stack, - (std::deque{c2, c3, c1})); + (std::deque{c1, c3, c2})); surface3.reset(); QVERIFY(wait_for_destroyed(c3)); diff --git a/como/input/xkb/keyboard.cpp b/como/input/xkb/keyboard.cpp index 1fbf7fca8..ae58e33ae 100644 --- a/como/input/xkb/keyboard.cpp +++ b/como/input/xkb/keyboard.cpp @@ -148,8 +148,7 @@ void keyboard::update_modifiers() constexpr auto is_active = xkb_state_mod_index_is_active; auto mods = Qt::KeyboardModifiers(); - if (is_active(state, modifiers_indices.shift, XKB_STATE_MODS_EFFECTIVE) == 1 - || is_active(state, modifiers_indices.caps, XKB_STATE_MODS_EFFECTIVE) == 1) { + if (is_active(state, modifiers_indices.shift, XKB_STATE_MODS_EFFECTIVE) == 1) { mods |= Qt::ShiftModifier; } if (is_active(state, modifiers_indices.alt, XKB_STATE_MODS_EFFECTIVE) == 1) { From 1507af79948e7c50d721451bcb6997e6d7dc3e9e Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 15 Feb 2024 09:58:35 +0000 Subject: [PATCH 68/76] fix: only show otherScreenThumbnail if we are actually dragging otherScreenThumbnail is used to fake a window thumbnail being dragged half in the old screen, half in the "new" one (or even more than two) right now the condition to use it is purely the "real" thumbnail x or y change, but sometimes especially when the item has just been created and is being laid out it might trigger an itemDraggedOutOfScreen when no-one was dragging. We should never make otherScreenThumbnail visible when we aren't dragging a thumbnail, so check for Drag being active in the source item. the item will be kept visible even if drag becomes inactive as before, as it still needs to be visible for the reset animation BUG:480564 --- plugins/effects/private/qml/WindowHeap.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/effects/private/qml/WindowHeap.qml b/plugins/effects/private/qml/WindowHeap.qml index 2f648dfa1..14a974313 100644 --- a/plugins/effects/private/qml/WindowHeap.qml +++ b/plugins/effects/private/qml/WindowHeap.qml @@ -103,7 +103,9 @@ FocusScope { otherScreenThumbnail.cloneOf = item otherScreenThumbnail.x = heapRelativePos.x; otherScreenThumbnail.y = heapRelativePos.y; - otherScreenThumbnail.visible = true; + if (item.Drag.active) { + otherScreenThumbnail.visible = true; + } } } From 3b61f1f0ee024a3f9832e2bced82637e5823786a Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Wed, 14 Feb 2024 16:56:27 +0000 Subject: [PATCH 69/76] fix(wl): dispatch mouse events to internal windows via QWindowSystemInterface QWindowSystemInterface goes via QGuiApplication which updates some internal properties. Most notably QGuiApplication::lastCursorPosition which is used by advanced menu closing behaviour. BUG: 478061 --- como/input/filters/internal_window.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/como/input/filters/internal_window.h b/como/input/filters/internal_window.h index 6795aaa4f..64b4a007c 100644 --- a/como/input/filters/internal_window.h +++ b/como/input/filters/internal_window.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace como::input { @@ -51,15 +52,14 @@ class internal_window_filter : public event_filter } auto qt_event = button_to_qt_event(*this->redirect.pointer, event); - auto adapted_qt_event = QMouseEvent(qt_event.type(), - qt_event.pos() - internal->position(), - qt_event.pos(), - qt_event.button(), - qt_event.buttons(), - qt_event.modifiers()); - adapted_qt_event.setAccepted(false); - QCoreApplication::sendEvent(internal, &adapted_qt_event); - return adapted_qt_event.isAccepted(); + return QWindowSystemInterface::handleMouseEvent< + QWindowSystemInterface::SynchronousDelivery>(internal, + qt_event.position() - internal->position(), + qt_event.globalPosition(), + qt_event.buttons(), + qt_event.button(), + qt_event.type(), + qt_event.modifiers()); } bool motion(motion_event const& event) override From f2672fe9298a2b64d7262dd189abfb68cdbe11fc Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 15 Feb 2024 15:08:00 +0200 Subject: [PATCH 70/76] fix: allow switching between modes using shortcuts while already active Overview and Grid modes have shortcuts assigned to them. While they provide a way to toggle the overview effect between on and off state, in other words overview <-> off or grid <-> off, it's not possible to move between the modes by pressing those shortcuts, e.g. off -> overview -> grid -> overview -> grid -> ... -> off. The culprit seems to be that EffectTogglableState has two "inactive" states - Inactive and Stopped. It's counter-intuitive and needs a further cleanup. To make switching between overview modes work, this change makes EffectTogglableState::toggle() toggle the state based on the Active state. There's only one active state. CCBUG: 481335 --- como/render/effect/interface/effect_togglable_state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/como/render/effect/interface/effect_togglable_state.cpp b/como/render/effect/interface/effect_togglable_state.cpp index 4b22763dd..81f3dfb70 100644 --- a/como/render/effect/interface/effect_togglable_state.cpp +++ b/como/render/effect/interface/effect_togglable_state.cpp @@ -112,7 +112,7 @@ void EffectTogglableState::partialDeactivate(qreal factor) void EffectTogglableState::toggle() { - if (m_status == Status::Inactive) { + if (m_status != Status::Active) { activate(); Q_EMIT activated(); } else { From 111b2ee399bc382cdc29400b52ee01e38bed72b5 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Mon, 19 Feb 2024 14:46:07 +0000 Subject: [PATCH 71/76] fix: only handled input events in on-screen desktops kwin does not support true multiscreen drag and drops. Events are sent to an offscreen location of the screen initiating the drag. Therefore it is important that off-screen items do not process drop events BUG: 481331 --- plugins/effects/overview/qml/main.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/effects/overview/qml/main.qml b/plugins/effects/overview/qml/main.qml index bd46abdf5..fcc02606c 100644 --- a/plugins/effects/overview/qml/main.qml +++ b/plugins/effects/overview/qml/main.qml @@ -412,7 +412,11 @@ FocusScope { Item { id: mainBackground + // visible when in the overview 'grid' mode, but also keep neighbouring items visible as they can appear during gestures visible: gridVal > 0 || nearCurrent + // Avoid handling drops for offscreen visible items + enabled: gridVal > 0 || current + anchors.fill: parent property bool shouldBeVisibleInOverview: !(effect.searchText.length > 0 && current) || (heap.count !== 0 && effect.filterWindows) opacity: 1 - overviewVal * (shouldBeVisibleInOverview ? 0 : 1) @@ -566,6 +570,7 @@ FocusScope { if (!mainBackground.contains(mainBackground.mapFromItem(null, pos.x, pos.y))) { return; } + // moving the window to the client screen is handled by WindowHeap's similiarly named function that is also run item.client.desktops = [mainBackground.desktop]; } } From a7eb3799e0a230bbd6eacc1bab4282655a40229c Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Mon, 19 Feb 2024 21:31:58 +0100 Subject: [PATCH 72/76] fix: set componentDisplayName for shortcut migration Otherwise the binary name is used as display name, causing all kwin shortcuts to be registered under kwin-6.0-delete-desktop-switching-shortcuts --- kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp b/kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp index 5aba1bab7..5acff0ed5 100644 --- a/kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp +++ b/kconf_update/kwin-6.0-delete-desktop-switching-shortcuts.cpp @@ -25,6 +25,7 @@ int main(int argc, char **argv) QAction action; action.setObjectName(actionName); action.setProperty("componentName", QStringLiteral("kwin")); + action.setProperty("componentDisplayName", QStringLiteral("KWin")); KGlobalAccel::self()->setShortcut(&action, {QKeySequence()}, KGlobalAccel::NoAutoloading); KGlobalAccel::self()->removeAllShortcuts(&action); } From 37bc2025ec82031c8cae8ab42f9be3642b2976f6 Mon Sep 17 00:00:00 2001 From: Michael VanOverbeek Date: Tue, 20 Feb 2024 20:23:35 +0000 Subject: [PATCH 73/76] fix: fix zoom push mouse tracking on multi-monitor workspaces Zoom push tracking now considers the layout of the user's monitors, accounting for situations where the monitor layout doesn't form a perfect rectangle. These changes help prevent the zoom area from being unable to reach certain areas of the workspace depending on which edge of which screen the user pushes against. One known issue is that, if the mouse moves too quickly, the zoom area can sometimes imperfectly track the movement. It will look the same as the original bug (areas of the screen will appear to be cut off/unreachable), but moving the mouse in the opposite direction a tiny bit snaps the zoom area back to where it should be. BUG: 467182 @teams/qa Heads-up that I'm very blind, and this is the first time I've ever contributed to a KDE project. I've tested the changes on my system and they fix the bug, but I want to make sure I didn't break anything in the process. --- plugins/effects/zoom/zoom.cpp | 49 +++++++++++++++++++++++++---------- plugins/effects/zoom/zoom.h | 3 +++ 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/plugins/effects/zoom/zoom.cpp b/plugins/effects/zoom/zoom.cpp index 3d348833b..20f527008 100644 --- a/plugins/effects/zoom/zoom.cpp +++ b/plugins/effects/zoom/zoom.cpp @@ -365,25 +365,42 @@ void ZoomEffect::paintScreen(effect::screen_paint_data& data) break; case MouseTrackingPush: { // touching an edge of the screen moves the zoom-area in that direction. - int x = cursorPoint.x() * zoom - prevPoint.x() * (zoom - 1.0); - int y = cursorPoint.y() * zoom - prevPoint.y() * (zoom - 1.0); - int threshold = 4; + const int x = cursorPoint.x() * zoom - prevPoint.x() * (zoom - 1.0); + const int y = cursorPoint.y() * zoom - prevPoint.y() * (zoom - 1.0); + const int threshold = 4; + const QRectF currScreen = effects->screenAt(QPoint(x, y))->geometry(); + + // bounds of the screen the cursor's on + const int screenTop = currScreen.top(); + const int screenLeft = currScreen.left(); + const int screenRight = currScreen.right(); + const int screenBottom = currScreen.bottom(); + const int screenCenterX = currScreen.center().x(); + const int screenCenterY = currScreen.center().y(); + + // figure out whether we have adjacent displays in all 4 directions + // We pan within the screen in directions where there are no adjacent screens. + const bool adjacentLeft = screenExistsAt(QPoint(screenLeft - 1, screenCenterY)); + const bool adjacentRight = screenExistsAt(QPoint(screenRight + 1, screenCenterY)); + const bool adjacentTop = screenExistsAt(QPoint(screenCenterX, screenTop - 1)); + const bool adjacentBottom = screenExistsAt(QPoint(screenCenterX, screenBottom + 1)); + xMove = yMove = 0; - if (x < threshold) { - xMove = (x - threshold) / zoom; - } else if (x + threshold > screenSize.width()) { - xMove = (x + threshold - screenSize.width()) / zoom; + if (x < screenLeft + threshold && !adjacentLeft) { + xMove = (x - threshold - screenLeft) / zoom; + } else if (x > screenRight - threshold && !adjacentRight) { + xMove = (x + threshold - screenRight) / zoom; } - if (y < threshold) { - yMove = (y - threshold) / zoom; - } else if (y + threshold > screenSize.height()) { - yMove = (y + threshold - screenSize.height()) / zoom; + if (y < screenTop + threshold && !adjacentTop) { + yMove = (y - threshold - screenTop) / zoom; + } else if (y > screenBottom - threshold && !adjacentBottom) { + yMove = (y + threshold - screenBottom) / zoom; } if (xMove) { - prevPoint.setX(qMax(0, qMin(screenSize.width(), prevPoint.x() + xMove))); + prevPoint.setX(prevPoint.x() + xMove); } if (yMove) { - prevPoint.setY(qMax(0, qMin(screenSize.height(), prevPoint.y() + yMove))); + prevPoint.setY(prevPoint.y() + yMove); } data.paint.geo.translation.setX(-int(prevPoint.x() * (zoom - 1.0))); data.paint.geo.translation.setY(-int(prevPoint.y() * (zoom - 1.0))); @@ -666,4 +683,10 @@ qreal ZoomEffect::targetZoom() const return target_zoom; } +bool ZoomEffect::screenExistsAt(const QPoint& point) const +{ + auto output = effects->screenAt(point); + return output && output->geometry().contains(point); +} + } // namespace diff --git a/plugins/effects/zoom/zoom.h b/plugins/effects/zoom/zoom.h index 866d61faa..146dba359 100644 --- a/plugins/effects/zoom/zoom.h +++ b/plugins/effects/zoom/zoom.h @@ -55,6 +55,9 @@ class ZoomEffect : public Effect int configuredFocusDelay() const; qreal configuredMoveFactor() const; qreal targetZoom() const; + +private: + bool screenExistsAt(const QPoint& point) const; private Q_SLOTS: inline void zoomIn() { From 740042ac6772f821b1d7361c7645aeb2a92db3aa Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 20 Feb 2024 17:22:05 +0100 Subject: [PATCH 74/76] feat: remove legacy virtual desktop number from the menu In almost all cases it's duplicated in the name, and if the user manually changes the name, the custom name should be shown without additional numbers CCBUG: 481576 Co-authored-by: Vlad Zahorodnii --- como/win/user_actions_menu.h | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/como/win/user_actions_menu.h b/como/win/user_actions_menu.h index 9783b22e0..c6471c420 100644 --- a/como/win/user_actions_menu.h +++ b/como/win/user_actions_menu.h @@ -281,16 +281,9 @@ class user_actions_menu } m_desktopMenu->addSeparator(); - const uint BASE = 10; - for (uint i = 1; i <= subs_manager->subspaces.size(); ++i) { - QString basic_name(QStringLiteral("%1 %2")); - if (i < BASE) { - basic_name.prepend(QLatin1Char('&')); - } - action = m_desktopMenu->addAction( - basic_name.arg(i).arg(subspace_manager_get_subspace_name(*subs_manager, i) - .replace(QLatin1Char('&'), QStringLiteral("&&")))); + action = m_desktopMenu->addAction(subspace_manager_get_subspace_name(*subs_manager, i) + .replace(QLatin1Char('&'), QStringLiteral("&&"))); action->setData(i); action->setCheckable(true); group->addAction(action); @@ -343,17 +336,10 @@ class user_actions_menu m_multipleDesktopsMenu->addSeparator(); - const uint BASE = 10; - for (uint i = 1; i <= subs_manager->subspaces.size(); ++i) { - QString basic_name(QStringLiteral("%1 %2")); - if (i < BASE) { - basic_name.prepend(QLatin1Char('&')); - } - - QAction* action = m_multipleDesktopsMenu->addAction( - basic_name.arg(i).arg(subspace_manager_get_subspace_name(*subs_manager, i) - .replace(QLatin1Char('&'), QStringLiteral("&&")))); + auto action = m_multipleDesktopsMenu->addAction( + subspace_manager_get_subspace_name(*subs_manager, i) + .replace(QLatin1Char('&'), QStringLiteral("&&"))); action->setData(QVariant::fromValue(user_actions_menu_desktop_action_data{i, false})); action->setCheckable(true); if (m_client From be218a9751c2cbcdd48a01219dde4f0e3e445dcb Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 16 Feb 2024 11:14:22 +0200 Subject: [PATCH 75/76] feat: make screen edge toggle overview rather than cycle between modes The user may have no usecase for the grid view mode, i.e. they may want to activate overview, do their thing, and then return back to normal desktop. However, with the current behavior, there's one extra step (switch to grid mode) in order to go back to desktop. In hindsight, we should have added different screen edge actions for overview and grid modes. This can be done in 6.1. BUG: 481335 --- plugins/effects/overview/overvieweffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/effects/overview/overvieweffect.cpp b/plugins/effects/overview/overvieweffect.cpp index 92af24f87..9e6197a27 100644 --- a/plugins/effects/overview/overvieweffect.cpp +++ b/plugins/effects/overview/overvieweffect.cpp @@ -326,7 +326,7 @@ int OverviewEffect::requestedEffectChainPosition() const bool OverviewEffect::borderActivated(ElectricBorder border) { if (m_borderActivate.contains(border)) { - cycle(); + m_overviewState->toggle(); return true; } return false; From 572fb77506d8b91c3c758cdf90cecacb9b4f6fbb Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Sat, 24 Feb 2024 08:46:42 +0100 Subject: [PATCH 76/76] build: bump KDE dependencies version requirements We require KF6 and Plasma 6.0 versioned packages. --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b337666e..d487792a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ cmake_minimum_required(VERSION 3.23 FATAL_ERROR) project("The Compositor Modules" VERSION 0.1.0) set(QT_MIN_VERSION "6.6.0") -set(KF6_MIN_VERSION "5.240.0") -set(KDE_PLASMA_VERSION "5.27") +set(KF6_MIN_VERSION "6.0.0") +set(KDE_PLASMA_VERSION "6.0.0") set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 20) @@ -138,7 +138,7 @@ set_package_properties(KScreenLocker PROPERTIES PURPOSE "For screenlocker integration in the Wayland session" ) -find_package(Breeze 5.9.0 CONFIG) +find_package(Breeze 6.0.0 CONFIG) set_package_properties(Breeze PROPERTIES TYPE OPTIONAL PURPOSE "For setting the default window decoration plugin"