| @@ -5,37 +5,70 @@ import Quickshell.Io |
| 5 | 5 | | import Quickshell.Hyprland |
| 6 | 6 | | |
| 7 | 7 | | // 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. |
| 9 | 10 | | Singleton { |
| 10 | 11 | | id: root |
| 11 | 12 | | 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 |
| 13 | 16 | | property string dir: Quickshell.env("HOME") + "/vids/rec" |
| 14 | 17 | | property string file: "" |
| 18 | + | readonly property string log: Quickshell.env("HOME") + "/.cache/aurora/rec.log" |
| 19 | + | property string mode: "region" |
| 15 | 20 | | property int elapsed: 0 // seconds |
| 16 | 21 | | |
| 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) { |
| 19 | 35 | | 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] |
| 25 | 41 | | elapsed = 0 |
| 26 | | − | proc.running = true |
| 42 | + | recProc.running = true |
| 27 | 43 | | } |
| 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) } |
| 30 | 44 | | |
| 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 | + | } |
| 31 | 64 | | Process { |
| 32 | | − | id: proc |
| 33 | | − | onExited: code => { |
| 65 | + | id: recProc |
| 66 | + | onExited: { |
| 34 | 67 | | Quickshell.execDetached(["sh", "-c", |
| 35 | 68 | | '[ -s "$1" ] && notify-send -a aurora "recording saved" "$1"', "sh", root.file]) |
| 36 | 69 | | } |
| 37 | 70 | | } |
| 38 | | − | Timer { interval: 1000; repeat: true; running: proc.running; onTriggered: root.elapsed++ } |
| 71 | + | Timer { interval: 1000; repeat: true; running: recProc.running; onTriggered: root.elapsed++ } |
| 39 | 72 | | |
| 40 | 73 | | Process { |
| 41 | 74 | | command: ["sh", "-c", "command -v wf-recorder && command -v slurp"] |