@desklink/host 0.1.0 → 0.2.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/NOTICE +3 -0
- package/README.md +72 -16
- package/bin/desklink-host.mjs +14 -3
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +5 -1
- package/dist/bridge.js.map +1 -1
- package/dist/engineProcess.d.ts.map +1 -1
- package/dist/engineProcess.js +17 -5
- package/dist/engineProcess.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/keeper.d.ts +52 -0
- package/dist/keeper.d.ts.map +1 -0
- package/dist/keeper.js +101 -0
- package/dist/keeper.js.map +1 -0
- package/dist/protocol.d.ts +42 -5
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/resolveEngine.d.ts +3 -2
- package/dist/resolveEngine.d.ts.map +1 -1
- package/dist/resolveEngine.js +20 -5
- package/dist/resolveEngine.js.map +1 -1
- package/docs/BOUNDARY.md +11 -6
- package/docs/PROTOCOL.md +297 -33
- package/engine/Cargo.lock +196 -1
- package/engine/Cargo.toml +26 -5
- package/engine/build.rs +98 -23
- package/engine/native/agent_overlay_mac.m +133 -0
- package/engine/native/agent_overlay_wayland.c +192 -0
- package/engine/native/agent_overlay_x11.c +103 -0
- package/engine/native/mac_stream.mm +188 -0
- package/engine/src/capture.rs +169 -19
- package/engine/src/capture_mac.rs +145 -0
- package/engine/src/clipboard_mac.rs +182 -0
- package/engine/src/convert.rs +57 -1
- package/engine/src/indicator.rs +162 -0
- package/engine/src/input.rs +12 -0
- package/engine/src/input_mac.rs +312 -0
- package/engine/src/keeper.rs +622 -0
- package/engine/src/keymap.rs +25 -0
- package/engine/src/keymap_mac.rs +209 -0
- package/engine/src/mac.rs +722 -0
- package/engine/src/main.rs +158 -2
- package/engine/src/peer.rs +429 -22
- package/engine/src/portal.rs +4 -0
- package/engine/src/portal_mac.rs +68 -0
- package/engine/src/protocol.rs +186 -3
- package/engine/src/session.rs +1307 -100
- package/engine/src/x11.rs +23 -3
- package/engine/src/x11_mac.rs +40 -0
- package/examples/reference-client.html +33 -4
- package/package.json +2 -2
package/engine/src/x11.rs
CHANGED
|
@@ -83,6 +83,15 @@ impl X11Desktop {
|
|
|
83
83
|
/// compositor this is every window drawn onto the framebuffer, which is what
|
|
84
84
|
/// the viewer expects to see.
|
|
85
85
|
pub fn capture(&mut self, max_width: usize, max_height: usize) -> Result<I420> {
|
|
86
|
+
self.capture_with_pixels(max_width, max_height)
|
|
87
|
+
.map(|(frame, _)| frame)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pub fn capture_with_pixels(
|
|
91
|
+
&mut self,
|
|
92
|
+
max_width: usize,
|
|
93
|
+
max_height: usize,
|
|
94
|
+
) -> Result<(I420, Vec<u8>)> {
|
|
86
95
|
let image = self
|
|
87
96
|
.connection
|
|
88
97
|
.get_image(
|
|
@@ -110,16 +119,27 @@ impl X11Desktop {
|
|
|
110
119
|
// The server's depth is the only field that can tell us how the pixel is
|
|
111
120
|
// packed; the engine assumes the byte order every TrueColor server uses.
|
|
112
121
|
let _ = self.depth;
|
|
113
|
-
to_i420(
|
|
122
|
+
let frame = to_i420(
|
|
123
|
+
&image.data,
|
|
124
|
+
self.width,
|
|
125
|
+
self.height,
|
|
126
|
+
stride,
|
|
127
|
+
crate::convert::PixelFormat::Bgrx,
|
|
128
|
+
width,
|
|
129
|
+
height,
|
|
130
|
+
)
|
|
131
|
+
.context("the captured X11 pixels are not a format this engine can read")?;
|
|
132
|
+
let raw = crate::convert::to_bgrx(
|
|
114
133
|
&image.data,
|
|
115
134
|
self.width,
|
|
116
135
|
self.height,
|
|
117
136
|
stride,
|
|
118
|
-
crate::
|
|
137
|
+
crate::convert::PixelFormat::Bgrx,
|
|
119
138
|
width,
|
|
120
139
|
height,
|
|
121
140
|
)
|
|
122
|
-
.context("the captured X11 pixels
|
|
141
|
+
.context("the captured X11 pixels could not be copied")?;
|
|
142
|
+
Ok((frame, raw))
|
|
123
143
|
}
|
|
124
144
|
|
|
125
145
|
fn flush(&self) -> Result<()> {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
use crate::convert::I420;
|
|
2
|
+
use anyhow::{bail, Result};
|
|
3
|
+
|
|
4
|
+
pub mod button {
|
|
5
|
+
pub const LEFT: u8 = 1;
|
|
6
|
+
pub const MIDDLE: u8 = 2;
|
|
7
|
+
pub const RIGHT: u8 = 3;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
pub struct X11Desktop;
|
|
11
|
+
|
|
12
|
+
impl X11Desktop {
|
|
13
|
+
pub fn connect(_: Option<&str>) -> Result<Self> {
|
|
14
|
+
bail!("the X11 backend is only available on Linux")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pub fn screen_size(&self) -> (i32, i32) {
|
|
18
|
+
(0, 0)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn capture_with_pixels(&mut self, _: usize, _: usize) -> Result<(I420, Vec<u8>)> {
|
|
22
|
+
bail!("the X11 backend is only available on Linux")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pub fn move_pointer(&mut self, _: i64, _: i64) -> Result<()> {
|
|
26
|
+
bail!("the X11 backend is only available on Linux")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
pub fn button(&mut self, _: u8, _: bool) -> Result<()> {
|
|
30
|
+
bail!("the X11 backend is only available on Linux")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn scroll(&mut self, _: i64, _: i64) -> Result<()> {
|
|
34
|
+
bail!("the X11 backend is only available on Linux")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn key(&mut self, _: i16, _: bool) -> Result<()> {
|
|
38
|
+
bail!("the X11 backend is only available on Linux")
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -67,12 +67,15 @@ const overlay = document.getElementById('overlay');
|
|
|
67
67
|
const status = document.getElementById('status');
|
|
68
68
|
const reconnect = document.getElementById('reconnect');
|
|
69
69
|
const keyboard = document.getElementById('remote-keyboard');
|
|
70
|
+
const keyboardButton = document.getElementById('keyboard');
|
|
70
71
|
const copy = document.getElementById('copy');
|
|
71
72
|
const paste = document.getElementById('paste');
|
|
72
73
|
|
|
73
74
|
let socket = null;
|
|
74
75
|
let peer = null;
|
|
75
76
|
let channel = null;
|
|
77
|
+
let currentSessionId = null;
|
|
78
|
+
let pendingRemoteCandidates = [];
|
|
76
79
|
let surface = { width: 0, height: 0 };
|
|
77
80
|
let nextSeq = 0;
|
|
78
81
|
let pending = new Map();
|
|
@@ -265,10 +268,16 @@ async function start() {
|
|
|
265
268
|
try {
|
|
266
269
|
const capabilities = await request('capabilities');
|
|
267
270
|
const clipboardAvailable = capabilities.clipboard?.read === true && capabilities.clipboard?.write === true;
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
271
|
+
const controlAvailable = capabilities.input?.pointer === true && capabilities.input?.keyboard === true;
|
|
272
|
+
const permissions = ['view'];
|
|
273
|
+
if (controlAvailable) permissions.push('control');
|
|
274
|
+
if (clipboardAvailable) permissions.push('clipboard');
|
|
275
|
+
const session = await request('session.open', { permissions, max_fps: 30 });
|
|
276
|
+
currentSessionId = session.sessionId;
|
|
277
|
+
keyboard.disabled = !controlAvailable;
|
|
278
|
+
keyboardButton.disabled = !controlAvailable;
|
|
279
|
+
keyboardButton.title = controlAvailable ? '' : capabilities.input?.unavailable_reason?.remedy ?? 'This session is view-only';
|
|
280
|
+
video.style.pointerEvents = controlAvailable ? 'auto' : 'none';
|
|
272
281
|
copy.hidden = !clipboardAvailable;
|
|
273
282
|
paste.hidden = !clipboardAvailable;
|
|
274
283
|
surface = session.geometry.encoded;
|
|
@@ -332,6 +341,9 @@ async function start() {
|
|
|
332
341
|
// The engine is the offerer, so this side waits for the offer and answers.
|
|
333
342
|
const offer = await waitForOffer();
|
|
334
343
|
await pc.setRemoteDescription({ type: 'offer', sdp: offer });
|
|
344
|
+
for (const pending of pendingRemoteCandidates.splice(0)) {
|
|
345
|
+
if (pending.sessionId === session.sessionId) await pc.addIceCandidate(pending.candidate);
|
|
346
|
+
}
|
|
335
347
|
const answer = await pc.createAnswer();
|
|
336
348
|
await pc.setLocalDescription(answer);
|
|
337
349
|
await request('session.description', {
|
|
@@ -371,6 +383,8 @@ function teardown(message) {
|
|
|
371
383
|
peer?.close();
|
|
372
384
|
peer = null;
|
|
373
385
|
channel = null;
|
|
386
|
+
currentSessionId = null;
|
|
387
|
+
pendingRemoteCandidates = [];
|
|
374
388
|
video.srcObject = null;
|
|
375
389
|
copy.hidden = true;
|
|
376
390
|
paste.hidden = true;
|
|
@@ -392,6 +406,21 @@ function connectSocket() {
|
|
|
392
406
|
else waiting.resolve(message.result);
|
|
393
407
|
return;
|
|
394
408
|
}
|
|
409
|
+
if (message.event === 'session.candidate') {
|
|
410
|
+
const params = message.params;
|
|
411
|
+
if (currentSessionId !== null && params.sessionId !== currentSessionId) return;
|
|
412
|
+
const candidate = {
|
|
413
|
+
candidate: params.candidate,
|
|
414
|
+
sdpMid: params.sdpMid,
|
|
415
|
+
sdpMLineIndex: params.sdpMLineIndex,
|
|
416
|
+
};
|
|
417
|
+
if (peer?.remoteDescription) {
|
|
418
|
+
void peer.addIceCandidate(candidate).catch(() => undefined);
|
|
419
|
+
} else {
|
|
420
|
+
pendingRemoteCandidates.push({ sessionId: params.sessionId, candidate });
|
|
421
|
+
}
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
395
424
|
if (message.event === 'session.description') {
|
|
396
425
|
const sdp = message.params.description.sdp;
|
|
397
426
|
if (offerWaiter !== null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desklink/host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Per-user, on-demand desktop capture, encode and input engine with a versioned local control protocol",
|
|
6
6
|
"type": "module",
|
|
@@ -33,6 +33,6 @@
|
|
|
33
33
|
"ws": "^8.18.0"
|
|
34
34
|
},
|
|
35
35
|
"optionalDependencies": {
|
|
36
|
-
"@desklink/host-linux-x64-gnu": "0.
|
|
36
|
+
"@desklink/host-linux-x64-gnu": "0.2.0"
|
|
37
37
|
}
|
|
38
38
|
}
|