@desklink/host 0.3.1 → 0.4.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/README.md +30 -9
- package/bin/desklink-host.mjs +2 -1
- package/dist/bridge.d.ts +3 -4
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +18 -7
- package/dist/bridge.js.map +1 -1
- package/dist/engineProcess.d.ts.map +1 -1
- package/dist/engineProcess.js +0 -1
- package/dist/engineProcess.js.map +1 -1
- package/dist/protocol.d.ts +4 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/docs/BOUNDARY.md +8 -8
- package/docs/PROTOCOL.md +72 -45
- package/engine/Cargo.lock +1 -1
- package/engine/Cargo.toml +1 -1
- package/engine/build.rs +1 -0
- package/engine/native/agent_indicator.h +57 -0
- package/engine/native/agent_overlay_wayland.c +47 -26
- package/engine/native/agent_overlay_x11.c +116 -57
- package/engine/src/capture.rs +4 -2
- package/engine/src/clipboard.rs +174 -0
- package/engine/src/indicator.rs +175 -10
- package/engine/src/main.rs +2 -2
- package/engine/src/peer.rs +10 -4
- package/engine/src/protocol.rs +7 -10
- package/engine/src/session.rs +305 -209
- package/engine/src/x11.rs +29 -29
- package/engine/src/x11_mac.rs +4 -0
- package/examples/reference-client.html +3 -5
- package/package.json +3 -3
package/engine/src/x11.rs
CHANGED
|
@@ -112,6 +112,14 @@ impl X11Desktop {
|
|
|
112
112
|
})
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
pub fn input_available(&self) -> bool {
|
|
116
|
+
self.connection
|
|
117
|
+
.xtest_get_version(2, 2)
|
|
118
|
+
.ok()
|
|
119
|
+
.and_then(|cookie| cookie.reply().ok())
|
|
120
|
+
.is_some()
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
/// Allocate one MIT-SHM segment for the full root and map it here. Any
|
|
116
124
|
/// failure means "no SHM on this server", never an error: the caller
|
|
117
125
|
/// falls back to socket GetImage and reports it via [`Self::capture_path`].
|
|
@@ -235,6 +243,13 @@ impl X11Desktop {
|
|
|
235
243
|
max_width: usize,
|
|
236
244
|
max_height: usize,
|
|
237
245
|
) -> Result<(I420, Vec<u8>)> {
|
|
246
|
+
let raw = self.capture_raw()?;
|
|
247
|
+
let frame = self.convert(&raw, max_width, max_height)?;
|
|
248
|
+
Ok((frame, raw))
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/// The root image as `width * 4`-byte BGRX rows at the screen's own size.
|
|
252
|
+
pub fn capture_raw(&mut self) -> Result<Vec<u8>> {
|
|
238
253
|
// The server's depth is the only field that can tell us how the pixel is
|
|
239
254
|
// packed; the engine assumes the byte order every TrueColor server uses.
|
|
240
255
|
let _ = self.depth;
|
|
@@ -242,7 +257,7 @@ impl X11Desktop {
|
|
|
242
257
|
// Prefer MIT-SHM. A segment that worked at connect can still break
|
|
243
258
|
// later (revoked by the server); that degrades to the socket path,
|
|
244
259
|
// which keeps capturing, instead of stalling on failing grabs.
|
|
245
|
-
if let Ok(output) = self.capture_shm(stride
|
|
260
|
+
if let Ok(output) = self.capture_shm(stride) {
|
|
246
261
|
return Ok(output);
|
|
247
262
|
}
|
|
248
263
|
self.shm = None;
|
|
@@ -268,35 +283,34 @@ impl X11Desktop {
|
|
|
268
283
|
self.height
|
|
269
284
|
);
|
|
270
285
|
}
|
|
286
|
+
Ok(image.data)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/// Downscale a [`Self::capture_raw`] image into the caller's box as I420.
|
|
290
|
+
pub fn convert(&self, raw: &[u8], max_width: usize, max_height: usize) -> Result<I420> {
|
|
271
291
|
let (width, height) = fit(self.width, self.height, max_width, max_height);
|
|
272
|
-
|
|
273
|
-
|
|
292
|
+
to_i420(
|
|
293
|
+
raw,
|
|
274
294
|
self.width,
|
|
275
295
|
self.height,
|
|
276
|
-
|
|
296
|
+
self.width * 4,
|
|
277
297
|
crate::convert::PixelFormat::Bgrx,
|
|
278
298
|
width,
|
|
279
299
|
height,
|
|
280
300
|
)
|
|
281
|
-
.context("the captured X11 pixels are not a format this engine can read")
|
|
282
|
-
Ok((frame, image.data))
|
|
301
|
+
.context("the captured X11 pixels are not a format this engine can read")
|
|
283
302
|
}
|
|
284
303
|
|
|
285
304
|
/// One MIT-SHM grab into the reused segment. The caller tears the
|
|
286
305
|
/// segment down on any error and falls back to the socket path.
|
|
287
|
-
fn capture_shm(
|
|
288
|
-
&self,
|
|
289
|
-
stride: usize,
|
|
290
|
-
max_width: usize,
|
|
291
|
-
max_height: usize,
|
|
292
|
-
) -> Result<(I420, Vec<u8>)> {
|
|
306
|
+
fn capture_shm(&self, stride: usize) -> Result<Vec<u8>> {
|
|
293
307
|
let (seg, ptr, len) = match &self.shm {
|
|
294
308
|
Some(shm) => (shm.seg, shm.ptr, shm.len),
|
|
295
309
|
None => anyhow::bail!("no MIT-SHM segment"),
|
|
296
310
|
};
|
|
297
311
|
// MIT-SHM: the server writes the root straight into our mapping;
|
|
298
312
|
// the reply only signals completion. The segment is reused, so
|
|
299
|
-
// copy out
|
|
313
|
+
// copy out before the next grab.
|
|
300
314
|
self.connection
|
|
301
315
|
.shm_get_image(
|
|
302
316
|
self.root,
|
|
@@ -325,22 +339,8 @@ impl X11Desktop {
|
|
|
325
339
|
);
|
|
326
340
|
}
|
|
327
341
|
// Copy out of the reused segment before the next grab overwrites
|
|
328
|
-
// it
|
|
329
|
-
|
|
330
|
-
// moves its reply instead.
|
|
331
|
-
let owned = pixels.to_vec();
|
|
332
|
-
let (width, height) = fit(self.width, self.height, max_width, max_height);
|
|
333
|
-
let frame = to_i420(
|
|
334
|
-
&owned,
|
|
335
|
-
self.width,
|
|
336
|
-
self.height,
|
|
337
|
-
stride,
|
|
338
|
-
crate::convert::PixelFormat::Bgrx,
|
|
339
|
-
width,
|
|
340
|
-
height,
|
|
341
|
-
)
|
|
342
|
-
.context("the captured X11 pixels are not a format this engine can read")?;
|
|
343
|
-
Ok((frame, owned))
|
|
342
|
+
// it. This copy is inherent to SHM; the socket arm moves its reply.
|
|
343
|
+
Ok(pixels.to_vec())
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
fn flush(&self) -> Result<()> {
|
package/engine/src/x11_mac.rs
CHANGED
|
@@ -269,14 +269,12 @@ async function start() {
|
|
|
269
269
|
const capabilities = await request('capabilities');
|
|
270
270
|
const clipboardAvailable = capabilities.clipboard?.read === true && capabilities.clipboard?.write === true;
|
|
271
271
|
const controlAvailable = capabilities.input?.pointer === true && capabilities.input?.keyboard === true;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (clipboardAvailable) permissions.push('clipboard');
|
|
275
|
-
const session = await request('session.open', { permissions });
|
|
272
|
+
// The bridge token is the pairing: a paired session has view, input and clipboard.
|
|
273
|
+
const session = await request('session.open', {});
|
|
276
274
|
currentSessionId = session.sessionId;
|
|
277
275
|
keyboard.disabled = !controlAvailable;
|
|
278
276
|
keyboardButton.disabled = !controlAvailable;
|
|
279
|
-
keyboardButton.title = controlAvailable ? '' : capabilities.input?.unavailable_reason?.remedy ?? '
|
|
277
|
+
keyboardButton.title = controlAvailable ? '' : capabilities.input?.unavailable_reason?.remedy ?? 'Input is unavailable on this machine';
|
|
280
278
|
video.style.pointerEvents = controlAvailable ? 'auto' : 'none';
|
|
281
279
|
copy.hidden = !clipboardAvailable;
|
|
282
280
|
paste.hidden = !clipboardAvailable;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desklink/host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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,7 +33,7 @@
|
|
|
33
33
|
"ws": "8.21.3"
|
|
34
34
|
},
|
|
35
35
|
"optionalDependencies": {
|
|
36
|
-
"@desklink/host-linux-x64-gnu": "0.
|
|
37
|
-
"@desklink/host-darwin-arm64": "0.
|
|
36
|
+
"@desklink/host-linux-x64-gnu": "0.4.0",
|
|
37
|
+
"@desklink/host-darwin-arm64": "0.4.0"
|
|
38
38
|
}
|
|
39
39
|
}
|