@@ -61,6 +61,26 @@ PluginComponent {
6161 root .close ();
6262 return " closed" ;
6363 }
64+
65+ /** Reset recording UI if interactive video setup fails (e.g. slurp cancelled). Called from bash. */
66+ function cancelRecording (): string {
67+ root .isRecording = false ;
68+ root .isPaused = false ;
69+ root .recordingElapsed = 0 ;
70+ return " cancelled" ;
71+ }
72+
73+ /** Show pill + timer only after region selection / portal begins recording (interactive video). Called from bash. */
74+ function recordingStarted (): string {
75+ root .isRecording = true ;
76+ root .isPaused = false ;
77+ root .recordingElapsed = 0 ;
78+ if (root .showNotify ) {
79+ let dirMsg = root .customPath !== " " ? root .customPath : " ~/Videos" ;
80+ Quickshell .execDetached ([" notify-send" , " Recording Started" , " Saving to " + dirMsg]);
81+ }
82+ return " started" ;
83+ }
6484 }
6585
6686
@@ -143,45 +163,61 @@ PluginComponent {
143163 let filename = " recording-" + timestamp + " ." + root .videoFormat ;
144164 let dir = root .customPath !== " " ? root .customPath .replace (/ ^ ~/ , " $HOME" ) : " $HOME/Videos" ;
145165 let path = dir + " /" + filename;
146-
166+
147167 let prepends = [];
148168 prepends .push (" export NIRI_SOCKET=$(ls /run/user/$(id -u)/niri*.sock 2>/dev/null | head -n 1)" );
149169 if (root .recordAudio ) {
150170 prepends .push (" SINK=$(pactl get-default-sink 2>/dev/null); if [ -n \" $SINK\" ]; then AUDIO=\" $SINK.monitor\" ; else AUDIO=\" default_output\" ; fi" );
151171 }
152172 prepends .push (" MONITOR=\"\" ; if command -v niri >/dev/null 2>&1; then MONITOR=$(niri msg -j outputs 2>/dev/null | jq -r 'keys[0]'); elif command -v hyprctl >/dev/null 2>&1; then MONITOR=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused) | .name'); fi; if [ -z \" $MONITOR\" ] || [ \" $MONITOR\" = \" null\" ]; then MONITOR=\" portal\" ; fi" );
153173
154- let gsrCmd = " gpu-screen-recorder" ;
174+ let gsrSuffix = " -c " + root .videoFormat ;
175+ gsrSuffix += " -f " + root .videoFPS ;
176+ if (root .recordAudio )
177+ gsrSuffix += " -a \" $AUDIO\" " ;
178+ gsrSuffix += root .showPointer ? " -cursor yes" : " -cursor no" ;
179+ gsrSuffix += " -o \" " + path + " \" " ;
180+ if (root .videoCodec !== " auto" )
181+ gsrSuffix += " -k " + root .videoCodec ;
182+
183+ let prelude = prepends .join (" ; " );
184+ let scriptBody;
155185 if (root .captureMode === " interactive" ) {
156- gsrCmd += " -w portal" ;
186+ // Portal alone is unreliable on niri / some Wayland compositors; use slurp + -w region when available.
187+ scriptBody =
188+ " cancel_rec() { command -v dms >/dev/null 2>&1 && ( dms ipc call screenCaptureToolbar cancelRecording 2>/dev/null || dms ipc screenCaptureToolbar cancelRecording 2>/dev/null ); }; " +
189+ " start_rec() { command -v dms >/dev/null 2>&1 && ( dms ipc call screenCaptureToolbar recordingStarted 2>/dev/null || dms ipc screenCaptureToolbar recordingStarted 2>/dev/null ); }; " +
190+ " sleep 0.2; mkdir -p \" " + dir + " \" ; " +
191+ " if command -v slurp >/dev/null 2>&1; then " +
192+ " REGION=$(slurp -f '%wx%h+%x+%y') || { cancel_rec; exit 1; }; " +
193+ " [ -z \" $REGION\" ] && { cancel_rec; exit 1; }; " +
194+ " start_rec; gpu-screen-recorder -w region -region \" $REGION\" " + gsrSuffix + " ; " +
195+ " else " +
196+ " start_rec; gpu-screen-recorder -w portal" + gsrSuffix + " ; " +
197+ " fi" ;
157198 } else {
158- gsrCmd += " -w \" $MONITOR\" " ;
199+ scriptBody = " sleep 0.2; mkdir -p \" " + dir + " \" ; gpu-screen-recorder -w \" $MONITOR\" " + gsrSuffix;
200+ }
201+
202+ let finalCmd = prelude !== " " ? prelude + " ; " + scriptBody : scriptBody;
203+
204+ let deferRecordingUi = root .captureMode === " interactive" ;
205+ if (! deferRecordingUi) {
206+ root .isRecording = true ;
207+ root .isPaused = false ;
208+ root .recordingElapsed = 0 ;
159209 }
160- gsrCmd += " -c " + root .videoFormat ;
161- gsrCmd += " -f " + root .videoFPS ;
162- if (root .recordAudio ) gsrCmd += " -a \" $AUDIO\" " ;
163- gsrCmd += root .showPointer ? " -cursor yes" : " -cursor no" ;
164- gsrCmd += " -o \" " + path + " \" " ;
165- if (root .videoCodec !== " auto" ) gsrCmd += " -k " + root .videoCodec ;
166-
167- let finalCmd = prepends .join (" ; " );
168- if (finalCmd !== " " ) finalCmd += " ; " ;
169- finalCmd += gsrCmd;
170-
171- root .isRecording = true ;
172- root .isPaused = false ;
173- root .recordingElapsed = 0 ;
174210 root .close ();
175-
176- Quickshell .execDetached ([" bash" , " -c" , " sleep 0.2; mkdir -p \" " + dir + " \" ; " + finalCmd]);
177-
178- if (root .showNotify ) {
211+
212+ Quickshell .execDetached ([" bash" , " -c" , finalCmd]);
213+
214+ if (root .showNotify && ! deferRecordingUi ) {
179215 Quickshell .execDetached ([" notify-send" , " Recording Started" , " Saving to " + dir]);
180216 }
181217 }
182218
183219 function stopRecording () {
184- Quickshell .execDetached ([" pkill" , " -SIGINT" , " gpu-screen-recorder" ]);
220+ Quickshell .execDetached ([" pkill" , " -SIGINT" , " -f " , " ^ gpu-screen-recorder" ]);
185221 root .isRecording = false ;
186222 root .isPaused = false ;
187223 root .recordingElapsed = 0 ;
@@ -192,12 +228,12 @@ PluginComponent {
192228 }
193229
194230 function pauseRecording () {
195- Quickshell .execDetached ([" bash " , " -c " , " killall -SIGUSR2 gpu-screen-recorder" ]);
231+ Quickshell .execDetached ([" pkill " , " -SIGUSR2 " , " -f " , " ^ gpu-screen-recorder" ]);
196232 root .isPaused = true ;
197233 }
198234
199235 function resumeRecording () {
200- Quickshell .execDetached ([" bash " , " -c " , " killall -SIGUSR2 gpu-screen-recorder" ]);
236+ Quickshell .execDetached ([" pkill " , " -SIGUSR2 " , " -f " , " ^ gpu-screen-recorder" ]);
201237 root .isPaused = false ;
202238 }
203239
0 commit comments