Skip to content

Commit 69a79fc

Browse files
authored
Merge pull request #2 from NordicsSys/fix/wayland-interactive-region-gsr-signals
fix: Wayland interactive video region + reliable GPU Screen Recorder signals
2 parents c03b365 + 5c91111 commit 69a79fc

3 files changed

Lines changed: 74 additions & 28 deletions

File tree

.github/workflows/auto-assign.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@ on:
33
issues:
44
types: [opened]
55
pull_request:
6-
types: [opened]
6+
types: [opened, reopened, synchronize]
77

88
jobs:
99
assign:
10+
# Fork PR runs get a read-only GITHUB_TOKEN; addAssignees fails with HttpError /
11+
# "Resource not accessible by integration". Assign only when the PR branch lives
12+
# in this repo; external forks skip this job cleanly (checks stay green).
13+
if: >-
14+
github.event_name == 'issues' ||
15+
github.event.pull_request.head.repo.full_name == github.repository
1016
runs-on: ubuntu-latest
1117
permissions:
18+
contents: read
1219
issues: write
1320
pull-requests: write
21+
1422
steps:
1523
- name: Assign to me
16-
uses: actions/github-script@v7
24+
# v9 runs on a current runner Node; avoids deprecated Node 20 runtime for v7.
25+
uses: actions/github-script@v9
1726
with:
1827
script: |
1928
github.rest.issues.addAssignees({

CaptureToolbar.qml

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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

plugin.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
],
2222
"dependencies": [
2323
"dms",
24-
"gpu screen recorder"
24+
"gpu screen recorder",
25+
"slurp (for interactive region recording on Wayland)"
2526
],
2627
"screenshot": "https://raw.githubusercontent.com/JDKamalakar/DMS-ScreenCapture_Toolbar/refs/heads/main/assets/Screenshot_UI.png",
2728
"permissions": [

0 commit comments

Comments
 (0)