Krolyxon
Commit a1aa759

Branches

Refs →

auroraa1aa759

aurora: fix screen recording stop/cancel

slurp ran inside the recorder's shell, so the bar showed a running timer
while still selecting and SIGINT never reached anything. Selection and
recording are now separate processes, the bar pill stops or cancels both,
and wf-recorder output goes to ~/.cache/aurora/rec.log (on the Process
pipe it exited without writing a file).
Author
krolyxon
Date
2026-10-17 15:13 +0530
Parent
f0edd5d
Commit
a1aa759facb2cf80e4a9f1c55f34b675384bb3e0

+56 −23

3 files changed

  1. M .config/quickshell/aurora/Bar.qml +4 −4
  2. M .config/quickshell/aurora/HomePage.qml +4 −4
  3. M .config/quickshell/aurora/Rec.qml +48 −15
M .config/quickshell/aurora/Bar.qml View
@@ -95,10 +95,10 @@ PanelWindow {
9595
9696 Tray { show: bar.monFocused }
9797
98− BarPill {
99− visible: Rec.running
100− icon: Theme.iRec
101− text: Theme.fmtClock(Rec.elapsed)
98+ BarPill { // click to stop recording / cancel the area selection
99+ visible: Rec.busy
100+ icon: Theme.iStop
101+ text: Rec.running ? "rec " + Theme.fmtClock(Rec.elapsed) : "select area"
102102 tint: Theme.err
103103 underline: Theme.err
104104 onActivated: Rec.stop()
M .config/quickshell/aurora/HomePage.qml View
@@ -122,10 +122,10 @@ ColumnLayout {
122122 IconBtn { // click: record a region; while recording: stop
123123 Layout.fillWidth: true
124124 visible: Rec.available
125− icon: Rec.running ? Theme.iStop : Theme.iRec
126− label: Rec.running ? Theme.fmtClock(Rec.elapsed) : "rec"
127− active: Rec.running
128− onClicked: { if (!Rec.running) page.closeRequested(); Rec.toggle("region") }
125+ icon: Rec.busy ? Theme.iStop : Theme.iRec
126+ label: Rec.running ? Theme.fmtClock(Rec.elapsed) : (Rec.selecting ? "cancel" : "rec")
127+ active: Rec.busy
128+ onClicked: { if (!Rec.busy) page.closeRequested(); Rec.toggle("region") }
129129 }
130130 }
131131
M .config/quickshell/aurora/Rec.qml View
@@ -5,37 +5,70 @@ import Quickshell.Io
55 import Quickshell.Hyprland
66
77 // Screen recording with wf-recorder (region via slurp, or the focused monitor), saved to ~/vids/rec.
8−// Everything is hidden if wf-recorder isn't installed.
8+// Two steps so each can be cancelled: `selecting` (slurp is up) then `running` (wf-recorder records).
9+// stop() works in both. Everything is hidden if wf-recorder / slurp aren't installed.
910 Singleton {
1011 id: root
1112 property bool available: false
12− readonly property bool running: proc.running
13+ readonly property bool running: recProc.running
14+ readonly property bool selecting: delay.running || selectProc.running
15+ readonly property bool busy: running || selecting
1316 property string dir: Quickshell.env("HOME") + "/vids/rec"
1417 property string file: ""
18+ readonly property string log: Quickshell.env("HOME") + "/.cache/aurora/rec.log"
19+ property string mode: "region"
1520 property int elapsed: 0 // seconds
1621
17− function start(mode) { // "region" | "screen"
18− if (!available || proc.running) return
22+ function start(m) { // "region" | "screen"
23+ if (!available || busy) return
24+ mode = m
25+ delay.start() // let the panel close before slurp grabs the screen
26+ }
27+ function stop() {
28+ if (delay.running) delay.stop()
29+ if (selectProc.running) selectProc.signal(15)
30+ if (recProc.running) recProc.signal(2) // SIGINT: wf-recorder finalises the file
31+ }
32+ function toggle(m) { if (busy) stop(); else start(m) }
33+
34+ function record(geometry) {
1935 file = dir + "/" + Qt.formatDateTime(new Date(), "yyyy-MM-dd_HH-mm-ss") + ".mp4"
20− var m = Hyprland.focusedMonitor
21− // exec so the Process pid is wf-recorder itself and SIGINT reaches it
22− proc.command = mode === "region"
23− ? ["sh", "-c", 'mkdir -p "${1%/*}"; g=$(slurp) || exit 1; exec wf-recorder -g "$g" -f "$1"', "sh", file]
24− : ["sh", "-c", 'mkdir -p "${1%/*}"; exec wf-recorder -o "$2" -f "$1"', "sh", file, m ? m.name : ""]
36+ var mon = Hyprland.focusedMonitor
37+ // exec so the Process pid is wf-recorder itself and SIGINT reaches it.
38+ // its (very chatty) output must go to a file: left on the Process pipe it dies without writing anything
39+ var run = 'mkdir -p "${1%/*}" "${3%/*}"; exec wf-recorder ' + (geometry !== "" ? '-g' : '-o') + ' "$2" -f "$1" >"$3" 2>&1'
40+ recProc.command = ["sh", "-c", run, "sh", file, geometry !== "" ? geometry : (mon ? mon.name : ""), log]
2541 elapsed = 0
26− proc.running = true
42+ recProc.running = true
2743 }
28− function stop() { if (proc.running) proc.signal(2) } // SIGINT: wf-recorder finalises the file
29− function toggle(mode) { if (proc.running) stop(); else start(mode) }
3044
45+ Timer {
46+ id: delay
47+ interval: 250
48+ onTriggered: {
49+ if (root.mode === "region") selectProc.running = true
50+ else root.record("")
51+ }
52+ }
53+ Process {
54+ id: selectProc
55+ command: ["slurp"]
56+ // slurp prints nothing when cancelled (Esc, or stop() killing it)
57+ stdout: StdioCollector {
58+ onStreamFinished: {
59+ var g = text.trim()
60+ if (g !== "") root.record(g)
61+ }
62+ }
63+ }
3164 Process {
32− id: proc
33− onExited: code => {
65+ id: recProc
66+ onExited: {
3467 Quickshell.execDetached(["sh", "-c",
3568 '[ -s "$1" ] && notify-send -a aurora "recording saved" "$1"', "sh", root.file])
3669 }
3770 }
38− Timer { interval: 1000; repeat: true; running: proc.running; onTriggered: root.elapsed++ }
71+ Timer { interval: 1000; repeat: true; running: recProc.running; onTriggered: root.elapsed++ }
3972
4073 Process {
4174 command: ["sh", "-c", "command -v wf-recorder && command -v slurp"]