(
url: string,
onmessage: (data: FrameData) => void,
onRequestFrame?: () => void,
options: ImageDataWSOptions = {},
)
| 257 | | RequestFrameMessage; |
| 258 | |
| 259 | export function createImageDataWS( |
| 260 | url: string, |
| 261 | onmessage: (data: FrameData) => void, |
| 262 | onRequestFrame?: () => void, |
| 263 | options: ImageDataWSOptions = {}, |
| 264 | ): [ |
| 265 | Omit<WebSocket, "onmessage">, |
| 266 | () => boolean, |
| 267 | () => boolean, |
| 268 | CanvasControls, |
| 269 | ] { |
| 270 | const [isConnected, setIsConnected] = createSignal(false); |
| 271 | const [isWorkerReady, setIsWorkerReady] = createSignal(false); |
| 272 | const ws = createWS(url); |
| 273 | |
| 274 | // The frame worker (and its SharedArrayBuffer) only exists for the |
| 275 | // OffscreenCanvas path; the direct-canvas consumers never need it, so it |
| 276 | // is created lazily to avoid a worker + SAB reservation per window. |
| 277 | let worker: Worker | null = null; |
| 278 | let workerCanvasMode = false; |
| 279 | let pendingFrame: ArrayBuffer | null = null; |
| 280 | let isProcessing = false; |
| 281 | let isProcessingSharedFrame = false; |
| 282 | let nextFrame: ArrayBuffer | null = null; |
| 283 | |
| 284 | let producer: Producer | null = null; |
| 285 | |
| 286 | function ensureWorker(): Worker { |
| 287 | if (worker) return worker; |
| 288 | worker = new FrameWorker(); |
| 289 | worker.onmessage = handleWorkerMessage; |
| 290 | if (SAB_SUPPORTED) { |
| 291 | try { |
| 292 | const init = createSharedFrameBuffer(FRAME_BUFFER_CONFIG); |
| 293 | producer = createProducer(init); |
| 294 | worker.postMessage({ |
| 295 | type: "init-shared-buffer", |
| 296 | buffer: init.buffer, |
| 297 | }); |
| 298 | } catch (e) { |
| 299 | console.error( |
| 300 | "[socket] SharedArrayBuffer allocation failed, falling back to non-SAB mode:", |
| 301 | e instanceof Error ? e.message : e, |
| 302 | ); |
| 303 | producer = null; |
| 304 | } |
| 305 | } |
| 306 | return worker; |
| 307 | } |
| 308 | |
| 309 | const [hasRenderedFrame, setHasRenderedFrame] = createSignal(false); |
| 310 | let isCleanedUp = false; |
| 311 | |
| 312 | let directCanvas: HTMLCanvasElement | null = null; |
| 313 | let directCtx: CanvasRenderingContext2D | null = null; |
| 314 | let strideWorker: Worker | null = null; |
| 315 | |
| 316 | let cachedDirectImageData: ImageData | null = null; |
no test coverage detected