-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbridge.mjs
More file actions
123 lines (106 loc) · 3.31 KB
/
Copy pathbridge.mjs
File metadata and controls
123 lines (106 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { spawn } from "node:child_process";
import { execFileSync } from "node:child_process";
import process from "node:process";
import { fileURLToPath } from "node:url";
import { radialToAxes, shouldResetStaleInput } from "./joystick.mjs";
import { WorkLouderRPC } from "./worklouder-rpc.mjs";
const debug = process.argv.includes("--debug");
const staleInputTimeoutMs = 350;
try {
execFileSync("pgrep", ["-x", "input"], { stdio: "ignore" });
throw new Error(
"Work Louder Input is running. Quit Input before starting the scroll bridge " +
"or it will restore the RADIAL mapping.",
);
} catch (error) {
if (error?.status === undefined) throw error;
}
const rpc = new WorkLouderRPC();
let emitter;
let shuttingDown = false;
let lastInputTime = Date.now();
let axesActive = false;
function writeAxes(x, y) {
if (emitter?.stdin.writable) emitter.stdin.write(`${x} ${y}\n`);
}
function resetAxes(reason) {
axesActive = false;
writeAxes(0, 0);
if (debug && reason) console.log(`axis x=0.000 y=0.000 ${reason}`);
}
rpc.on("disconnect", (error) => {
resetAxes("device disconnected");
console.error(`Creator Micro 2 disconnected: ${error.message}`);
if (!shuttingDown) emitter?.kill("SIGTERM");
});
await rpc.open();
const extraArguments = process.argv.slice(2).filter((argument) => argument !== "--debug");
emitter = spawn(
fileURLToPath(new URL(".build/release/codex-micro-scroll", import.meta.url)),
["--stream", ...extraArguments],
{ stdio: ["pipe", "inherit", "inherit"] },
);
emitter.on("error", async (error) => {
console.error(`Could not start the scroll emitter: ${error.message}`);
await rpc.close().catch(() => {});
process.exit(1);
});
emitter.on("exit", async (code, signal) => {
await rpc.close().catch(() => {});
if (signal && !shuttingDown) {
console.error(`Scroll emitter stopped by ${signal}`);
} else if (code !== null && code !== 0) {
console.error(`Scroll emitter exited with code ${code}`);
}
process.exit(shuttingDown ? 0 : code ?? 1);
});
let lastX = 0;
let lastY = 0;
let lastDebugTime = 0;
rpc.onNotification("kb.radial", (params) => {
const operation = params?.o;
lastInputTime = Date.now();
if (operation === 0) {
resetAxes("released");
return;
}
const angle = params?.a;
const distance = params?.d;
if (!Number.isFinite(angle) || !Number.isFinite(distance)) return;
({ x: lastX, y: lastY } = radialToAxes(angle, distance));
axesActive = lastX !== 0 || lastY !== 0;
writeAxes(lastX, lastY);
const now = Date.now();
if (debug && now - lastDebugTime >= 100) {
console.log(
`axis x=${lastX.toFixed(3)} y=${lastY.toFixed(3)} ` +
`angle=${angle.toFixed(3)} distance=${distance.toFixed(3)} op=${operation}`,
);
lastDebugTime = now;
}
});
const watchdog = setInterval(() => {
if (
shouldResetStaleInput({
active: axesActive,
lastInputTime,
now: Date.now(),
timeoutMs: staleInputTimeoutMs,
})
) {
resetAxes("input timeout");
}
}, 50);
for (const signal of ["SIGINT", "SIGTERM"]) {
process.on(signal, async () => {
if (shuttingDown) return;
shuttingDown = true;
clearInterval(watchdog);
resetAxes();
await rpc.close().catch(() => {});
emitter.kill(signal);
});
}
console.log(
"Creator Micro 2 radial stream connected over USB/Bluetooth. Press Control-C to stop.",
);