quickshell: run slurp from the recorder card, not inside the script
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
87217f4675
commit
0d0009ca8e
1 changed files with 79 additions and 26 deletions
|
|
@ -106,12 +106,21 @@ in
|
|||
pw=$(${pkgs.zenity}/bin/zenity --password --title="WiFi Password" 2>/dev/null)
|
||||
[ -n "$pw" ] && ${pkgs.networkmanager}/bin/nmcli device wifi connect "$ssid" password "$pw"
|
||||
'';
|
||||
# Screen recording, driven by the quick-settings card. The shell execs
|
||||
# into wf-recorder so the card's SIGINT lands on the recorder itself —
|
||||
# wf-recorder finalises the mp4 on SIGINT, killing a wrapper instead
|
||||
# would leave a truncated file. The path is echoed before the exec so
|
||||
# the card can offer the result. Audio means the default sink's monitor
|
||||
# (desktop sound), not the microphone.
|
||||
# Screen recording, driven by the quick-settings card. $1 is a slurp
|
||||
# geometry ("" = whole output), $2 is "audio" or anything else.
|
||||
#
|
||||
# The region pick deliberately does NOT happen here. A wrapper parked
|
||||
# in $(slurp) is a process the card cannot stop: non-interactive bash
|
||||
# defers a signal until its foreground child exits, so Stop did
|
||||
# nothing, and the wrapper's lifetime made the card claim it was
|
||||
# recording while slurp was still waiting for a drag. The card runs
|
||||
# slurp itself and passes the result in.
|
||||
#
|
||||
# Everything before the exec is a few ms of setup, so the process the
|
||||
# card signals is wf-recorder itself — which finalises the mp4 on
|
||||
# SIGINT, where killing a wrapper would leave a truncated file. The
|
||||
# path is echoed first so the card can offer the result. Audio means
|
||||
# the default sink's monitor (desktop sound), not the microphone.
|
||||
screenRecScript = pkgs.writeShellScript "screen-rec" ''
|
||||
out="$HOME/Videos/rec-$(${pkgs.coreutils}/bin/date +%Y%m%d-%H%M%S).mp4"
|
||||
${pkgs.coreutils}/bin/mkdir -p "$HOME/Videos"
|
||||
|
|
@ -121,12 +130,10 @@ in
|
|||
| ${pkgs.gnused}/bin/sed -n 's/.*node\.name = "\(.*\)".*/\1/p')
|
||||
[ -n "$sink" ] && audio="--audio=$sink.monitor"
|
||||
fi
|
||||
if [ "$1" = region ]; then
|
||||
geom=$(${pkgs.slurp}/bin/slurp) || exit 1
|
||||
echo "$out"
|
||||
exec ${pkgs.wf-recorder}/bin/wf-recorder $audio -g "$geom" -f "$out"
|
||||
fi
|
||||
echo "$out"
|
||||
if [ -n "$1" ]; then
|
||||
exec ${pkgs.wf-recorder}/bin/wf-recorder $audio -g "$1" -f "$out"
|
||||
fi
|
||||
exec ${pkgs.wf-recorder}/bin/wf-recorder $audio -f "$out"
|
||||
'';
|
||||
nmcli = "${pkgs.networkmanager}/bin/nmcli";
|
||||
|
|
@ -1417,6 +1424,7 @@ in
|
|||
readonly property string wxFetch: "${wxFetchScript}"
|
||||
readonly property string xdgOpen: "${pkgs.xdg-utils}/bin/xdg-open"
|
||||
readonly property string screenRec: "${screenRecScript}"
|
||||
readonly property string slurp: "${pkgs.slurp}/bin/slurp"
|
||||
readonly property string themeApply: "${themeApply}"
|
||||
}
|
||||
'';
|
||||
|
|
@ -6259,19 +6267,53 @@ in
|
|||
Card {
|
||||
id: recCard
|
||||
width: parent.width
|
||||
// recProc is wf-recorder itself, so `recording` is
|
||||
// true only while frames are actually being
|
||||
// written — the region pick is its own state.
|
||||
readonly property bool recording: recProc.running
|
||||
readonly property bool selecting: slurpProc.running
|
||||
property bool audio: false
|
||||
property int elapsed: 0
|
||||
property string lastFile: ""
|
||||
|
||||
function start(mode) {
|
||||
if (recording || selecting) return;
|
||||
lastFile = "";
|
||||
elapsed = 0;
|
||||
recProc.command = [Commands.screenRec, mode, recCard.audio ? "audio" : "silent"];
|
||||
recProc.running = true;
|
||||
// The panel would otherwise be in the shot —
|
||||
// and slurp needs the screen to itself.
|
||||
// slurp wants the screen to itself, and the
|
||||
// panel would otherwise be in frame.
|
||||
qsDropdown.animateClose();
|
||||
if (mode === "region") slurpProc.running = true;
|
||||
else launch("");
|
||||
}
|
||||
|
||||
function launch(geom) {
|
||||
elapsed = 0;
|
||||
recProc.command = [Commands.screenRec, geom, recCard.audio ? "audio" : "silent"];
|
||||
recProc.running = true;
|
||||
}
|
||||
|
||||
// Region pick lives here, not in the script: bash
|
||||
// blocked in $(slurp) swallows the Stop signal and
|
||||
// makes the card claim it is recording while slurp
|
||||
// is still waiting for a drag.
|
||||
Process {
|
||||
id: slurpProc
|
||||
command: [Commands.slurp]
|
||||
property string geom: ""
|
||||
// SplitParser, not StdioCollector: the line has
|
||||
// to be in hand by the time exited fires.
|
||||
stdout: SplitParser { onRead: data => slurpProc.geom = data.trim() }
|
||||
// Cancelled with Escape or right-click: slurp
|
||||
// prints nothing, so an empty geom is the
|
||||
// cancel signal and nothing starts. (Watching
|
||||
// running, not exited: exited's exitStatus
|
||||
// parameter is a C++ enum qmllint can't
|
||||
// resolve, and the exit code adds nothing.)
|
||||
onRunningChanged: {
|
||||
if (running) return;
|
||||
if (geom !== "") recCard.launch(geom);
|
||||
geom = "";
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
|
|
@ -6300,13 +6342,15 @@ in
|
|||
SIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "video"
|
||||
color: recCard.recording ? Theme.err : Theme.base05
|
||||
color: recCard.recording ? Theme.err
|
||||
: recCard.selecting ? Theme.base0D : Theme.base05
|
||||
font.pixelSize: 15
|
||||
}
|
||||
SText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: recCard.recording
|
||||
? Math.floor(recCard.elapsed / 60) + ":" + ("0" + (recCard.elapsed % 60)).slice(-2)
|
||||
: recCard.selecting ? "Drag a region"
|
||||
: "Screen Recording"
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
|
@ -6316,7 +6360,7 @@ in
|
|||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 6
|
||||
visible: !recCard.recording
|
||||
visible: !recCard.recording && !recCard.selecting
|
||||
SText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Audio"
|
||||
|
|
@ -6334,7 +6378,7 @@ in
|
|||
Row {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
visible: !recCard.recording
|
||||
visible: !recCard.recording && !recCard.selecting
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
|
|
@ -6369,26 +6413,35 @@ in
|
|||
}
|
||||
}
|
||||
|
||||
// Doubles as the way out of a region pick: if slurp
|
||||
// ever comes up without input (its grab can be
|
||||
// dropped the same way the bar's is), Escape never
|
||||
// reaches it and this is the only escape hatch.
|
||||
Rectangle {
|
||||
visible: recCard.recording
|
||||
visible: recCard.recording || recCard.selecting
|
||||
width: parent.width
|
||||
height: 26
|
||||
radius: Theme.radiusSmall
|
||||
color: Theme.err
|
||||
color: recCard.recording ? Theme.err : Theme.base02t
|
||||
border.color: Theme.base03
|
||||
border.width: recCard.recording ? 0 : 1
|
||||
|
||||
SText {
|
||||
anchors.centerIn: parent
|
||||
text: "Stop"
|
||||
text: recCard.recording ? "Stop" : "Cancel"
|
||||
font.pixelSize: 11
|
||||
color: Theme.base00
|
||||
color: recCard.recording ? Theme.base00 : Theme.base05
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
// SIGINT, not kill — wf-recorder writes the
|
||||
// container trailer on it.
|
||||
onClicked: recProc.signal(2)
|
||||
onClicked: {
|
||||
// SIGINT, not kill — wf-recorder writes
|
||||
// the container trailer on it.
|
||||
if (recCard.recording) recProc.signal(2);
|
||||
else slurpProc.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue