@napolab/texture-bridge-renderer 0.4.1 → 0.6.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/dist/index.cjs +148 -15
- package/dist/index.d.cts +53 -2
- package/dist/index.d.mts +53 -2
- package/dist/index.mjs +147 -16
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -121,6 +121,11 @@ var FpsCounter = class {
|
|
|
121
121
|
this.count = 0;
|
|
122
122
|
this.lastTime = Date.now();
|
|
123
123
|
}
|
|
124
|
+
/** Reset the counter, discarding any accumulated frames and time. */
|
|
125
|
+
reset() {
|
|
126
|
+
this.count = 0;
|
|
127
|
+
this.lastTime = Date.now();
|
|
128
|
+
}
|
|
124
129
|
/** Call on every frame. Returns FPS when 1 second has elapsed, otherwise null. */
|
|
125
130
|
tick() {
|
|
126
131
|
this.count++;
|
|
@@ -155,6 +160,24 @@ var TextureBridgeImpl = class extends events.EventEmitter {
|
|
|
155
160
|
get isDisposed() {
|
|
156
161
|
return this._disposed;
|
|
157
162
|
}
|
|
163
|
+
/** Handle a paint event from the offscreen BrowserWindow. */
|
|
164
|
+
handlePaint(event) {
|
|
165
|
+
const texture = event.texture;
|
|
166
|
+
if (!texture?.textureInfo) return;
|
|
167
|
+
try {
|
|
168
|
+
if (this._disposed) return;
|
|
169
|
+
(0, _napolab_texture_bridge_core.sendTextureFromPaintEvent)(this.sender, texture.textureInfo);
|
|
170
|
+
this.previewManager?.sendFrame(texture);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
173
|
+
this.emit("error", error);
|
|
174
|
+
} finally {
|
|
175
|
+
texture.release?.();
|
|
176
|
+
}
|
|
177
|
+
if (this._disposed) return;
|
|
178
|
+
const fps = this.fpsCounter.tick();
|
|
179
|
+
if (fps !== null) this.emit("fps", fps);
|
|
180
|
+
}
|
|
158
181
|
openPreview() {
|
|
159
182
|
if (this._disposed) return;
|
|
160
183
|
if (!this.previewManager) this.previewManager = new PreviewManager(this.options.width, this.options.height, this.options.preview);
|
|
@@ -165,6 +188,7 @@ var TextureBridgeImpl = class extends events.EventEmitter {
|
|
|
165
188
|
}
|
|
166
189
|
resize(width, height) {
|
|
167
190
|
if (this._disposed) return;
|
|
191
|
+
const prevOpts = this.options;
|
|
168
192
|
this.options = {
|
|
169
193
|
...this.options,
|
|
170
194
|
width,
|
|
@@ -172,7 +196,14 @@ var TextureBridgeImpl = class extends events.EventEmitter {
|
|
|
172
196
|
};
|
|
173
197
|
this._renderWindow.setSize(width, height);
|
|
174
198
|
this.sender.stop();
|
|
175
|
-
|
|
199
|
+
try {
|
|
200
|
+
this.sender = new _napolab_texture_bridge_core.TextureSender(this.options.name, width, height);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
this.options = prevOpts;
|
|
203
|
+
this._renderWindow.setSize(prevOpts.width, prevOpts.height);
|
|
204
|
+
this.sender = new _napolab_texture_bridge_core.TextureSender(prevOpts.name, prevOpts.width, prevOpts.height);
|
|
205
|
+
throw err;
|
|
206
|
+
}
|
|
176
207
|
this.previewManager?.updateSize(width, height);
|
|
177
208
|
this.emit("resize", width, height);
|
|
178
209
|
}
|
|
@@ -186,6 +217,9 @@ var TextureBridgeImpl = class extends events.EventEmitter {
|
|
|
186
217
|
this.emit("disposed");
|
|
187
218
|
this.removeAllListeners();
|
|
188
219
|
}
|
|
220
|
+
[Symbol.dispose]() {
|
|
221
|
+
this.dispose();
|
|
222
|
+
}
|
|
189
223
|
};
|
|
190
224
|
/**
|
|
191
225
|
* Create a fully-wired texture bridge: offscreen window, native sender,
|
|
@@ -215,19 +249,7 @@ async function createTextureBridge(options) {
|
|
|
215
249
|
}
|
|
216
250
|
const bridge = new TextureBridgeImpl(renderWindow, sender, previewManager, options);
|
|
217
251
|
renderWindow.webContents.on("paint", (event) => {
|
|
218
|
-
|
|
219
|
-
if (!texture?.textureInfo) return;
|
|
220
|
-
try {
|
|
221
|
-
(0, _napolab_texture_bridge_core.sendTextureFromPaintEvent)(bridge.sender, texture.textureInfo);
|
|
222
|
-
bridge.previewManager?.sendFrame(texture);
|
|
223
|
-
} catch (err) {
|
|
224
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
225
|
-
bridge.emit("error", error);
|
|
226
|
-
} finally {
|
|
227
|
-
texture.release?.();
|
|
228
|
-
}
|
|
229
|
-
const fps = bridge.fpsCounter.tick();
|
|
230
|
-
if (fps !== null) bridge.emit("fps", fps);
|
|
252
|
+
bridge.handlePaint(event);
|
|
231
253
|
});
|
|
232
254
|
renderWindow.webContents.setFrameRate(frameRate);
|
|
233
255
|
if (rendererUrl.startsWith("http://") || rendererUrl.startsWith("https://")) await renderWindow.loadURL(rendererUrl);
|
|
@@ -238,4 +260,115 @@ async function createTextureBridge(options) {
|
|
|
238
260
|
}
|
|
239
261
|
|
|
240
262
|
//#endregion
|
|
241
|
-
|
|
263
|
+
//#region src/receiver.ts
|
|
264
|
+
var TextureReceiverBridgeImpl = class extends events.EventEmitter {
|
|
265
|
+
constructor(receiver, pollIntervalMs) {
|
|
266
|
+
super();
|
|
267
|
+
this.fpsCounter = new FpsCounter();
|
|
268
|
+
this._disposed = false;
|
|
269
|
+
this._timer = null;
|
|
270
|
+
this.receiver = receiver;
|
|
271
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
272
|
+
}
|
|
273
|
+
get isDisposed() {
|
|
274
|
+
return this._disposed;
|
|
275
|
+
}
|
|
276
|
+
start() {
|
|
277
|
+
if (this._disposed || this._timer) return;
|
|
278
|
+
this.fpsCounter.reset();
|
|
279
|
+
this._timer = setInterval(() => this._poll(), this.pollIntervalMs);
|
|
280
|
+
}
|
|
281
|
+
stop() {
|
|
282
|
+
if (this._timer) {
|
|
283
|
+
clearInterval(this._timer);
|
|
284
|
+
this._timer = null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
dispose() {
|
|
288
|
+
if (this._disposed) return;
|
|
289
|
+
this._disposed = true;
|
|
290
|
+
this.stop();
|
|
291
|
+
this.receiver.stop();
|
|
292
|
+
this.emit("disposed");
|
|
293
|
+
this.removeAllListeners();
|
|
294
|
+
}
|
|
295
|
+
[Symbol.dispose]() {
|
|
296
|
+
this.dispose();
|
|
297
|
+
}
|
|
298
|
+
_poll() {
|
|
299
|
+
if (this._disposed) return;
|
|
300
|
+
try {
|
|
301
|
+
if (!this.receiver.hasNewFrame()) return;
|
|
302
|
+
const frame = this.receiver.receiveFrame();
|
|
303
|
+
if (!frame) return;
|
|
304
|
+
this.emit("frame", frame);
|
|
305
|
+
const fps = this.fpsCounter.tick();
|
|
306
|
+
if (fps !== null) this.emit("fps", fps);
|
|
307
|
+
} catch (err) {
|
|
308
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
309
|
+
this.emit("error", error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
function createTextureReceiver(options) {
|
|
314
|
+
const { senderName, appName, serverUuid, pollIntervalMs = 16 } = options;
|
|
315
|
+
return new TextureReceiverBridgeImpl(new _napolab_texture_bridge_core.TextureReceiver(senderName, appName, serverUuid), pollIntervalMs);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/discovery.ts
|
|
320
|
+
var SenderDiscovery = class extends events.EventEmitter {
|
|
321
|
+
constructor(..._args) {
|
|
322
|
+
super(..._args);
|
|
323
|
+
this._senders = [];
|
|
324
|
+
this._timer = null;
|
|
325
|
+
this._disposed = false;
|
|
326
|
+
}
|
|
327
|
+
get isDisposed() {
|
|
328
|
+
return this._disposed;
|
|
329
|
+
}
|
|
330
|
+
start(intervalMs = 1e3) {
|
|
331
|
+
if (this._disposed || this._timer) return;
|
|
332
|
+
this._timer = setInterval(() => this._refresh(), intervalMs);
|
|
333
|
+
}
|
|
334
|
+
stop() {
|
|
335
|
+
if (this._timer) {
|
|
336
|
+
clearInterval(this._timer);
|
|
337
|
+
this._timer = null;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
getSenders() {
|
|
341
|
+
return [...this._senders];
|
|
342
|
+
}
|
|
343
|
+
dispose() {
|
|
344
|
+
if (this._disposed) return;
|
|
345
|
+
this._disposed = true;
|
|
346
|
+
this.stop();
|
|
347
|
+
this.removeAllListeners();
|
|
348
|
+
}
|
|
349
|
+
_refresh() {
|
|
350
|
+
if (this._disposed) return;
|
|
351
|
+
try {
|
|
352
|
+
const current = (0, _napolab_texture_bridge_core.listSenders)();
|
|
353
|
+
const prev = this._senders;
|
|
354
|
+
const added = current.filter((c) => !prev.some((p) => this._isSame(c, p)));
|
|
355
|
+
const removed = prev.filter((p) => !current.some((c) => this._isSame(c, p)));
|
|
356
|
+
this._senders = current;
|
|
357
|
+
if (added.length > 0) this.emit("added", added);
|
|
358
|
+
if (removed.length > 0) this.emit("removed", removed);
|
|
359
|
+
if (added.length > 0 || removed.length > 0) this.emit("updated", current);
|
|
360
|
+
} catch (err) {
|
|
361
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
362
|
+
this.emit("error", error);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
_isSame(a, b) {
|
|
366
|
+
if (a.uuid && b.uuid) return a.uuid === b.uuid;
|
|
367
|
+
return a.name === b.name && a.appName === b.appName;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
//#endregion
|
|
372
|
+
exports.SenderDiscovery = SenderDiscovery;
|
|
373
|
+
exports.createTextureBridge = createTextureBridge;
|
|
374
|
+
exports.createTextureReceiver = createTextureReceiver;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BrowserWindow } from "electron";
|
|
2
|
+
import { ReceivedFrame, SenderInfo } from "@napolab/texture-bridge-core";
|
|
3
|
+
import { EventEmitter } from "events";
|
|
2
4
|
|
|
3
5
|
//#region src/types.d.ts
|
|
4
6
|
/** Options for the preview window */
|
|
@@ -50,8 +52,10 @@ interface TextureBridge {
|
|
|
50
52
|
readonly previewWindow: BrowserWindow | null;
|
|
51
53
|
/** Whether the bridge has been disposed */
|
|
52
54
|
readonly isDisposed: boolean;
|
|
53
|
-
/** Tear down all resources */
|
|
55
|
+
/** Tear down all resources. Terminal operation — the bridge cannot be reused afterward. */
|
|
54
56
|
dispose(): void;
|
|
57
|
+
/** Alias for dispose(), enabling `using bridge = await createTextureBridge(...)` */
|
|
58
|
+
[Symbol.dispose](): void;
|
|
55
59
|
}
|
|
56
60
|
//#endregion
|
|
57
61
|
//#region src/bridge.d.ts
|
|
@@ -63,4 +67,51 @@ interface TextureBridge {
|
|
|
63
67
|
*/
|
|
64
68
|
declare function createTextureBridge(options: TextureBridgeOptions): Promise<TextureBridge>;
|
|
65
69
|
//#endregion
|
|
66
|
-
|
|
70
|
+
//#region src/receiver.d.ts
|
|
71
|
+
interface TextureReceiverBridgeOptions {
|
|
72
|
+
senderName: string;
|
|
73
|
+
appName?: string;
|
|
74
|
+
serverUuid?: string;
|
|
75
|
+
pollIntervalMs?: number;
|
|
76
|
+
}
|
|
77
|
+
interface ReceiverBridgeEvents {
|
|
78
|
+
frame: [frame: ReceivedFrame];
|
|
79
|
+
fps: [fps: number];
|
|
80
|
+
error: [error: Error];
|
|
81
|
+
disposed: [];
|
|
82
|
+
}
|
|
83
|
+
interface TextureReceiverBridge {
|
|
84
|
+
on<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
|
|
85
|
+
off<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
|
|
86
|
+
once<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
|
|
87
|
+
start(): void;
|
|
88
|
+
stop(): void;
|
|
89
|
+
/** Tear down all resources. Terminal operation — the bridge cannot be reused afterward. */
|
|
90
|
+
dispose(): void;
|
|
91
|
+
/** Alias for dispose(), enabling `using receiver = createTextureReceiver(...)` */
|
|
92
|
+
[Symbol.dispose](): void;
|
|
93
|
+
readonly isDisposed: boolean;
|
|
94
|
+
}
|
|
95
|
+
declare function createTextureReceiver(options: TextureReceiverBridgeOptions): TextureReceiverBridge;
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/discovery.d.ts
|
|
98
|
+
interface SenderDiscoveryEvents {
|
|
99
|
+
updated: [senders: SenderInfo[]];
|
|
100
|
+
added: [senders: SenderInfo[]];
|
|
101
|
+
removed: [senders: SenderInfo[]];
|
|
102
|
+
error: [error: Error];
|
|
103
|
+
}
|
|
104
|
+
declare class SenderDiscovery extends EventEmitter {
|
|
105
|
+
private _senders;
|
|
106
|
+
private _timer;
|
|
107
|
+
private _disposed;
|
|
108
|
+
get isDisposed(): boolean;
|
|
109
|
+
start(intervalMs?: number): void;
|
|
110
|
+
stop(): void;
|
|
111
|
+
getSenders(): SenderInfo[];
|
|
112
|
+
dispose(): void;
|
|
113
|
+
private _refresh;
|
|
114
|
+
private _isSame;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { type BridgeEvents, type PreviewOptions, type ReceiverBridgeEvents, SenderDiscovery, type SenderDiscoveryEvents, type TextureBridge, type TextureBridgeOptions, type TextureReceiverBridge, type TextureReceiverBridgeOptions, createTextureBridge, createTextureReceiver };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
1
2
|
import { BrowserWindow } from "electron";
|
|
3
|
+
import { ReceivedFrame, SenderInfo } from "@napolab/texture-bridge-core";
|
|
2
4
|
|
|
3
5
|
//#region src/types.d.ts
|
|
4
6
|
/** Options for the preview window */
|
|
@@ -50,8 +52,10 @@ interface TextureBridge {
|
|
|
50
52
|
readonly previewWindow: BrowserWindow | null;
|
|
51
53
|
/** Whether the bridge has been disposed */
|
|
52
54
|
readonly isDisposed: boolean;
|
|
53
|
-
/** Tear down all resources */
|
|
55
|
+
/** Tear down all resources. Terminal operation — the bridge cannot be reused afterward. */
|
|
54
56
|
dispose(): void;
|
|
57
|
+
/** Alias for dispose(), enabling `using bridge = await createTextureBridge(...)` */
|
|
58
|
+
[Symbol.dispose](): void;
|
|
55
59
|
}
|
|
56
60
|
//#endregion
|
|
57
61
|
//#region src/bridge.d.ts
|
|
@@ -63,4 +67,51 @@ interface TextureBridge {
|
|
|
63
67
|
*/
|
|
64
68
|
declare function createTextureBridge(options: TextureBridgeOptions): Promise<TextureBridge>;
|
|
65
69
|
//#endregion
|
|
66
|
-
|
|
70
|
+
//#region src/receiver.d.ts
|
|
71
|
+
interface TextureReceiverBridgeOptions {
|
|
72
|
+
senderName: string;
|
|
73
|
+
appName?: string;
|
|
74
|
+
serverUuid?: string;
|
|
75
|
+
pollIntervalMs?: number;
|
|
76
|
+
}
|
|
77
|
+
interface ReceiverBridgeEvents {
|
|
78
|
+
frame: [frame: ReceivedFrame];
|
|
79
|
+
fps: [fps: number];
|
|
80
|
+
error: [error: Error];
|
|
81
|
+
disposed: [];
|
|
82
|
+
}
|
|
83
|
+
interface TextureReceiverBridge {
|
|
84
|
+
on<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
|
|
85
|
+
off<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
|
|
86
|
+
once<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
|
|
87
|
+
start(): void;
|
|
88
|
+
stop(): void;
|
|
89
|
+
/** Tear down all resources. Terminal operation — the bridge cannot be reused afterward. */
|
|
90
|
+
dispose(): void;
|
|
91
|
+
/** Alias for dispose(), enabling `using receiver = createTextureReceiver(...)` */
|
|
92
|
+
[Symbol.dispose](): void;
|
|
93
|
+
readonly isDisposed: boolean;
|
|
94
|
+
}
|
|
95
|
+
declare function createTextureReceiver(options: TextureReceiverBridgeOptions): TextureReceiverBridge;
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/discovery.d.ts
|
|
98
|
+
interface SenderDiscoveryEvents {
|
|
99
|
+
updated: [senders: SenderInfo[]];
|
|
100
|
+
added: [senders: SenderInfo[]];
|
|
101
|
+
removed: [senders: SenderInfo[]];
|
|
102
|
+
error: [error: Error];
|
|
103
|
+
}
|
|
104
|
+
declare class SenderDiscovery extends EventEmitter {
|
|
105
|
+
private _senders;
|
|
106
|
+
private _timer;
|
|
107
|
+
private _disposed;
|
|
108
|
+
get isDisposed(): boolean;
|
|
109
|
+
start(intervalMs?: number): void;
|
|
110
|
+
stop(): void;
|
|
111
|
+
getSenders(): SenderInfo[];
|
|
112
|
+
dispose(): void;
|
|
113
|
+
private _refresh;
|
|
114
|
+
private _isSame;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { type BridgeEvents, type PreviewOptions, type ReceiverBridgeEvents, SenderDiscovery, type SenderDiscoveryEvents, type TextureBridge, type TextureBridgeOptions, type TextureReceiverBridge, type TextureReceiverBridgeOptions, createTextureBridge, createTextureReceiver };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
import { BrowserWindow, app, ipcMain, sharedTexture } from "electron";
|
|
3
|
-
import { TextureSender, sendTextureFromPaintEvent } from "@napolab/texture-bridge-core";
|
|
3
|
+
import { TextureReceiver, TextureSender, listSenders, sendTextureFromPaintEvent } from "@napolab/texture-bridge-core";
|
|
4
4
|
import path from "path";
|
|
5
5
|
|
|
6
6
|
//#region src/preview-manager.ts
|
|
@@ -92,6 +92,11 @@ var FpsCounter = class {
|
|
|
92
92
|
this.count = 0;
|
|
93
93
|
this.lastTime = Date.now();
|
|
94
94
|
}
|
|
95
|
+
/** Reset the counter, discarding any accumulated frames and time. */
|
|
96
|
+
reset() {
|
|
97
|
+
this.count = 0;
|
|
98
|
+
this.lastTime = Date.now();
|
|
99
|
+
}
|
|
95
100
|
/** Call on every frame. Returns FPS when 1 second has elapsed, otherwise null. */
|
|
96
101
|
tick() {
|
|
97
102
|
this.count++;
|
|
@@ -126,6 +131,24 @@ var TextureBridgeImpl = class extends EventEmitter {
|
|
|
126
131
|
get isDisposed() {
|
|
127
132
|
return this._disposed;
|
|
128
133
|
}
|
|
134
|
+
/** Handle a paint event from the offscreen BrowserWindow. */
|
|
135
|
+
handlePaint(event) {
|
|
136
|
+
const texture = event.texture;
|
|
137
|
+
if (!texture?.textureInfo) return;
|
|
138
|
+
try {
|
|
139
|
+
if (this._disposed) return;
|
|
140
|
+
sendTextureFromPaintEvent(this.sender, texture.textureInfo);
|
|
141
|
+
this.previewManager?.sendFrame(texture);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
144
|
+
this.emit("error", error);
|
|
145
|
+
} finally {
|
|
146
|
+
texture.release?.();
|
|
147
|
+
}
|
|
148
|
+
if (this._disposed) return;
|
|
149
|
+
const fps = this.fpsCounter.tick();
|
|
150
|
+
if (fps !== null) this.emit("fps", fps);
|
|
151
|
+
}
|
|
129
152
|
openPreview() {
|
|
130
153
|
if (this._disposed) return;
|
|
131
154
|
if (!this.previewManager) this.previewManager = new PreviewManager(this.options.width, this.options.height, this.options.preview);
|
|
@@ -136,6 +159,7 @@ var TextureBridgeImpl = class extends EventEmitter {
|
|
|
136
159
|
}
|
|
137
160
|
resize(width, height) {
|
|
138
161
|
if (this._disposed) return;
|
|
162
|
+
const prevOpts = this.options;
|
|
139
163
|
this.options = {
|
|
140
164
|
...this.options,
|
|
141
165
|
width,
|
|
@@ -143,7 +167,14 @@ var TextureBridgeImpl = class extends EventEmitter {
|
|
|
143
167
|
};
|
|
144
168
|
this._renderWindow.setSize(width, height);
|
|
145
169
|
this.sender.stop();
|
|
146
|
-
|
|
170
|
+
try {
|
|
171
|
+
this.sender = new TextureSender(this.options.name, width, height);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
this.options = prevOpts;
|
|
174
|
+
this._renderWindow.setSize(prevOpts.width, prevOpts.height);
|
|
175
|
+
this.sender = new TextureSender(prevOpts.name, prevOpts.width, prevOpts.height);
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
147
178
|
this.previewManager?.updateSize(width, height);
|
|
148
179
|
this.emit("resize", width, height);
|
|
149
180
|
}
|
|
@@ -157,6 +188,9 @@ var TextureBridgeImpl = class extends EventEmitter {
|
|
|
157
188
|
this.emit("disposed");
|
|
158
189
|
this.removeAllListeners();
|
|
159
190
|
}
|
|
191
|
+
[Symbol.dispose]() {
|
|
192
|
+
this.dispose();
|
|
193
|
+
}
|
|
160
194
|
};
|
|
161
195
|
/**
|
|
162
196
|
* Create a fully-wired texture bridge: offscreen window, native sender,
|
|
@@ -186,19 +220,7 @@ async function createTextureBridge(options) {
|
|
|
186
220
|
}
|
|
187
221
|
const bridge = new TextureBridgeImpl(renderWindow, sender, previewManager, options);
|
|
188
222
|
renderWindow.webContents.on("paint", (event) => {
|
|
189
|
-
|
|
190
|
-
if (!texture?.textureInfo) return;
|
|
191
|
-
try {
|
|
192
|
-
sendTextureFromPaintEvent(bridge.sender, texture.textureInfo);
|
|
193
|
-
bridge.previewManager?.sendFrame(texture);
|
|
194
|
-
} catch (err) {
|
|
195
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
196
|
-
bridge.emit("error", error);
|
|
197
|
-
} finally {
|
|
198
|
-
texture.release?.();
|
|
199
|
-
}
|
|
200
|
-
const fps = bridge.fpsCounter.tick();
|
|
201
|
-
if (fps !== null) bridge.emit("fps", fps);
|
|
223
|
+
bridge.handlePaint(event);
|
|
202
224
|
});
|
|
203
225
|
renderWindow.webContents.setFrameRate(frameRate);
|
|
204
226
|
if (rendererUrl.startsWith("http://") || rendererUrl.startsWith("https://")) await renderWindow.loadURL(rendererUrl);
|
|
@@ -209,4 +231,113 @@ async function createTextureBridge(options) {
|
|
|
209
231
|
}
|
|
210
232
|
|
|
211
233
|
//#endregion
|
|
212
|
-
|
|
234
|
+
//#region src/receiver.ts
|
|
235
|
+
var TextureReceiverBridgeImpl = class extends EventEmitter {
|
|
236
|
+
constructor(receiver, pollIntervalMs) {
|
|
237
|
+
super();
|
|
238
|
+
this.fpsCounter = new FpsCounter();
|
|
239
|
+
this._disposed = false;
|
|
240
|
+
this._timer = null;
|
|
241
|
+
this.receiver = receiver;
|
|
242
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
243
|
+
}
|
|
244
|
+
get isDisposed() {
|
|
245
|
+
return this._disposed;
|
|
246
|
+
}
|
|
247
|
+
start() {
|
|
248
|
+
if (this._disposed || this._timer) return;
|
|
249
|
+
this.fpsCounter.reset();
|
|
250
|
+
this._timer = setInterval(() => this._poll(), this.pollIntervalMs);
|
|
251
|
+
}
|
|
252
|
+
stop() {
|
|
253
|
+
if (this._timer) {
|
|
254
|
+
clearInterval(this._timer);
|
|
255
|
+
this._timer = null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
dispose() {
|
|
259
|
+
if (this._disposed) return;
|
|
260
|
+
this._disposed = true;
|
|
261
|
+
this.stop();
|
|
262
|
+
this.receiver.stop();
|
|
263
|
+
this.emit("disposed");
|
|
264
|
+
this.removeAllListeners();
|
|
265
|
+
}
|
|
266
|
+
[Symbol.dispose]() {
|
|
267
|
+
this.dispose();
|
|
268
|
+
}
|
|
269
|
+
_poll() {
|
|
270
|
+
if (this._disposed) return;
|
|
271
|
+
try {
|
|
272
|
+
if (!this.receiver.hasNewFrame()) return;
|
|
273
|
+
const frame = this.receiver.receiveFrame();
|
|
274
|
+
if (!frame) return;
|
|
275
|
+
this.emit("frame", frame);
|
|
276
|
+
const fps = this.fpsCounter.tick();
|
|
277
|
+
if (fps !== null) this.emit("fps", fps);
|
|
278
|
+
} catch (err) {
|
|
279
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
280
|
+
this.emit("error", error);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
function createTextureReceiver(options) {
|
|
285
|
+
const { senderName, appName, serverUuid, pollIntervalMs = 16 } = options;
|
|
286
|
+
return new TextureReceiverBridgeImpl(new TextureReceiver(senderName, appName, serverUuid), pollIntervalMs);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region src/discovery.ts
|
|
291
|
+
var SenderDiscovery = class extends EventEmitter {
|
|
292
|
+
constructor(..._args) {
|
|
293
|
+
super(..._args);
|
|
294
|
+
this._senders = [];
|
|
295
|
+
this._timer = null;
|
|
296
|
+
this._disposed = false;
|
|
297
|
+
}
|
|
298
|
+
get isDisposed() {
|
|
299
|
+
return this._disposed;
|
|
300
|
+
}
|
|
301
|
+
start(intervalMs = 1e3) {
|
|
302
|
+
if (this._disposed || this._timer) return;
|
|
303
|
+
this._timer = setInterval(() => this._refresh(), intervalMs);
|
|
304
|
+
}
|
|
305
|
+
stop() {
|
|
306
|
+
if (this._timer) {
|
|
307
|
+
clearInterval(this._timer);
|
|
308
|
+
this._timer = null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
getSenders() {
|
|
312
|
+
return [...this._senders];
|
|
313
|
+
}
|
|
314
|
+
dispose() {
|
|
315
|
+
if (this._disposed) return;
|
|
316
|
+
this._disposed = true;
|
|
317
|
+
this.stop();
|
|
318
|
+
this.removeAllListeners();
|
|
319
|
+
}
|
|
320
|
+
_refresh() {
|
|
321
|
+
if (this._disposed) return;
|
|
322
|
+
try {
|
|
323
|
+
const current = listSenders();
|
|
324
|
+
const prev = this._senders;
|
|
325
|
+
const added = current.filter((c) => !prev.some((p) => this._isSame(c, p)));
|
|
326
|
+
const removed = prev.filter((p) => !current.some((c) => this._isSame(c, p)));
|
|
327
|
+
this._senders = current;
|
|
328
|
+
if (added.length > 0) this.emit("added", added);
|
|
329
|
+
if (removed.length > 0) this.emit("removed", removed);
|
|
330
|
+
if (added.length > 0 || removed.length > 0) this.emit("updated", current);
|
|
331
|
+
} catch (err) {
|
|
332
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
333
|
+
this.emit("error", error);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
_isSame(a, b) {
|
|
337
|
+
if (a.uuid && b.uuid) return a.uuid === b.uuid;
|
|
338
|
+
return a.name === b.name && a.appName === b.appName;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
//#endregion
|
|
343
|
+
export { SenderDiscovery, createTextureBridge, createTextureReceiver };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@napolab/texture-bridge-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "High-level factory API for GPU texture bridge (BrowserWindow + Preview + Sender)",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@napolab/texture-bridge-core": "0.
|
|
28
|
+
"@napolab/texture-bridge-core": "0.6.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"electron": ">=40.0.0"
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
],
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsdown src/index.ts src/client/index.ts src/client/worker-protocol.ts --format cjs,esm --dts && cp -r src/assets dist/assets",
|
|
56
|
+
"test": "vitest run",
|
|
56
57
|
"typecheck": "tsgo --noEmit"
|
|
57
58
|
}
|
|
58
59
|
}
|