@desklink/host 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +44 -0
- package/README.md +266 -0
- package/bin/desklink-host.mjs +113 -0
- package/dist/bridge.d.ts +57 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +208 -0
- package/dist/bridge.js.map +1 -0
- package/dist/engineProcess.d.ts +82 -0
- package/dist/engineProcess.d.ts.map +1 -0
- package/dist/engineProcess.js +243 -0
- package/dist/engineProcess.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +169 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +18 -0
- package/dist/protocol.js.map +1 -0
- package/dist/resolveEngine.d.ts +34 -0
- package/dist/resolveEngine.d.ts.map +1 -0
- package/dist/resolveEngine.js +103 -0
- package/dist/resolveEngine.js.map +1 -0
- package/docs/BOUNDARY.md +145 -0
- package/docs/PROTOCOL.md +363 -0
- package/engine/Cargo.lock +3077 -0
- package/engine/Cargo.toml +37 -0
- package/engine/build.rs +107 -0
- package/engine/native/inputtino_shim.cpp +11 -0
- package/engine/native/vpx_shim.c +184 -0
- package/engine/native/wrapper.h +7 -0
- package/engine/src/capture.rs +510 -0
- package/engine/src/clipboard.rs +132 -0
- package/engine/src/convert.rs +299 -0
- package/engine/src/encoder.rs +204 -0
- package/engine/src/input.rs +355 -0
- package/engine/src/keymap.rs +338 -0
- package/engine/src/main.rs +614 -0
- package/engine/src/peer.rs +775 -0
- package/engine/src/portal.rs +115 -0
- package/engine/src/protocol.rs +331 -0
- package/engine/src/session.rs +2159 -0
- package/engine/src/x11.rs +244 -0
- package/engine/vendor/inputtino/.clang-format +166 -0
- package/engine/vendor/inputtino/.clang-tidy +30 -0
- package/engine/vendor/inputtino/.dockerignore +11 -0
- package/engine/vendor/inputtino/.github/workflows/mirror-repo.yml +14 -0
- package/engine/vendor/inputtino/.github/workflows/python-lint-format-test-typecheck.yml +124 -0
- package/engine/vendor/inputtino/.github/workflows/python-test-install.yaml +49 -0
- package/engine/vendor/inputtino/.github/workflows/rust.yml +40 -0
- package/engine/vendor/inputtino/CMakeLists.txt +187 -0
- package/engine/vendor/inputtino/LICENSE +21 -0
- package/engine/vendor/inputtino/README.md +110 -0
- package/engine/vendor/inputtino/bindings/python/.devcontainer/Dockerfile +23 -0
- package/engine/vendor/inputtino/bindings/python/.devcontainer/devcontainer.json +28 -0
- package/engine/vendor/inputtino/bindings/python/.python-version +1 -0
- package/engine/vendor/inputtino/bindings/python/CMakeLists.txt +23 -0
- package/engine/vendor/inputtino/bindings/python/CPPLINT.cfg +2 -0
- package/engine/vendor/inputtino/bindings/python/Makefile +49 -0
- package/engine/vendor/inputtino/bindings/python/README.md +215 -0
- package/engine/vendor/inputtino/bindings/python/pyproject.toml +178 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/__init__.py +43 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/_core.pyi +533 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/base.py +63 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/joypad.py +248 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/keyboard.py +256 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/mouse.py +106 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/pentablet.py +73 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/py.typed +0 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/touchscreen.py +61 -0
- package/engine/vendor/inputtino/bindings/python/src/inputtino/trackpad.py +74 -0
- package/engine/vendor/inputtino/bindings/python/src/main.cpp +217 -0
- package/engine/vendor/inputtino/bindings/python/tests/__init__.py +0 -0
- package/engine/vendor/inputtino/bindings/python/tests/helpers.py +3 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_base.py +60 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_inputtino.py +5 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_joypad.py +332 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_keyboard.py +164 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_mouse.py +185 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_pentablet.py +198 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_touchscreen.py +144 -0
- package/engine/vendor/inputtino/bindings/python/tests/test_trackpad.py +146 -0
- package/engine/vendor/inputtino/bindings/python/uv.lock +390 -0
- package/engine/vendor/inputtino/bindings/rust/Cargo.toml +6 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/.dockerignore +1 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/Cargo.toml +18 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/examples/keyboard.rs +33 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/examples/mouse.rs +17 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/common.rs +116 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/error.rs +17 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad.rs +74 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad_nintendo.rs +135 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad_ps5.rs +280 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad_xbox.rs +134 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/keyboard.rs +76 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/lib.rs +32 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/src/mouse.rs +178 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/tests/common.rs +38 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/tests/joypads.rs +150 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/tests/keyboard.rs +73 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino/tests/mouse.rs +139 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino-sys/.dockerignore +1 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino-sys/Cargo.toml +12 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino-sys/build.rs +48 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino-sys/src/lib.rs +5 -0
- package/engine/vendor/inputtino/bindings/rust/inputtino-sys/wrapper.h +1 -0
- package/engine/vendor/inputtino/cmake/utils.cmake +9 -0
- package/engine/vendor/inputtino/docker/Dockerfile +70 -0
- package/engine/vendor/inputtino/include/inputtino/input.h +324 -0
- package/engine/vendor/inputtino/include/inputtino/input.hpp +466 -0
- package/engine/vendor/inputtino/include/inputtino/result.hpp +37 -0
- package/engine/vendor/inputtino/share/pkgconfig/libinputtino.pc.in +17 -0
- package/engine/vendor/inputtino/src/c-bindings/helpers.hpp +19 -0
- package/engine/vendor/inputtino/src/c-bindings/joypad_ps5.cpp +115 -0
- package/engine/vendor/inputtino/src/c-bindings/joypad_switch.cpp +63 -0
- package/engine/vendor/inputtino/src/c-bindings/joypad_xone.cpp +62 -0
- package/engine/vendor/inputtino/src/c-bindings/keyboard.cpp +42 -0
- package/engine/vendor/inputtino/src/c-bindings/mouse.cpp +67 -0
- package/engine/vendor/inputtino/src/c-bindings/pen_tablet.cpp +50 -0
- package/engine/vendor/inputtino/src/c-bindings/touchscreen.cpp +43 -0
- package/engine/vendor/inputtino/src/c-bindings/trackpad.cpp +49 -0
- package/engine/vendor/inputtino/src/uhid/README.adoc +63 -0
- package/engine/vendor/inputtino/src/uhid/include/crc32.hpp +38 -0
- package/engine/vendor/inputtino/src/uhid/include/uhid/protected_types.hpp +36 -0
- package/engine/vendor/inputtino/src/uhid/include/uhid/ps5.hpp +550 -0
- package/engine/vendor/inputtino/src/uhid/include/uhid/uhid.hpp +168 -0
- package/engine/vendor/inputtino/src/uhid/joypad_ps5.cpp +580 -0
- package/engine/vendor/inputtino/src/uinput/include/inputtino/keyboard.hpp +84 -0
- package/engine/vendor/inputtino/src/uinput/include/inputtino/protected_ps5_types.hpp +7 -0
- package/engine/vendor/inputtino/src/uinput/include/inputtino/protected_types.hpp +110 -0
- package/engine/vendor/inputtino/src/uinput/joypad_nintendo.cpp +190 -0
- package/engine/vendor/inputtino/src/uinput/joypad_ps.cpp +203 -0
- package/engine/vendor/inputtino/src/uinput/joypad_utils.hpp +246 -0
- package/engine/vendor/inputtino/src/uinput/joypad_xbox.cpp +190 -0
- package/engine/vendor/inputtino/src/uinput/keyboard.cpp +120 -0
- package/engine/vendor/inputtino/src/uinput/mouse.cpp +199 -0
- package/engine/vendor/inputtino/src/uinput/pentablet.cpp +162 -0
- package/engine/vendor/inputtino/src/uinput/touchscreen.cpp +140 -0
- package/engine/vendor/inputtino/src/uinput/trackpad.cpp +192 -0
- package/examples/reference-client.html +428 -0
- package/package.json +38 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API of the desktop host engine package.
|
|
3
|
+
*
|
|
4
|
+
* The engine itself is a native per-user process; this package is how a
|
|
5
|
+
* consumer starts it and speaks its local control protocol. Nothing in this
|
|
6
|
+
* surface refers to any particular application.
|
|
7
|
+
*/
|
|
8
|
+
export * from './protocol.js';
|
|
9
|
+
export { EngineClient } from './engineProcess.js';
|
|
10
|
+
export { resolveEngine, explainMissingEngine, enginePackageRoot } from './resolveEngine.js';
|
|
11
|
+
export { Bridge, BRIDGE_PATH } from './bridge.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,iBAAiB,EAAuB,MAAM,oBAAoB,CAAC;AACjH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAsB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine's local control protocol, as a consumer sees it.
|
|
3
|
+
*
|
|
4
|
+
* `docs/PROTOCOL.md` is the prose authority; these types are the same contract
|
|
5
|
+
* expressed for a caller. They deliberately contain nothing about any particular
|
|
6
|
+
* application: a machine id, a chat or an account has no place here.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PROTOCOL_VERSION = 2;
|
|
9
|
+
export interface EngineError {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export interface EngineCapabilities {
|
|
14
|
+
protocol: number;
|
|
15
|
+
engine: string;
|
|
16
|
+
platform: string;
|
|
17
|
+
session: {
|
|
18
|
+
kind: string;
|
|
19
|
+
};
|
|
20
|
+
/** Whether the X11 backend can reach a server here, and that server's screen size. */
|
|
21
|
+
x11: {
|
|
22
|
+
available: boolean;
|
|
23
|
+
size: [number, number] | null;
|
|
24
|
+
};
|
|
25
|
+
capture: {
|
|
26
|
+
mechanism: string;
|
|
27
|
+
/** Backends this build has, not what this machine can necessarily use. */
|
|
28
|
+
backends: string[];
|
|
29
|
+
formats: string[];
|
|
30
|
+
cursor: string;
|
|
31
|
+
audio: boolean;
|
|
32
|
+
};
|
|
33
|
+
encode: {
|
|
34
|
+
codecs: string[];
|
|
35
|
+
hardware: boolean;
|
|
36
|
+
};
|
|
37
|
+
input: {
|
|
38
|
+
mechanism: string;
|
|
39
|
+
pointer: boolean;
|
|
40
|
+
wheel: boolean;
|
|
41
|
+
keyboard: boolean;
|
|
42
|
+
text: string[];
|
|
43
|
+
layout: string;
|
|
44
|
+
unavailable_reason: {
|
|
45
|
+
reason: string;
|
|
46
|
+
remedy: string;
|
|
47
|
+
} | null;
|
|
48
|
+
grant: string;
|
|
49
|
+
};
|
|
50
|
+
clipboard: {
|
|
51
|
+
read: boolean;
|
|
52
|
+
write: boolean;
|
|
53
|
+
mime: string[];
|
|
54
|
+
maxBytes: number;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface SurfaceGeometry {
|
|
58
|
+
source: {
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
};
|
|
62
|
+
encoded: {
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
};
|
|
66
|
+
origin: {
|
|
67
|
+
x: number;
|
|
68
|
+
y: number;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Which desktop to capture. Absent means the portal, which is the only backend
|
|
73
|
+
* that carries a Wayland user's consent; an explicit X display is the supported
|
|
74
|
+
* alternative for a machine whose screen-cast portal does not work, and it is
|
|
75
|
+
* what a host with no Wayland session uses.
|
|
76
|
+
*/
|
|
77
|
+
export type SourceRequest = {
|
|
78
|
+
kind: 'portal';
|
|
79
|
+
} | {
|
|
80
|
+
kind: 'x11';
|
|
81
|
+
display?: string;
|
|
82
|
+
};
|
|
83
|
+
export interface OpenSessionRequest {
|
|
84
|
+
source?: SourceRequest;
|
|
85
|
+
/** `view` alone is capture-only; `control` needs a working input backend. */
|
|
86
|
+
permissions: Array<'view' | 'control' | 'clipboard'>;
|
|
87
|
+
maxWidth?: number;
|
|
88
|
+
maxHeight?: number;
|
|
89
|
+
bitrateKbps?: number;
|
|
90
|
+
maxFps?: number;
|
|
91
|
+
iceServers?: Array<{
|
|
92
|
+
urls: string[];
|
|
93
|
+
username?: string;
|
|
94
|
+
credential?: string;
|
|
95
|
+
}>;
|
|
96
|
+
restoreToken?: string;
|
|
97
|
+
ttlSeconds?: number;
|
|
98
|
+
/** Also listen for ICE over TCP on the loopback, for a client behind a local forward. */
|
|
99
|
+
loopbackTcp?: boolean;
|
|
100
|
+
}
|
|
101
|
+
export interface OpenedSession {
|
|
102
|
+
sessionId: string;
|
|
103
|
+
generation: number;
|
|
104
|
+
source: {
|
|
105
|
+
kind: string;
|
|
106
|
+
width: number;
|
|
107
|
+
height: number;
|
|
108
|
+
origin: {
|
|
109
|
+
x: number;
|
|
110
|
+
y: number;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
geometry: SurfaceGeometry;
|
|
114
|
+
}
|
|
115
|
+
export type EngineEvent = {
|
|
116
|
+
event: 'session.description';
|
|
117
|
+
params: {
|
|
118
|
+
sessionId: string;
|
|
119
|
+
generation: number;
|
|
120
|
+
description: {
|
|
121
|
+
type: 'offer';
|
|
122
|
+
sdp: string;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
} | {
|
|
126
|
+
event: 'session.candidate';
|
|
127
|
+
params: {
|
|
128
|
+
sessionId: string;
|
|
129
|
+
generation: number;
|
|
130
|
+
candidate: string;
|
|
131
|
+
sdpMid: string | null;
|
|
132
|
+
sdpMLineIndex: number | null;
|
|
133
|
+
};
|
|
134
|
+
} | {
|
|
135
|
+
event: 'session.state';
|
|
136
|
+
params: {
|
|
137
|
+
sessionId: string;
|
|
138
|
+
capture: string;
|
|
139
|
+
transport: string;
|
|
140
|
+
firstFrame: boolean;
|
|
141
|
+
};
|
|
142
|
+
} | {
|
|
143
|
+
event: 'session.restoreToken';
|
|
144
|
+
params: {
|
|
145
|
+
sessionId: string;
|
|
146
|
+
token: string;
|
|
147
|
+
};
|
|
148
|
+
} | {
|
|
149
|
+
event: 'session.revoked';
|
|
150
|
+
params: {
|
|
151
|
+
sessionId: string;
|
|
152
|
+
reason: string;
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
export type EngineEventName = EngineEvent['event'];
|
|
156
|
+
export interface SessionMetrics {
|
|
157
|
+
captured_frames: number;
|
|
158
|
+
dropped_frames: number;
|
|
159
|
+
encoded_frames: number;
|
|
160
|
+
encoded_bytes: number;
|
|
161
|
+
input_applied: number;
|
|
162
|
+
input_rejected: number;
|
|
163
|
+
}
|
|
164
|
+
/** A protocol-level failure carrying the engine's stable error code. */
|
|
165
|
+
export declare class EngineRefused extends Error {
|
|
166
|
+
readonly code: string;
|
|
167
|
+
constructor(code: string, message: string);
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine's local control protocol, as a consumer sees it.
|
|
3
|
+
*
|
|
4
|
+
* `docs/PROTOCOL.md` is the prose authority; these types are the same contract
|
|
5
|
+
* expressed for a caller. They deliberately contain nothing about any particular
|
|
6
|
+
* application: a machine id, a chat or an account has no place here.
|
|
7
|
+
*/
|
|
8
|
+
export const PROTOCOL_VERSION = 2;
|
|
9
|
+
/** A protocol-level failure carrying the engine's stable error code. */
|
|
10
|
+
export class EngineRefused extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'EngineRefused';
|
|
15
|
+
this.code = code;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AA0GlC,wEAAwE;AACxE,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC3B,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,OAAe;QACrC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC;CACJ"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding the engine executable.
|
|
3
|
+
*
|
|
4
|
+
* Three sources, in order, and no fourth:
|
|
5
|
+
*
|
|
6
|
+
* 1. an explicit path, when a consumer or an operator names one;
|
|
7
|
+
* 2. this package's platform package, which npm installs as an optional
|
|
8
|
+
* dependency on a platform that has a prebuilt engine (Linux x64 glibc);
|
|
9
|
+
* 3. this package's own build output, for a source build.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately not searched: `PATH`. "A program called desklink-host" is not
|
|
12
|
+
* evidence of which program is about to be given control of a desktop.
|
|
13
|
+
*/
|
|
14
|
+
export interface ResolvedEngine {
|
|
15
|
+
command: string;
|
|
16
|
+
args: string[];
|
|
17
|
+
origin: 'configured' | 'prebuilt' | 'package';
|
|
18
|
+
}
|
|
19
|
+
export declare function enginePackageRoot(): string;
|
|
20
|
+
/**
|
|
21
|
+
* The platform package for this machine.
|
|
22
|
+
*
|
|
23
|
+
* `libc` is part of the tag because a glibc binary on a musl system fails in
|
|
24
|
+
* ways that look like a broken install rather than an unsupported one, and
|
|
25
|
+
* because npm's own `libc` selector is not honoured by every client.
|
|
26
|
+
*/
|
|
27
|
+
export declare function platformTag(platform?: string, arch?: string, glibc?: boolean | undefined): string;
|
|
28
|
+
export declare function resolveEngine(configured?: string | undefined): ResolvedEngine | null;
|
|
29
|
+
/**
|
|
30
|
+
* Why the engine is missing, in words fit to show a user. `null` means it is
|
|
31
|
+
* there.
|
|
32
|
+
*/
|
|
33
|
+
export declare function explainMissingEngine(configured?: string | undefined): string | null;
|
|
34
|
+
//# sourceMappingURL=resolveEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveEngine.d.ts","sourceRoot":"","sources":["../src/resolveEngine.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;CACjD;AAKD,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACvB,QAAQ,GAAE,MAAyB,EACnC,IAAI,GAAE,MAAqB,EAC3B,KAAK,GAAE,OAAO,GAAG,SAAsB,GACxC,MAAM,CAGR;AAiCD,wBAAgB,aAAa,CAAC,UAAU,qBAAmC,GAAG,cAAc,GAAG,IAAI,CAelG;AAWD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,qBAAmC,GAAG,MAAM,GAAG,IAAI,CAiBjG"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
/** Platforms with a published platform package. */
|
|
6
|
+
const PREBUILT_PLATFORMS = ['linux-x64-gnu'];
|
|
7
|
+
export function enginePackageRoot() {
|
|
8
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The platform package for this machine.
|
|
12
|
+
*
|
|
13
|
+
* `libc` is part of the tag because a glibc binary on a musl system fails in
|
|
14
|
+
* ways that look like a broken install rather than an unsupported one, and
|
|
15
|
+
* because npm's own `libc` selector is not honoured by every client.
|
|
16
|
+
*/
|
|
17
|
+
export function platformTag(platform = process.platform, arch = process.arch, glibc = hasGlibc()) {
|
|
18
|
+
if (platform === 'linux')
|
|
19
|
+
return `linux-${arch}-${glibc === false ? 'musl' : 'gnu'}`;
|
|
20
|
+
return `${platform}-${arch}`;
|
|
21
|
+
}
|
|
22
|
+
/** A Linux Node without a reported glibc runtime is taken to be musl. */
|
|
23
|
+
function hasGlibc() {
|
|
24
|
+
if (process.platform !== 'linux')
|
|
25
|
+
return undefined;
|
|
26
|
+
const report = process.report?.getReport?.();
|
|
27
|
+
return report?.header?.glibcVersionRuntime !== undefined;
|
|
28
|
+
}
|
|
29
|
+
function prebuiltBinary() {
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const name = `@desklink/host-${platformTag()}`;
|
|
32
|
+
try {
|
|
33
|
+
// The platform package's manifest points at the executable, so the
|
|
34
|
+
// binary's name and location stay that package's business.
|
|
35
|
+
const manifest = require(`${name}/package.json`);
|
|
36
|
+
const relative = manifest.executable ?? manifest.bin?.['desklink-host'];
|
|
37
|
+
if (typeof relative !== 'string')
|
|
38
|
+
return null;
|
|
39
|
+
const path = require.resolve(`${name}/${relative.replace(/^\.\//, '')}`);
|
|
40
|
+
return path;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function buildCandidates(root) {
|
|
47
|
+
return [
|
|
48
|
+
join(root, 'engine', 'target', 'release', 'desklink-host'),
|
|
49
|
+
join(root, 'engine', 'target', 'debug', 'desklink-host'),
|
|
50
|
+
join(root, 'bin', 'desklink-host'),
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
export function resolveEngine(configured = process.env.MUXR_DESKLINK_ENGINE) {
|
|
54
|
+
if (configured !== undefined && configured.trim() !== '') {
|
|
55
|
+
if (!isExecutable(configured))
|
|
56
|
+
return null;
|
|
57
|
+
return { command: configured, args: ['serve'], origin: 'configured' };
|
|
58
|
+
}
|
|
59
|
+
const prebuilt = prebuiltBinary();
|
|
60
|
+
if (prebuilt !== null && isExecutable(prebuilt)) {
|
|
61
|
+
return { command: prebuilt, args: ['serve'], origin: 'prebuilt' };
|
|
62
|
+
}
|
|
63
|
+
for (const candidate of buildCandidates(enginePackageRoot())) {
|
|
64
|
+
if (isExecutable(candidate)) {
|
|
65
|
+
return { command: candidate, args: ['serve'], origin: 'package' };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function isExecutable(path) {
|
|
71
|
+
if (!existsSync(path))
|
|
72
|
+
return false;
|
|
73
|
+
const stat = statSync(path);
|
|
74
|
+
if (!stat.isFile())
|
|
75
|
+
return false;
|
|
76
|
+
// A file that is not executable is an install that went wrong, and saying so
|
|
77
|
+
// is more useful than an EACCES three calls later.
|
|
78
|
+
return (stat.mode & 0o111) !== 0;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Why the engine is missing, in words fit to show a user. `null` means it is
|
|
82
|
+
* there.
|
|
83
|
+
*/
|
|
84
|
+
export function explainMissingEngine(configured = process.env.MUXR_DESKLINK_ENGINE) {
|
|
85
|
+
const resolved = resolveEngine(configured);
|
|
86
|
+
if (resolved !== null)
|
|
87
|
+
return null;
|
|
88
|
+
if (configured !== undefined && configured.trim() !== '') {
|
|
89
|
+
if (!existsSync(configured)) {
|
|
90
|
+
return `The desktop engine is not at the configured path (${configured}).`;
|
|
91
|
+
}
|
|
92
|
+
return `The desktop engine at ${configured} is not an executable file.`;
|
|
93
|
+
}
|
|
94
|
+
if (process.platform !== 'linux') {
|
|
95
|
+
return `The desktop engine runs on Linux only; ${process.platform} is not supported yet.`;
|
|
96
|
+
}
|
|
97
|
+
const tag = platformTag();
|
|
98
|
+
if (PREBUILT_PLATFORMS.includes(tag)) {
|
|
99
|
+
return `The prebuilt desktop engine for ${tag} is missing. It arrives as the optional dependency @desklink/host-${tag}: reinstall without omitting optional dependencies, or point MUXR_DESKLINK_ENGINE at an engine built from source.`;
|
|
100
|
+
}
|
|
101
|
+
return `There is no prebuilt desktop engine for ${tag}. Build it from source (see the @desklink/host README) and point MUXR_DESKLINK_ENGINE at the binary.`;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=resolveEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveEngine.js","sourceRoot":"","sources":["../src/resolveEngine.ts"],"names":[],"mappings":"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"}
|
package/docs/BOUNDARY.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Boundary sketch — two packages, one consumer
|
|
2
|
+
|
|
3
|
+
Written before implementation as the contract the code has to satisfy. Status
|
|
4
|
+
markers are honest: `[decided]`, `[open]`.
|
|
5
|
+
|
|
6
|
+
## Why two packages and not one
|
|
7
|
+
|
|
8
|
+
The host side is a native, per-user, permission-bearing process: capture,
|
|
9
|
+
encode, WebRTC, kernel input. The client side is mobile-native: hardware decode,
|
|
10
|
+
a renderer, an IME bridge, the platform clipboard. They share exactly one thing —
|
|
11
|
+
a versioned wire contract — and nothing else. Putting them in one package would
|
|
12
|
+
mean the phone pins a native host build, or the host pins a mobile runtime.
|
|
13
|
+
|
|
14
|
+
## Package 1 — host engine `[decided]`
|
|
15
|
+
|
|
16
|
+
**Boundary:** "given a granted desktop source, produce a WebRTC video track and
|
|
17
|
+
apply authenticated input to the same desktop".
|
|
18
|
+
|
|
19
|
+
- In: `session.open`, SDP/ICE, scoped WebRTC control-channel input, explicit
|
|
20
|
+
clipboard read/write, close. See `docs/PROTOCOL.md`.
|
|
21
|
+
- Out: one VP9 video track and session notifications. Metrics are a request, not
|
|
22
|
+
an event; the wire contract is in `docs/PROTOCOL.md`.
|
|
23
|
+
- Process shape: per-user, on-demand, spawned by a consumer that already owns
|
|
24
|
+
the session, spoken to over an inherited private channel. No listener, no
|
|
25
|
+
daemon, no autostart, no root.
|
|
26
|
+
- Never: decides identity, installs rules, grants capabilities, carries audio,
|
|
27
|
+
arbitrates two controllers, remembers a session across a restart.
|
|
28
|
+
|
|
29
|
+
The public surface carries no consumer concepts. A machine id, a chat, a pane or
|
|
30
|
+
an account never appears in the protocol or in the engine's state.
|
|
31
|
+
|
|
32
|
+
## Package 2 — React Native client `[decided]`
|
|
33
|
+
|
|
34
|
+
**Boundary:** "given an authorized session description, show the desktop and turn
|
|
35
|
+
local gestures into that session's input".
|
|
36
|
+
|
|
37
|
+
- `useDesktopSession({ authorize, onStateChange, onError })` owns connection,
|
|
38
|
+
negotiation, readiness, reconnect and typed errors. `authorize()` returns the
|
|
39
|
+
session's `signaling` channel and the permissions it may ask for.
|
|
40
|
+
- `<DesktopView sessionId style />` owns the live surface and gesture mapping. It
|
|
41
|
+
owns nothing about layout, chrome, navigation or the conversation it sits in.
|
|
42
|
+
- `session.showKeyboard()/hideKeyboard()`,
|
|
43
|
+
`session.copyRemoteToLocal()/pasteLocalToRemote()` are the control primitives
|
|
44
|
+
the app mounts wherever it wants.
|
|
45
|
+
- Native side owns the video decoder factory selection (hardware VP9 rather
|
|
46
|
+
than the underlying wrapper's software default), the renderer and the IME/text
|
|
47
|
+
connection. For input enablement and release on background/unmount, see the
|
|
48
|
+
[client contract](../../desktop-client/README.md#what-the-package-guarantees).
|
|
49
|
+
- The app supplies `authorize()` (a short-lived, engine-scoped capability plus
|
|
50
|
+
the authenticated `signaling` channel it carries). The package never sees a
|
|
51
|
+
pairing blob, a machine id or a token format, and never persists one.
|
|
52
|
+
- Platform files: `.native.ts` uses WebRTC through the app's existing native
|
|
53
|
+
binding; `.web.ts` uses the browser's own `RTCPeerConnection`, so the same
|
|
54
|
+
screen works in the PWA. `[decided]` the browser shim shares the
|
|
55
|
+
readiness/state layer (`useDesktopSession`) and carries its own gesture layer
|
|
56
|
+
in `src/native.web.ts`, exactly as the native view does; observed on an owned
|
|
57
|
+
display, not on the phone journey.
|
|
58
|
+
|
|
59
|
+
## What the consumer (this application) keeps
|
|
60
|
+
|
|
61
|
+
- The computer action in the conversation and the return.
|
|
62
|
+
- Its own pairing, machine identity, authorization decision and revocation.
|
|
63
|
+
- Carrying SDP/ICE over its existing authenticated encrypted channel.
|
|
64
|
+
- Mapping an authorized request onto `session.open`'s permissions, and the
|
|
65
|
+
product's own start/loading/error copy.
|
|
66
|
+
- Where the keyboard and clipboard controls sit, and the fact that internal ids
|
|
67
|
+
are never displayed.
|
|
68
|
+
|
|
69
|
+
## Reused, not rebuilt `[decided]`
|
|
70
|
+
|
|
71
|
+
| Need | Already in this repository |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Encrypted mobile → host request path, identity, grants | `packages/contract` control plane + `apps/host/src/requests` |
|
|
74
|
+
| Relay stream channel (host joins as machine, client as client) | `packages/contract` `wsTickets` / preview channel pattern |
|
|
75
|
+
| A reference for a "live surface inside a conversation" | `apps/mobile/sources/desktop` |
|
|
76
|
+
| Flow-test style for the packages | `apps/mobile/sources/catalog/application/sessionSync.integration.spec.ts` |
|
|
77
|
+
|
|
78
|
+
## Reused, not rebuilt, outside the repository `[decided]`
|
|
79
|
+
|
|
80
|
+
| Need | Library | Licence |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| WebRTC peer, ICE, DTLS, SRTP, RTP | `webrtc` (webrtc-rs) | MIT / Apache-2.0 |
|
|
83
|
+
| Portal ScreenCast + PipeWire fd | `ashpd` (zbus) | MIT |
|
|
84
|
+
| PipeWire stream client | `pipewire` | MIT |
|
|
85
|
+
| VP9 encode | libvpx | BSD-3-Clause |
|
|
86
|
+
| Virtual mouse/keyboard | inputtino (uinput/libevdev) | MIT |
|
|
87
|
+
| Clipboard read/write | `wl-clipboard-rs` | MIT |
|
|
88
|
+
| Keyboard layout mapping | `xkbcommon` | MIT |
|
|
89
|
+
|
|
90
|
+
Not adopted: libwebrtc (a multi-gigabyte source build with an LGPL/GIO portal
|
|
91
|
+
seam), GStreamer (LGPL), and every GPL/AGPL remote-desktop application.
|
|
92
|
+
|
|
93
|
+
## Settled by reading the compositor's own source `[decided]`
|
|
94
|
+
|
|
95
|
+
**1. Capture is CPU-readable, with no GPU download step.** A compositor's screen
|
|
96
|
+
cast node chooses its buffer type from the client's format pod: xdg-desktop-
|
|
97
|
+
portal-hyprland hard-codes `SPA_DATA_MemFd` unless the client's `EnumFormat`
|
|
98
|
+
carries a `SPA_FORMAT_VIDEO_modifier` property, in which case it switches to
|
|
99
|
+
DMA-BUF (and logs `pw requested dmabuf`). This engine deliberately sends no
|
|
100
|
+
modifier and asks for MemFd, so frames arrive as mapped shared memory. It also
|
|
101
|
+
means the engine has no EGL, GBM or vendor-driver dependency at all — the whole
|
|
102
|
+
capture path is CPU, which is what makes it portable across machine classes. If
|
|
103
|
+
some other compositor ignores the request and produces DMA-BUFs anyway, the
|
|
104
|
+
engine drops those frames rather than reading garbage: a non-mappable buffer only
|
|
105
|
+
increments the dropped counter, and a source that never yields a readable buffer
|
|
106
|
+
gives up with the capture-start timeout, because capture never reports readiness.
|
|
107
|
+
The negotiated buffer type is visible through `desktop-host capture-probe`, which
|
|
108
|
+
is the only place it is reported.
|
|
109
|
+
|
|
110
|
+
**2. The Android side reuses the app's WebRTC binding.** The client package
|
|
111
|
+
compiles against the `org.webrtc` classes the installed `react-native-webrtc`
|
|
112
|
+
already ships, so one native library serves the whole app, and it builds its own
|
|
113
|
+
`PeerConnectionFactory` with `DefaultVideoDecoderFactory` (hardware-first, VP9
|
|
114
|
+
included). It never touches `WebRTCModule.options`, so the app's shared factory
|
|
115
|
+
and its voice path are unchanged. A second AAR copy was the alternative and was
|
|
116
|
+
rejected: two copies of the JNI library in one app is a worse failure mode than
|
|
117
|
+
one extra factory instance.
|
|
118
|
+
|
|
119
|
+
## The seam a consumer connects through `[decided]`
|
|
120
|
+
|
|
121
|
+
The engine needs exactly one thing from a consumer: somewhere for SDP and ICE to
|
|
122
|
+
travel. That is `Signaling` on the client side and `EngineClient.drainEvents` /
|
|
123
|
+
`acceptAnswer` / `addCandidate` on the host side, and it is deliberately small,
|
|
124
|
+
because every application already has an authenticated way to talk to itself.
|
|
125
|
+
|
|
126
|
+
muxr supplies its existing paired, encrypted request path and nothing else: no
|
|
127
|
+
address, port, code or certificate reaches the user, and the engine's session is
|
|
128
|
+
started by the host that already serves the terminal. A consumer with no channel
|
|
129
|
+
of its own is not left to invent one — `desklink-host bridge` re-serves the same
|
|
130
|
+
protocol over a WebSocket and serves a reference client that needs no build step,
|
|
131
|
+
so a stranger's first run is one command and one URL.
|
|
132
|
+
|
|
133
|
+
The same substitution is therefore available to both: the packages carry no muxr
|
|
134
|
+
concept, and muxr's contribution is one file that adapts its own channel to the
|
|
135
|
+
`Signaling` interface.
|
|
136
|
+
|
|
137
|
+
## Still open `[open]`
|
|
138
|
+
|
|
139
|
+
- Whether a persisted portal restore token makes the second visit to the desktop
|
|
140
|
+
silent, or whether the compositor asks again. The engine returns the token the
|
|
141
|
+
portal gives it (`session.restoreToken`) and accepts one on `session.open`; the
|
|
142
|
+
behaviour is the compositor's to decide and is not claimed until observed.
|
|
143
|
+
- Whether the consumer should set a smaller `maxWidth`/`maxHeight` box per
|
|
144
|
+
device rather than use the engine's default (see `docs/PROTOCOL.md`). The
|
|
145
|
+
engine never upscales.
|