Skip to content
Source
Sign inSign up

NiriCaptureShortcut.ts

65 lines2.0 KB

1// @effect-diagnostics globalTimers:off -- Bound session-bus registration at a native callback boundary.
2import { Message, MessageType, NameFlag, RequestNameReply, sessionBus } from "dbus-next";
3​
4import {
5 NIRI_CAPTURE_INTERFACE as INTERFACE,
6 NIRI_CAPTURE_PATH as PATH,
7} from "./linuxCaptureSession.ts";
8​
9export async function startNiriCaptureShortcut(
10 appId: string,
11 onCapture: () => void,
12 onFailure: () => void,
13 bus = sessionBus(),
14): Promise<() => void> {
15 let closed = false;
16 const close = () => {
17 if (closed) return;
18 closed = true;
19 bus.disconnect();
20 };
21 const failure = new Promise<never>((_, reject) => {
22 bus.on("error", (error: Error) => {
23 reject(error);
24 if (!closed) onFailure();
25 close();
26 });
27 });
28 void failure.catch(() => undefined);
29 bus.addMethodHandler((message: Message) => {
30 if (message.path !== PATH || message.interface !== INTERFACE || message.member !== "Capture")
31 return false;
32 if (message.signature || message.body.length) {
33 // Preserve dbus-next's numeric reply serial; its newError factory has incorrect types.
34 const reply = Message.newMethodReturn(message, "s", ["Capture takes no arguments."]);
35 reply.type = MessageType.ERROR;
36 reply.errorName = "org.freedesktop.DBus.Error.InvalidArgs";
37 bus.send(reply);
38 return true;
39 }
40 if (!closed) onCapture();
41 bus.send(Message.newMethodReturn(message));
42 return true;
43 });
44 let timer: ReturnType<typeof setTimeout> | undefined;
45 try {
46 const result = await Promise.race([
47 bus.requestName(`${appId}.SnapShot`, NameFlag.DO_NOT_QUEUE),
48 failure,
49 new Promise<never>((_, reject) => {
50 timer = setTimeout(
51 () => reject(new Error("Niri capture shortcut registration timed out.")),
52 5_000,
53 );
54 }),
55 ]);
56 if (result !== RequestNameReply.PRIMARY_OWNER)
57 throw new Error("Another T3 Code instance already owns the capture shortcut.");
58 return close;
59 } catch (error) {
60 close();
61 throw error;
62 } finally {
63 clearTimeout(timer);
64 }