@desklink/host 0.5.10 → 0.5.11
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 +14 -44
- package/engine/Cargo.lock +1 -1
- package/engine/Cargo.toml +1 -1
- package/engine/native/agent_overlay_mac.m +19 -12
- package/engine/native/agent_overlay_wayland.c +15 -12
- package/engine/native/agent_overlay_x11.c +66 -17
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -104,37 +104,7 @@ The one step no install does for you is [kernel input access](#kernel-input-acce
|
|
|
104
104
|
and only the portal backend needs it.
|
|
105
105
|
|
|
106
106
|
macOS arm64 (preview) also has a prebuilt engine. There is no prebuilt engine for other platforms. Another Linux (arm64, musl)
|
|
107
|
-
can [build it from source](#building-from-source).
|
|
108
|
-
`x86_64-pc-windows-msvc`: static libvpx enables `serve` and consumer-fed H.264
|
|
109
|
-
encoded sources. Native capture, input, clipboard and the agent indicator are
|
|
110
|
-
unavailable. Native Windows desktop capture sessions are not supported yet. Node resolution
|
|
111
|
-
is an opt-in preview for Windows 11 22H2+ x64: set `DESKLINK_WINDOWS=1` and
|
|
112
|
-
point `DESKLINK_ENGINE` at a built `desklink-host.exe`. Windows platform packages
|
|
113
|
-
are not published yet. Without static libvpx, the executable's `capabilities`
|
|
114
|
-
command remains available, but CLI `capabilities` and `bridge` require `serve`.
|
|
115
|
-
The Windows bridge defaults to `--source display` and handles Ctrl+Break;
|
|
116
|
-
consumer-fed video requires `--source encoded`.
|
|
117
|
-
A startup exit `0xC0000135` is reported as `missing-system-library` (a required
|
|
118
|
-
DLL could not load); spawn failures are reported as `engine-spawn-failed` with
|
|
119
|
-
the underlying error. Elevated windows and the secure desktop remain outside
|
|
120
|
-
Windows control support.
|
|
121
|
-
|
|
122
|
-
On Windows with Visual Studio Build Tools and PowerShell 7, build libvpx with
|
|
123
|
-
`release/build-libvpx-win.ps1 -OutDir <new-build-directory>`, set
|
|
124
|
-
`DESKLINK_VPX_STATIC_DIR` to its `prefix` directory and
|
|
125
|
-
`RUSTFLAGS=-Ctarget-feature=+crt-static`, then run `cargo build --locked --target
|
|
126
|
-
x86_64-pc-windows-msvc --manifest-path engine/Cargo.toml`. The build retains
|
|
127
|
-
libvpx's licence and patent grant in `prefix/THIRD_PARTY_LICENSES.txt`.
|
|
128
|
-
Without that prefix the compile seam still builds and refuses `serve`.
|
|
129
|
-
Hosted Windows CI resolves the opt-in engine through the published Node API
|
|
130
|
-
and completes a `serve` hello/capabilities handshake with clean shutdown; it
|
|
131
|
-
also installs the packed packages into an empty project, checks the installed
|
|
132
|
-
`@desklink/host` tree against its pack manifest, starts the engine from that
|
|
133
|
-
install, and checks Chrome decode and input relay, and DLL
|
|
134
|
-
dependencies. That install proof is installation and startup on a hosted
|
|
135
|
-
machine: the engine still comes from a source build, because Windows platform
|
|
136
|
-
packages are not published, and capture, input, encoding quality, mixed DPI, GPU
|
|
137
|
-
encoding and elevation stay with the dedicated rig lanes.
|
|
107
|
+
can [build it from source](#building-from-source).
|
|
138
108
|
|
|
139
109
|
### macOS (preview)
|
|
140
110
|
|
|
@@ -305,7 +275,7 @@ source-coordinate `x`, `y`, `visible`, the cursor image's `hotspot` where
|
|
|
305
275
|
known, and monotonic `timestamp_us` (the same clock as local frame metadata).
|
|
306
276
|
A compositor without that protocol still captures, cursor hidden, and says so
|
|
307
277
|
in `cursor.limitation`. X11 reports positions even on still
|
|
308
|
-
frames and already captures root pixels without a cursor. macOS
|
|
278
|
+
frames and already captures root pixels without a cursor. macOS and consumer-encoded
|
|
309
279
|
sources return `cursor-unavailable` for hidden requests. Omitting the option
|
|
310
280
|
preserves existing behavior. See [the cursor protocol](https://github.com/umeranjum17/desklink/blob/main/packages/desktop-host/docs/PROTOCOL.md#cursor-free-capture).
|
|
311
281
|
|
|
@@ -385,7 +355,7 @@ platform". Linux variants are published only after their own qualification; macO
|
|
|
385
355
|
is published as a default-enabled preview, not as qualified desktop support.
|
|
386
356
|
See [Install and run](#install-and-run) for current support. Another variant,
|
|
387
357
|
such as Linux arm64, needs a real machine to run a real journey first. macOS
|
|
388
|
-
|
|
358
|
+
is a separate backend, not a separate build.
|
|
389
359
|
|
|
390
360
|
### Building and packing a release
|
|
391
361
|
|
|
@@ -445,19 +415,19 @@ the current platform's host and engine tarballs into an empty project and checks
|
|
|
445
415
|
|
|
446
416
|
Publishing is automatic. Merging a version bump to `main` runs
|
|
447
417
|
`.github/workflows/release.yml`: the test, build, build-pack and per-OS engine
|
|
448
|
-
jobs gate a publish job that releases every package
|
|
449
|
-
yet on npm, in dependency order — the engine
|
|
450
|
-
`@desklink/host
|
|
451
|
-
`@desklink/axi` gated on the host (not the reverse), then
|
|
418
|
+
jobs gate a publish job (`release/publish-set.sh`) that releases every package
|
|
419
|
+
whose exact version is not yet on npm, in dependency order — the engine
|
|
420
|
+
platform packages first, then `@desklink/host`, then `@desklink/axi`, then
|
|
452
421
|
`@desklink/react-native` — all with `--provenance`. A version lockstep check
|
|
453
422
|
fails the workflow when `@desklink/host`, `@desklink/react-native` and
|
|
454
|
-
`@desklink/axi` disagree, so a half-bump never reaches publish.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
`release/
|
|
460
|
-
|
|
423
|
+
`@desklink/axi` disagree, so a half-bump never reaches publish. The publish job
|
|
424
|
+
publishes nothing unless every tarball of the set is present and the registry
|
|
425
|
+
answers for each package.
|
|
426
|
+
Each package then waits until the registry serves the one before it, within a
|
|
427
|
+
30-minute budget that covers npm's processing and CDN lag (shared through
|
|
428
|
+
`release/readback-budget.json` with the manual `release/publish.mjs` route).
|
|
429
|
+
The first failure stops the job with an error naming what is served and what
|
|
430
|
+
is not, so a failed run leaves only a prefix of that order published.
|
|
461
431
|
The workflow uses trusted publishing (OIDC) and no npm token. Re-runs skip
|
|
462
432
|
versions already published.
|
|
463
433
|
|
package/engine/Cargo.lock
CHANGED
package/engine/Cargo.toml
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
NSPoint target, cursor;
|
|
10
10
|
CFTimeInterval clickAt, typeAt, endingAt;
|
|
11
11
|
BOOL positioned;
|
|
12
|
-
BOOL pointMode, pointVisible;
|
|
13
|
-
CFTimeInterval pointDeadline;
|
|
12
|
+
BOOL pointMode, pointVisible, pointDrawnHidden;
|
|
13
|
+
CFTimeInterval pointDeadline, pointStarted;
|
|
14
14
|
NSString *pointLabel;
|
|
15
15
|
}
|
|
16
16
|
- (void)event:(char)kind x:(double)x y:(double)y;
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- (void)enablePointMode { pointMode = YES; }
|
|
25
25
|
- (void)pointX:(double)x y:(double)y timeout:(unsigned)timeout label:(NSString *)label {
|
|
26
26
|
cursor = NSMakePoint(x, self.bounds.size.height-y);
|
|
27
|
-
pointLabel = label; pointVisible = YES;
|
|
27
|
+
pointLabel = label; pointVisible = YES; pointStarted = CACurrentMediaTime();
|
|
28
28
|
pointDeadline = CACurrentMediaTime() + timeout/1000.0;
|
|
29
29
|
[self setNeedsDisplay:YES];
|
|
30
30
|
}
|
|
@@ -43,7 +43,9 @@
|
|
|
43
43
|
- (void)end { if (pointMode) { [NSApp terminate:nil]; return; } endingAt = CACurrentMediaTime(); }
|
|
44
44
|
- (void)tick {
|
|
45
45
|
if (pointMode) {
|
|
46
|
-
if (pointVisible && CACurrentMediaTime() >= pointDeadline)
|
|
46
|
+
if (pointVisible && CACurrentMediaTime() >= pointDeadline) pointVisible = NO;
|
|
47
|
+
if (pointVisible || !pointDrawnHidden) [self setNeedsDisplay:YES];
|
|
48
|
+
pointDrawnHidden = !pointVisible;
|
|
47
49
|
return;
|
|
48
50
|
}
|
|
49
51
|
if (endingAt && CACurrentMediaTime() - endingAt > 0.7) { [NSApp terminate:nil]; return; }
|
|
@@ -56,17 +58,22 @@
|
|
|
56
58
|
if (pointMode) {
|
|
57
59
|
if (!pointVisible) return;
|
|
58
60
|
[[NSColor colorWithCalibratedRed:0.30 green:0.62 blue:0.82 alpha:1] set];
|
|
59
|
-
|
|
60
|
-
ring
|
|
61
|
-
|
|
61
|
+
// 72pt ring with a 1.2s pulse wave growing out of it.
|
|
62
|
+
NSBezierPath *ring = [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(cursor.x-36, cursor.y-36, 72, 72)];
|
|
63
|
+
ring.lineWidth = 6; [ring stroke];
|
|
64
|
+
[[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(cursor.x-6,cursor.y-6,12,12)] fill];
|
|
65
|
+
double phase = fmod(CACurrentMediaTime() - pointStarted, 1.2) / 1.2, wave = 36 + 18*phase;
|
|
66
|
+
[[NSColor colorWithCalibratedRed:0.30 green:0.62 blue:0.82 alpha:1-phase] set];
|
|
67
|
+
NSBezierPath *pulse = [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(cursor.x-wave, cursor.y-wave, 2*wave, 2*wave)];
|
|
68
|
+
pulse.lineWidth = 6 - 4*phase; [pulse stroke];
|
|
62
69
|
if (pointLabel.length) {
|
|
63
|
-
NSDictionary *style = @{NSFontAttributeName: [NSFont
|
|
70
|
+
NSDictionary *style = @{NSFontAttributeName: [NSFont boldSystemFontOfSize:26], NSForegroundColorAttributeName: NSColor.whiteColor};
|
|
64
71
|
NSSize size = [pointLabel sizeWithAttributes:style];
|
|
65
|
-
CGFloat x = fmax(0, fmin(cursor.x+
|
|
66
|
-
CGFloat y = fmax(0, fmin(cursor.y-
|
|
72
|
+
CGFloat x = fmax(0, fmin(cursor.x+66, self.bounds.size.width-size.width-24));
|
|
73
|
+
CGFloat y = fmax(0, fmin(cursor.y-20, self.bounds.size.height-40));
|
|
67
74
|
[[NSColor colorWithCalibratedRed:0.07 green:0.19 blue:0.28 alpha:1] set];
|
|
68
|
-
[[NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x,y,size.width+
|
|
69
|
-
[pointLabel drawAtPoint:NSMakePoint(x+
|
|
75
|
+
[[NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x,y,size.width+24,40) xRadius:6 yRadius:6] fill];
|
|
76
|
+
[pointLabel drawAtPoint:NSMakePoint(x+12,y+(40-size.height)/2) withAttributes:style];
|
|
70
77
|
}
|
|
71
78
|
return;
|
|
72
79
|
}
|
|
@@ -203,27 +203,30 @@ static FT_Face point_font(FT_Library *library) {
|
|
|
203
203
|
FcResult result;FcPattern *match=FcFontMatch(NULL,pattern,&result);FcPatternDestroy(pattern);
|
|
204
204
|
FcChar8 *path=NULL;FT_Face face=NULL;
|
|
205
205
|
if(match && FcPatternGetString(match,FC_FILE,0,&path)==FcResultMatch)
|
|
206
|
-
if(!FT_New_Face(*library,(const char *)path,0,&face))FT_Set_Pixel_Sizes(face,0,
|
|
206
|
+
if(!FT_New_Face(*library,(const char *)path,0,&face))FT_Set_Pixel_Sizes(face,0,26);
|
|
207
207
|
if(match)FcPatternDestroy(match);
|
|
208
208
|
return face;
|
|
209
209
|
}
|
|
210
|
-
|
|
210
|
+
// 72 logical px ring with a 1.2 s pulse wave growing out of it, and a 40px
|
|
211
|
+
// badge with 26px text. Opaque pixels only, like the X11 cue.
|
|
212
|
+
static void draw_point(struct Overlay *o,struct Buffer *b,double x,double y,const char *label,FT_Face font,int visible,double elapsed) {
|
|
211
213
|
memset(b->pixels,0,b->length);if(!visible)return;
|
|
212
214
|
int w=o->width,h=o->height;
|
|
213
|
-
|
|
215
|
+
double phase=fmod(elapsed,1.2)/1.2,wave=36+18*phase,wave_half=3-2*phase;
|
|
216
|
+
for(int py=fmax(0,y-56);py<fmin(h,y+57);py++)for(int px=fmax(0,x-56);px<fmin(w,x+57);px++) {
|
|
214
217
|
double d=hypot(px-x,py-y);
|
|
215
|
-
if(fabs(d-
|
|
218
|
+
if(fabs(d-36)<=3 || d<=6 || fabs(d-wave)<=wave_half)b->pixels[(size_t)py*w+px]=0xff4c9ed0;
|
|
216
219
|
}
|
|
217
220
|
if(!*label)return;
|
|
218
|
-
int badge_w=
|
|
221
|
+
int badge_w=24;
|
|
219
222
|
for(const char *p=label;*p;p++)if(!FT_Load_Char(font,(unsigned char)*p,FT_LOAD_DEFAULT))badge_w+=font->glyph->advance.x>>6;
|
|
220
|
-
badge_w=fmin(w,badge_w);int bx=fmax(0,fmin(x+
|
|
221
|
-
for(int py=by;py<fmin(h,by+
|
|
222
|
-
int pen=bx+
|
|
223
|
+
badge_w=fmin(w,badge_w);int bx=fmax(0,fmin(x+66,w-badge_w)),by=fmax(0,fmin(y-20,h-40));
|
|
224
|
+
for(int py=by;py<fmin(h,by+40);py++)for(int px=bx;px<bx+badge_w;px++)b->pixels[(size_t)py*w+px]=0xff123047;
|
|
225
|
+
int pen=bx+12;
|
|
223
226
|
for(const char *p=label;*p;p++)if(!FT_Load_Char(font,(unsigned char)*p,FT_LOAD_RENDER)) {
|
|
224
227
|
FT_GlyphSlot g=font->glyph;
|
|
225
228
|
for(unsigned row=0;row<g->bitmap.rows;row++)for(unsigned col=0;col<g->bitmap.width;col++) {
|
|
226
|
-
int px=pen+g->bitmap_left+col,py=by+
|
|
229
|
+
int px=pen+g->bitmap_left+col,py=by+28-g->bitmap_top+row;
|
|
227
230
|
if(px<0||px>=w||py<0||py>=h)continue;
|
|
228
231
|
unsigned a=g->bitmap.buffer[row*g->bitmap.pitch+col];
|
|
229
232
|
unsigned r=(18*(255-a)+255*a)/255,green=(48*(255-a)+255*a)/255,blue=(71*(255-a)+255*a)/255;
|
|
@@ -314,7 +317,7 @@ static int overlay_main(int source_w,int source_h,int point_mode,int origin_know
|
|
|
314
317
|
unsigned timeout=0;int offset=0;
|
|
315
318
|
if(sscanf(pending,"P %lf %lf %u %n",&px,&py,&timeout,&offset)==3 && offset && timeout>=1 && timeout<=120000 && strlen(pending+offset)<=96) {
|
|
316
319
|
snprintf(label,sizeof(label),"%s",pending+offset);deadline=now()+timeout/1000.0;active=1;dirty=1;
|
|
317
|
-
visible=locate(&o,px,py,&x,&y);
|
|
320
|
+
visible=locate(&o,px,py,&x,&y);cue_started=now();
|
|
318
321
|
}
|
|
319
322
|
}
|
|
320
323
|
else if(kind=='S')ending=now();
|
|
@@ -336,11 +339,11 @@ static int overlay_main(int source_w,int source_h,int point_mode,int origin_know
|
|
|
336
339
|
used=0;
|
|
337
340
|
}else if(used<sizeof(pending)-1)pending[used++]=bytes[i];
|
|
338
341
|
}
|
|
339
|
-
if(point_mode && visible
|
|
342
|
+
if(point_mode && visible){if(now()>=deadline)visible=0;dirty=1;}
|
|
340
343
|
if(!active || (point_mode&&!dirty))continue;
|
|
341
344
|
if(!point_mode){x+=(tx-x)*0.24;y+=(ty-y)*0.24;}
|
|
342
345
|
for(int i=0;i<2;i++)if(!o.buffers[i].busy) {
|
|
343
|
-
if(point_mode)draw_point(&o,&o.buffers[i],x,y,label,font,visible);
|
|
346
|
+
if(point_mode)draw_point(&o,&o.buffers[i],x,y,label,font,visible,now()-cue_started);
|
|
344
347
|
else draw(&o,&o.buffers[i],x,y,click,typed,ending,started,cue_started);
|
|
345
348
|
dirty=0;
|
|
346
349
|
o.buffers[i].busy=1;
|
|
@@ -163,6 +163,50 @@ int desklink_agent_overlay_main(const char *display_name) {
|
|
|
163
163
|
return 0;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// The ring is 72 logical px across, a 6px band with a pulse wave that grows
|
|
167
|
+
// out from it every PULSE_SECONDS, so the cue reads at a glance. The label is
|
|
168
|
+
// the core "fixed" font drawn at 2x, because a core X server offers nothing larger.
|
|
169
|
+
#define POINT_RING 36
|
|
170
|
+
#define POINT_PULSE_SECONDS 1.2
|
|
171
|
+
#define POINT_BADGE_H 40
|
|
172
|
+
#define POINT_FRAME_SECONDS 0.033
|
|
173
|
+
|
|
174
|
+
static void point_shape(Display *d, Window window, Pixmap mask, GC shape, int x, int y,
|
|
175
|
+
int badge_x, int badge_y, int badge_w, double elapsed) {
|
|
176
|
+
double phase = fmod(elapsed, POINT_PULSE_SECONDS) / POINT_PULSE_SECONDS;
|
|
177
|
+
int wave = POINT_RING + (int)lround(18*phase), wave_width = 6 - (int)lround(4*phase);
|
|
178
|
+
XSetForeground(d, shape, 0); XFillRectangle(d, mask, shape, 0, 0, 0xffff, 0xffff);
|
|
179
|
+
XSetForeground(d, shape, 1);
|
|
180
|
+
XSetLineAttributes(d, shape, 6, LineSolid, CapRound, JoinRound);
|
|
181
|
+
XDrawArc(d, mask, shape, x-POINT_RING, y-POINT_RING, 2*POINT_RING, 2*POINT_RING, 0, 360*64);
|
|
182
|
+
XFillArc(d, mask, shape, x-6, y-6, 12, 12, 0, 360*64);
|
|
183
|
+
XSetLineAttributes(d, shape, wave_width, LineSolid, CapRound, JoinRound);
|
|
184
|
+
XDrawArc(d, mask, shape, x-wave, y-wave, 2*wave, 2*wave, 0, 360*64);
|
|
185
|
+
if (badge_w) XFillRectangle(d, mask, shape, badge_x, badge_y, badge_w, POINT_BADGE_H);
|
|
186
|
+
XShapeCombineMask(d, window, ShapeBounding, 0, 0, mask, ShapeSet);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Text is rasterised once at 1x and drawn as 2x2 blocks.
|
|
190
|
+
static void point_text(Display *d, Window window, GC ink, XFontStruct *font, const char *label,
|
|
191
|
+
int len, int x, int y) {
|
|
192
|
+
int w = XTextWidth(font, label, len), h = font->ascent + font->descent;
|
|
193
|
+
Pixmap bits = XCreatePixmap(d, window, w, h, 1);
|
|
194
|
+
GC gc = XCreateGC(d, bits, 0, NULL);
|
|
195
|
+
XSetFont(d, gc, font->fid);
|
|
196
|
+
XSetForeground(d, gc, 0); XFillRectangle(d, bits, gc, 0, 0, w, h);
|
|
197
|
+
XSetForeground(d, gc, 1); XDrawString(d, bits, gc, 0, font->ascent, label, len);
|
|
198
|
+
XImage *image = XGetImage(d, bits, 0, 0, w, h, 1, ZPixmap);
|
|
199
|
+
XRectangle *blocks = malloc(sizeof(XRectangle) * (size_t)w * h);
|
|
200
|
+
int count = 0;
|
|
201
|
+
if (image && blocks)
|
|
202
|
+
for (int row = 0; row < h; row++) for (int col = 0; col < w; col++)
|
|
203
|
+
if (XGetPixel(image, col, row))
|
|
204
|
+
blocks[count++] = (XRectangle){(short)(x+2*col), (short)(y+2*row), 2, 2};
|
|
205
|
+
if (count) XFillRectangles(d, window, ink, blocks, count);
|
|
206
|
+
free(blocks); if (image) XDestroyImage(image);
|
|
207
|
+
XFreeGC(d, gc); XFreePixmap(d, bits);
|
|
208
|
+
}
|
|
209
|
+
|
|
166
210
|
// Point cues are independent of the real cursor. A shaped override-redirect
|
|
167
211
|
// window works without a compositor; an empty input shape passes every event
|
|
168
212
|
// through and mapping it never asks for keyboard focus. It intentionally appears
|
|
@@ -185,12 +229,11 @@ int desklink_point_overlay_main(const char *display_name) {
|
|
|
185
229
|
Pixmap mask = XCreatePixmap(d, root, width, height, 1);
|
|
186
230
|
GC shape = XCreateGC(d, mask, 0, NULL), ink = XCreateGC(d, window, 0, NULL);
|
|
187
231
|
XFontStruct *font = XLoadQueryFont(d, "fixed");
|
|
188
|
-
if (font) XSetFont(d, ink, font->fid);
|
|
189
|
-
XSetLineAttributes(d, shape, 4, LineSolid, CapRound, JoinRound);
|
|
190
232
|
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
|
|
191
233
|
puts("READY"); fflush(stdout);
|
|
192
234
|
char pending[512]; size_t used = 0;
|
|
193
|
-
double deadline = 0;
|
|
235
|
+
double deadline = 0, started = 0, drawn = 0;
|
|
236
|
+
int x = 0, y = 0, badge_x = 0, badge_y = 0, badge_w = 0;
|
|
194
237
|
for (;;) {
|
|
195
238
|
char bytes[512]; ssize_t n = read(STDIN_FILENO, bytes, sizeof(bytes));
|
|
196
239
|
if (n == 0) break;
|
|
@@ -200,35 +243,41 @@ int desklink_point_overlay_main(const char *display_name) {
|
|
|
200
243
|
double px, py; unsigned timeout; int offset = 0;
|
|
201
244
|
if (sscanf(pending, "P %lf %lf %u %n", &px, &py, &timeout, &offset) != 3 || !offset) continue;
|
|
202
245
|
const char *label = pending + offset;
|
|
203
|
-
int
|
|
204
|
-
|
|
246
|
+
int len = (int)strlen(label);
|
|
247
|
+
x = (int)px; y = (int)py;
|
|
248
|
+
int text_w = font ? 2*XTextWidth(font, label, len) : 12*len;
|
|
249
|
+
badge_w = len ? text_w + 24 : 0;
|
|
205
250
|
if (badge_w > width) badge_w = width;
|
|
206
|
-
|
|
251
|
+
badge_x = x + POINT_RING + 30; badge_y = y - POINT_BADGE_H/2;
|
|
207
252
|
if (badge_x + badge_w > width) badge_x = width - badge_w;
|
|
208
253
|
if (badge_y < 0) badge_y = 0;
|
|
209
|
-
if (badge_y +
|
|
254
|
+
if (badge_y + POINT_BADGE_H > height) badge_y = height - POINT_BADGE_H;
|
|
210
255
|
XUnmapWindow(d, window);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
XDrawArc(d, mask, shape, x-18, y-18, 36, 36, 0, 360*64);
|
|
214
|
-
XFillArc(d, mask, shape, x-3, y-3, 6, 6, 0, 360*64);
|
|
215
|
-
if (len) XFillRectangle(d, mask, shape, badge_x, badge_y, badge_w, 28);
|
|
216
|
-
XShapeCombineMask(d, window, ShapeBounding, 0, 0, mask, ShapeSet);
|
|
256
|
+
started = now();
|
|
257
|
+
point_shape(d, window, mask, shape, x, y, badge_x, badge_y, badge_w, 0);
|
|
217
258
|
XMapRaised(d, window);
|
|
218
259
|
XClearWindow(d, window);
|
|
219
260
|
if (len) {
|
|
220
261
|
XSetForeground(d, ink, 0x123047);
|
|
221
|
-
XFillRectangle(d, window, ink, badge_x, badge_y, badge_w,
|
|
262
|
+
XFillRectangle(d, window, ink, badge_x, badge_y, badge_w, POINT_BADGE_H);
|
|
222
263
|
XSetForeground(d, ink, 0xffffff);
|
|
223
|
-
|
|
264
|
+
if (font) point_text(d, window, ink, font, label, len, badge_x+12,
|
|
265
|
+
badge_y + (POINT_BADGE_H - 2*(font->ascent+font->descent))/2);
|
|
224
266
|
}
|
|
225
267
|
XSync(d, False);
|
|
226
|
-
|
|
268
|
+
drawn = now();
|
|
269
|
+
deadline = drawn + timeout/1000.0;
|
|
270
|
+
}
|
|
271
|
+
double t = now();
|
|
272
|
+
if (deadline && t >= deadline) { XUnmapWindow(d, window); XFlush(d); deadline = 0; }
|
|
273
|
+
else if (deadline && t - drawn >= POINT_FRAME_SECONDS) {
|
|
274
|
+
point_shape(d, window, mask, shape, x, y, badge_x, badge_y, badge_w, t - started);
|
|
275
|
+
XFlush(d); drawn = t;
|
|
227
276
|
}
|
|
228
|
-
if (deadline && now() >= deadline) { XUnmapWindow(d, window); XFlush(d); deadline = 0; }
|
|
229
277
|
usleep(5000);
|
|
230
278
|
}
|
|
231
279
|
XDestroyWindow(d, window); XFreeGC(d, shape); XFreeGC(d, ink);
|
|
232
280
|
XFreePixmap(d, mask); if (font) XFreeFont(d, font); XCloseDisplay(d);
|
|
233
281
|
return 0;
|
|
234
282
|
}
|
|
283
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desklink/host",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"ws": "8.21.3"
|
|
39
39
|
},
|
|
40
40
|
"optionalDependencies": {
|
|
41
|
-
"@desklink/host-linux-x64-gnu": "0.5.
|
|
42
|
-
"@desklink/host-darwin-arm64": "0.5.
|
|
41
|
+
"@desklink/host-linux-x64-gnu": "0.5.11",
|
|
42
|
+
"@desklink/host-darwin-arm64": "0.5.11"
|
|
43
43
|
}
|
|
44
44
|
}
|