Skip to content

Commit b53f3db

Browse files
committed
improved SCV implementation using grabToImage
1 parent 055467b commit b53f3db

2 files changed

Lines changed: 88 additions & 37 deletions

File tree

Modules/LockScreen/LockScreen.qml

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,20 @@ import qs.Services.Compositor
99
import qs.Services.Hardware
1010
import qs.Services.Keyboard
1111
import qs.Services.Media
12+
import qs.Services.Power
1213
import qs.Services.UI
1314
import qs.Widgets
1415

1516
Loader {
1617
id: root
18+
19+
// Starts the lock screen when activated
1720
active: false
1821

22+
// Only controls the state of the session lock
23+
// Decoupled because we need to capture the screen first
24+
property bool locked: false
25+
1926
// Track if the visualizer should be shown (lockscreen active + media playing + non-compact mode)
2027
readonly property bool needsSpectrum: root.active && !Settings.data.general.compactLockScreen && Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
2128

@@ -31,6 +38,10 @@ Loader {
3138
} else {
3239
LockKeysService.unregisterComponent("lockscreen");
3340
}
41+
42+
// Trigger locksreen when loader is active
43+
if (root.active)
44+
startLockScreen();
3445
}
3546

3647
onNeedsSpectrumChanged: {
@@ -51,24 +62,82 @@ Loader {
5162
LockKeysService.unregisterComponent("lockscreen");
5263
}
5364

65+
// Dictionary of screen name to ItemGrabResult of screen captures
66+
property var screenCaptures: ({})
67+
68+
// Wait until all screens are captured before locking
69+
onScreenCapturesChanged: {
70+
let captureCount = Object.keys(screenCaptures).length;
71+
if (captureCount > 0 && captureCount === Quickshell.screens.length) {
72+
root.locked = true;
73+
}
74+
}
75+
76+
// When lock is requested, clear previous captures and activate SCVs
77+
function startLockScreen() {
78+
screenCaptures = {};
79+
screenCopies.active = true;
80+
}
81+
5482
Timer {
5583
id: unloadAfterUnlockTimer
5684
interval: 250
5785
repeat: false
5886
onTriggered: root.active = false
5987
}
6088

61-
function scheduleUnloadAfterUnlock() {
89+
function unlockAndUnload() {
90+
locked = false;
91+
screenCopies.active = false;
6292
unloadAfterUnlockTimer.start();
6393
}
6494

95+
// Image grabber for each connected screen
96+
Loader {
97+
id: screenCopies
98+
active: false
99+
100+
sourceComponent: Instantiator {
101+
model: Quickshell.screens
102+
103+
delegate: PanelWindow {
104+
anchors {
105+
top: true
106+
left: true
107+
right: true
108+
bottom: true
109+
}
110+
exclusionMode: ExclusionMode.Ignore
111+
screen: modelData
112+
color: "transparent"
113+
114+
ScreencopyView {
115+
anchors.fill: parent
116+
captureSource: screen
117+
118+
// Only grab an image when content is ready
119+
onHasContentChanged: {
120+
if (hasContent) {
121+
grabToImage(function (result) {
122+
// Store a ref to the result to avoid GC
123+
root.screenCaptures[screen.name] = result;
124+
// Manually call changed to trigger mutation signal
125+
root.screenCapturesChanged();
126+
});
127+
}
128+
}
129+
}
130+
}
131+
}
132+
}
133+
65134
sourceComponent: Component {
66135
Item {
67136
id: lockContainer
68137

69-
readonly property bool transitionsEnabled: Settings.data.general.lockScreenAnimations
138+
readonly property bool canTransition: Settings.data.general.lockScreenAnimations && !PowerProfileService.noctaliaPerformanceMode
70139

71-
property real transitionProgress: transitionsEnabled ? 0.0 : 1.0
140+
property real transitionProgress: canTransition ? 0.0 : 1.0
72141

73142
NumberAnimation {
74143
id: lockEnter
@@ -77,12 +146,11 @@ Loader {
77146
to: 1.0
78147
duration: Style.animationNormal
79148
easing.type: Easing.OutCubic
80-
running: transitionsEnabled
149+
running: root.locked && canTransition
81150
}
82151

83152
function unlockScreen() {
84-
lockSession.locked = false;
85-
root.scheduleUnloadAfterUnlock();
153+
root.unlockAndUnload();
86154
lockContext.currentText = "";
87155
}
88156

@@ -99,7 +167,7 @@ Loader {
99167
LockContext {
100168
id: lockContext
101169
onUnlocked: {
102-
if (transitionsEnabled) {
170+
if (canTransition) {
103171
lockEnter.stop();
104172
lockExit.start();
105173
return;
@@ -121,10 +189,11 @@ Loader {
121189

122190
WlSessionLock {
123191
id: lockSession
124-
locked: root.active
192+
locked: root.locked
125193

126194
WlSessionLockSurface {
127195
id: lockSurface
196+
color: "black"
128197

129198
Loader {
130199
anchors.fill: parent
@@ -156,6 +225,7 @@ Loader {
156225
LockScreenBackground {
157226
id: backgroundComponent
158227
screen: lockSurface.screen
228+
screenGrab: lockSurface.screen ? (root.screenCaptures[lockSurface.screen.name] || null) : null
159229
transitionProgress: lockContainer.transitionProgress
160230
}
161231

Modules/LockScreen/LockScreenBackground.qml

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ Item {
1111
id: root
1212
anchors.fill: parent
1313

14+
// Screen of the lock surface
1415
required property ShellScreen screen
1516

17+
// ItemGrabResult of the screen copy for this surface
18+
required property var screenGrab
19+
1620
// Cached wallpaper path - exposed for parent components
1721
property string resolvedWallpaperPath: ""
1822

@@ -111,39 +115,16 @@ Item {
111115
});
112116
}
113117

114-
// Opaque black at the very bottom to prevent white flash
115-
Rectangle {
116-
anchors.fill: parent
117-
color: "black"
118-
}
119-
120118
// A copy of the screen to use as background
121119
// Also used as fade in / fade out backdrop
122-
ScreencopyView {
120+
Image {
123121
id: lockBgScreencopy
124122
anchors.fill: parent
125-
captureSource: screen
126-
visible: lockBgRender.visible && lockBgRender.opacity < 0.99
127-
128-
// This is to observe a bug where screen is null on the first lock
129-
// and is only a valid value after 1ms into the lock screen...
130-
Timer {
131-
id: recaptureWhenEmpty
132-
interval: 1
133-
repeat: false
134-
onTriggered: {
135-
Logger.w("LockScreenBackground", "Screen after is: ", screen);
136-
// there's no use for this after the screen's already locked
137-
// lockBgScreencopy.captureFrame()
138-
}
139-
}
123+
source: screenGrab ? screenGrab.url : ""
140124

141-
Component.onCompleted: {
142-
if (!hasContent) {
143-
Logger.w("LockScreenBackground", "Screen now is: ", screen);
144-
recaptureWhenEmpty.start();
145-
}
146-
}
125+
cache: false
126+
fillMode: Image.PreserveAspectCrop
127+
visible: screenGrab && lockBgRender.visible && lockBgRender.opacity < 1.0
147128
}
148129

149130
// If using a solid color wallpaper
@@ -171,7 +152,7 @@ Item {
171152
id: lockBgRender
172153
anchors.fill: parent
173154
opacity: transitionProgress
174-
visible: useEffects && (useScreencopy || useWallpaper)
155+
visible: useEffects && ((screenGrab && useScreencopy) || useWallpaper)
175156
source: useScreencopy ? lockBgScreencopy : lockBgImage
176157

177158
blurEnabled: Settings.data.general.lockScreenBlur > 0

0 commit comments

Comments
 (0)