Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions bin/omarchy-capture-screenrecording
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ STOP_RECORDING="false"
# both files back at the fixed names this comment describes.
RUNTIME_DIR="${XDG_RUNTIME_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/omarchy}"
RECORDING_FILE="$RUNTIME_DIR/omarchy-screenrecord-filename"
GSR_SOCKET="$RUNTIME_DIR/omarchy-gsr.sock"
REGION_FILE="$RUNTIME_DIR/omarchy-screenrecord-region"
LOG_FILE=$([[ ${OMARCHY_SCREENRECORD_DEBUG:-false} == "true" ]] && echo "$RUNTIME_DIR/omarchy-screenrecord.log" || echo "/dev/null")

Expand Down Expand Up @@ -203,7 +204,7 @@ start_screenrecording() {
[[ -n $audio_devices ]] && audio_args+=(-a "$audio_devices" -ac aac)

echo "===== $(date '+%F %T') args: $* target: $target =====" >>"$LOG_FILE"
gpu-screen-recorder "${capture_args[@]}" -k auto -f 60 -fm cfr -fallback-cpu-encoding yes -o "$filename" "${audio_args[@]}" 2>>"$LOG_FILE" &
gpu-screen-recorder "${capture_args[@]}" -k auto -f 60 -fm cfr -fallback-cpu-encoding yes -ipc "$GSR_SOCKET" -o "$filename" "${audio_args[@]}" 2>>"$LOG_FILE" &
local pid=$!

while kill -0 $pid 2>/dev/null && [[ ! -f $filename ]]; do
Expand All @@ -217,22 +218,16 @@ start_screenrecording() {
}

stop_screenrecording() {
pkill -SIGINT -f "^gpu-screen-recorder" # SIGINT required to save video properly

# Wait a maximum of 5 seconds to finish before hard killing
local count=0
while pgrep -f "^gpu-screen-recorder" >/dev/null && ((count < 50)); do
sleep 0.1
count=$((count + 1))
done
# gsr-cli stop asks the instance on GSR_SOCKET to save and waits until it has.
local stopped=0
if gsr-cli -ipc "$GSR_SOCKET" stop >/dev/null 2>&1; then
stopped=1
fi

toggle_screenrecording_indicator
cleanup_webcam

if pgrep -f "^gpu-screen-recorder" >/dev/null; then
pkill -9 -f "^gpu-screen-recorder"
omarchy-notification-send -u critical -t 5000 "Screen recording error" "Recording process had to be force-killed. Video may be corrupted."
else
if ((stopped)); then
finalize_recording
local filename=$(cat "$RECORDING_FILE" 2>/dev/null)
echo "$filename"
Expand All @@ -252,17 +247,20 @@ stop_screenrecording() {
sleep 2
rm -f "$preview"
) &
fi

rm -f "$RECORDING_FILE"
rm -f "$RECORDING_FILE"
else
omarchy-notification-send -u critical -t 5000 "Screen recording error" "Could not stop the recording through gsr-cli."
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.
return 1
fi
}

toggle_screenrecording_indicator() {
omarchy-shell -q omarchy.indicators refresh
}

screenrecording_active() {
pgrep -f "^gpu-screen-recorder" >/dev/null
gsr-cli -ipc "$GSR_SOCKET" status >/dev/null 2>&1
}

finalize_recording() {
Expand Down
2 changes: 1 addition & 1 deletion default/omarchy/omarchy-menu.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"trigger.reminder": {"icon":"󰢌","label":"Reminder","aliases":["reminder"]},
"trigger.capture": {"icon":"","label":"Capture","aliases":["capture","screenshot","screenrecord","screen-record","screenrecording"]},
"trigger.capture.screenshot": {"icon":"","label":"Screenshot","action":"omarchy-capture-screenshot"},
"trigger.capture.screenrecord.stop": {"icon":"","label":"Stop Screenrecording","when":"pgrep -f '^gpu-screen-recorder'","action":"omarchy-capture-screenrecording --stop-recording"},
"trigger.capture.screenrecord.stop": {"icon":"","label":"Stop Screenrecording","when":"gsr-cli -ipc \"${XDG_RUNTIME_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/omarchy}/omarchy-gsr.sock\" status","action":"omarchy-capture-screenrecording --stop-recording"},
"trigger.capture.screenrecord": {"icon":"","label":"Screenrecord"},
"trigger.capture.text": {"icon":"󰴑","label":"Text","action":"omarchy-capture-text"},
"trigger.capture.qr": {"icon":"󰐲","label":"QR Code","action":"omarchy-capture-qr"},
Expand Down
4 changes: 3 additions & 1 deletion shell/plugins/bar/indicators/ScreenRecording.qml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Ui

Expand All @@ -15,7 +16,8 @@ BarIndicator {

function refresh() {
if (!root.bar || statusProc.running) return
statusProc.command = ["pgrep", "--quiet", "-f", "^gpu-screen-recorder"]
var runtimeDir = Quickshell.env("XDG_RUNTIME_DIR") || (Quickshell.env("XDG_STATE_HOME") || Quickshell.env("HOME") + "/.local/state") + "/omarchy"
statusProc.command = ["gsr-cli", "-ipc", runtimeDir + "/omarchy-gsr.sock", "status"]
statusProc.running = true
}

Expand Down
135 changes: 135 additions & 0 deletions test/shell.d/screenrecording-ipc-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/bin/bash

set -euo pipefail

source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"

recording="$ROOT/shell/plugins/bar/indicators/ScreenRecording.qml"
capture="$ROOT/bin/omarchy-capture-screenrecording"
menu="$ROOT/default/omarchy/omarchy-menu.jsonc"
socket='${XDG_RUNTIME_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/omarchy}/omarchy-gsr.sock'

grep -Fq 'GSR_SOCKET="$RUNTIME_DIR/omarchy-gsr.sock"' "$capture" ||
fail "capture helper uses the shared Omarchy gsr socket"
grep -Eq '^ *gpu-screen-recorder .*-ipc "\$GSR_SOCKET"' "$capture" ||
fail "gpu-screen-recorder is launched with -ipc on the shared socket"
grep -Fq 'gsr-cli -ipc "$GSR_SOCKET" status' "$capture" ||
fail "capture helper gates on gsr-cli status for the shared socket"
grep -Fq 'gsr-cli -ipc "$GSR_SOCKET" stop' "$capture" ||
fail "capture helper stops via gsr-cli on the shared socket"
Comment thread
greptile-apps[bot] marked this conversation as resolved.
if grep -q 'RECORDING_PID_FILE' "$capture"; then
fail "stop does not keep a pid file beside gsr-cli"
fi
if grep -E 'kill[[:space:]]+(-s[[:space:]]+)?(SIGINT|-INT|-9|-KILL)' "$capture" >/dev/null; then
fail "stop does not kill the recorder; gsr-cli stop is enough"
fi

grep -Fq 'Quickshell.env("XDG_RUNTIME_DIR")' "$recording" ||
fail "indicator builds the gsr socket from XDG_RUNTIME_DIR"
grep -qx 'import Quickshell' "$recording" ||
fail "indicator imports Quickshell, which Quickshell.env needs"
grep -Fq '"gsr-cli", "-ipc"' "$recording" ||
fail "indicator status uses gsr-cli -ipc"
grep -Fq '"status"' "$recording" ||
fail "indicator status asks gsr-cli for status"
grep -Fq '/omarchy-gsr.sock' "$recording" ||
fail "indicator uses the shared Omarchy gsr socket"

grep -Fq "gsr-cli -ipc \\\"$socket\\\" status" "$menu" ||
fail "the capture menu uses gsr-cli status on the shared socket"

for file in "$capture" "$recording" "$menu"; do
if grep -E 'pidof[[:space:]"]+-q[[:space:]"]+gpu-screen-recorder|pgrep[[:space:]"-]+-f[[:space:]"]+\^?gpu-screen-recorder' "$file" >/dev/null; then
fail "screen-recording gate does not use pidof or pgrep -f for gpu-screen-recorder" "$file"
fi
done

if grep -E 'pkill[[:space:]].*-f[[:space:]"]+\^?gpu-screen-recorder' "$capture" >/dev/null; then
fail "stop does not pkill -f gpu-screen-recorder"
fi
if grep -E 'pgrep[[:space:]].*-f[[:space:]"]+\^?gpu-screen-recorder' "$capture" >/dev/null; then
fail "stop does not pgrep -f gpu-screen-recorder"
fi

pass "screen recording start/status/stop talk to the Omarchy gsr socket"

# Run the stop path. A stub that exits 0 for the notification must not hide a
# failed gsr-cli stop, and a successful stop must still print and forget the file.
stub_bin=$(mktemp -d)
runtime=$(mktemp -d)
videos=$(mktemp -d)
trap 'rm -rf "$stub_bin" "$runtime" "$videos"' EXIT

cat >"$stub_bin/gsr-cli" <<'SH'
#!/bin/bash
printf '%s\n' "$*" >>"${GSR_LOG:?}"
if [[ $1 == -ipc && $3 == status ]]; then
exit 0
fi
if [[ $1 == -ipc && $3 == stop ]]; then
exit "${GSR_STOP_STATUS:-0}"
fi
exit 1
SH
cat >"$stub_bin/omarchy-notification-send" <<'SH'
#!/bin/bash
printf '%s\n' "$*" >>"${NOTE_LOG:?}"
exit 0
SH
cat >"$stub_bin/omarchy-shell" <<'SH'
#!/bin/bash
exit 0
SH
cat >"$stub_bin/ffprobe" <<'SH'
#!/bin/bash
exit 1
SH
cat >"$stub_bin/ffmpeg" <<'SH'
#!/bin/bash
exit 1
SH
cat >"$stub_bin/pkill" <<'SH'
#!/bin/bash
printf '%s\n' "$*" >>"${PKILL_LOG:?}"
exit 0
SH
chmod +x "$stub_bin"/*

export PATH="$stub_bin:$PATH"
export GSR_LOG="$videos/gsr.log"
export NOTE_LOG="$videos/note.log"
export PKILL_LOG="$videos/pkill.log"
export XDG_RUNTIME_DIR="$runtime"
export OMARCHY_SCREENRECORD_DIR="$videos"

clip="$videos/clip.mp4"
printf 'not a video\n' >"$clip"
printf '%s\n' "$clip" >"$runtime/omarchy-screenrecord-filename"

set +e
GSR_STOP_STATUS=1 "$capture" --stop-recording >/dev/null 2>&1
Comment thread
greptile-apps[bot] marked this conversation as resolved.
failed_status=$?
set -e
[[ $failed_status -ne 0 ]] ||
fail "a failed gsr-cli stop does not exit successfully" "status $failed_status"
[[ -f $runtime/omarchy-screenrecord-filename ]] ||
fail "a failed gsr-cli stop keeps the recording filename"
grep -Fq 'Could not stop the recording through gsr-cli.' "$NOTE_LOG" ||
fail "a failed gsr-cli stop reports the error" "$(cat "$NOTE_LOG")"
pass "a failed gsr-cli stop exits non-zero and keeps the filename"

: >"$GSR_LOG"
: >"$NOTE_LOG"
printf '%s\n' "$clip" >"$runtime/omarchy-screenrecord-filename"
saved=$("$capture" --stop-recording)
[[ $saved == "$clip" ]] ||
fail "a successful stop prints the saved recording" "$saved"
[[ ! -e $runtime/omarchy-screenrecord-filename ]] ||
fail "a successful stop removes the recording filename"
grep -Fqx -- "-ipc $runtime/omarchy-gsr.sock stop" "$GSR_LOG" ||
fail "a successful stop calls gsr-cli stop on the Omarchy gsr socket" "$(cat "$GSR_LOG")"
pass "a successful gsr-cli stop prints the recording and clears its filename"

grep -Fq -- '-f WebcamOverlay' "$videos/pkill.log" ||
fail "stop cleanup does not call the stubbed pkill" "$(cat "$videos/pkill.log" 2>/dev/null || true)"
pass "stop cleanup calls the stubbed pkill instead of a live WebcamOverlay"