From ff54140d626502149db91baa422836e7e7eb0a56 Mon Sep 17 00:00:00 2001 From: Allie H Date: Wed, 11 Mar 2026 09:19:47 -0400 Subject: [PATCH] Further optimization passes --- electron/dist/main.js | 211 ++++--- electron/native/wo_dmabuf.cc | 221 ++++++- electron/src/damage-buffer.ts | 74 ++- electron/src/main.ts | 199 +++++-- electron/src/preload.ts | 18 + electron/types/package.json | 2 +- electron/types/types.d.ts | 36 ++ src/backend.rs | 3 + src/cursor.rs | 2 +- src/dmabuf.rs | 4 +- src/electron.rs | 55 +- src/handlers/input.rs | 2 +- src/handlers/screencopy.rs | 3 +- src/handlers/xdg_shell.rs | 3 +- src/handlers/xwayland.rs | 2 + src/main.rs | 4 +- src/nested.rs | 19 +- src/state.rs | 2 +- whatever.txt | 1020 ++++++++++++++------------------- 19 files changed, 1099 insertions(+), 781 deletions(-) diff --git a/electron/dist/main.js b/electron/dist/main.js index b3bd6c5..e1084f2 100644 --- a/electron/dist/main.js +++ b/electron/dist/main.js @@ -88,14 +88,14 @@ class DamageBuffer { } applyFullFrame(frame, frameStride = this.width * 4) { if (frameStride === this.stride) { - Buffer.from(frame).copy(this.frame, 0, 0, this.frame.length); + this.frame.set(new Uint8Array(frame.buffer, frame.byteOffset, this.frame.length)); return; } const rowBytes = Math.min(frameStride, this.stride); for (let y = 0;y < this.height; y += 1) { const srcStart = y * frameStride; const dstStart = y * this.stride; - Buffer.from(frame).copy(this.frame, dstStart, srcStart, srcStart + rowBytes); + this.frame.set(new Uint8Array(frame.buffer, frame.byteOffset + srcStart, rowBytes), dstStart); } } applyPatch(patch) { @@ -109,7 +109,7 @@ class DamageBuffer { for (let row = 0;row < rect.height; row += 1) { const srcStart = row * patchStride; const dstStart = (rect.y + row) * this.stride + dstStartX; - Buffer.from(patch.rgba).copy(this.frame, dstStart, srcStart, srcStart + rowBytes); + this.frame.set(new Uint8Array(patch.rgba.buffer, patch.rgba.byteOffset + srcStart, rowBytes), dstStart); } } applyPatches(patches) { @@ -124,40 +124,49 @@ class WoDmabufSender { windowName; native; seq = 1n; + wireBuf; + nameLen; + seqOffset; + widthOffset; + heightOffset; constructor(socket, windowName, native) { this.socket = socket; this.windowName = windowName; this.native = native; + const nameBuf = Buffer.from(windowName, "utf8"); + this.nameLen = nameBuf.length; + const totalLen = 32 + this.nameLen + 16; + this.wireBuf = Buffer.alloc(totalLen); + let off = 0; + this.wireBuf.writeUInt32LE(MAGIC.FRAME, off); + off += 4; + this.wireBuf.writeUInt32LE(this.nameLen, off); + off += 4; + nameBuf.copy(this.wireBuf, off); + off += this.nameLen; + this.seqOffset = off; + off += 8; + this.widthOffset = off; + off += 4; + this.heightOffset = off; + off += 4; + this.wireBuf.writeUInt32LE(ARGB8888, off); + off += 4; + this.wireBuf.writeUInt32LE(1, off); } send(buffer) { const imported = this.native.importRgba(buffer.getFrameBuffer(), buffer.getWidth(), buffer.getHeight(), buffer.getStride()); try { const socketFd = getSocketFd(this.socket); - const nameBuf = Buffer.from(this.windowName, "utf8"); - const header = Buffer.alloc(4 + 4 + nameBuf.length + 8 + 4 + 4 + 4 + 4); - let offset = 0; - header.writeUInt32LE(MAGIC.FRAME, offset); - offset += 4; - header.writeUInt32LE(nameBuf.length, offset); - offset += 4; - nameBuf.copy(header, offset); - offset += nameBuf.length; - header.writeBigUInt64LE(this.seq, offset); - offset += 8; - header.writeUInt32LE(buffer.getWidth(), offset); - offset += 4; - header.writeUInt32LE(buffer.getHeight(), offset); - offset += 4; - header.writeUInt32LE(ARGB8888, offset); - offset += 4; - header.writeUInt32LE(1, offset); - const plane = Buffer.alloc(16); - plane.writeUInt32LE(imported.offset, 0); - plane.writeUInt32LE(imported.stride, 4); - plane.writeUInt32LE(imported.modifier_hi, 8); - plane.writeUInt32LE(imported.modifier_lo, 12); - const combined = Buffer.concat([header, plane]); - writeSync(socketFd, combined, 0, combined.length); + this.wireBuf.writeBigUInt64LE(this.seq, this.seqOffset); + this.wireBuf.writeUInt32LE(buffer.getWidth(), this.widthOffset); + this.wireBuf.writeUInt32LE(buffer.getHeight(), this.heightOffset); + const planeOff = this.wireBuf.length - 16; + this.wireBuf.writeUInt32LE(imported.offset, planeOff); + this.wireBuf.writeUInt32LE(imported.stride, planeOff + 4); + this.wireBuf.writeUInt32LE(imported.modifier_hi, planeOff + 8); + this.wireBuf.writeUInt32LE(imported.modifier_lo, planeOff + 12); + writeSync(socketFd, this.wireBuf, 0, this.wireBuf.length); this.native.sendFd(socketFd, imported.fd); const sentSeq = this.seq; this.seq += 1n; @@ -205,12 +214,24 @@ var __filename2 = fileURLToPath(import.meta.url); var __dirname2 = dirname(__filename2); var require2 = createRequire2(import.meta.url); var DEBUG_LOG = "/tmp/wo-electron-debug.log"; +var _logBuffer = []; +var _logTimer = null; +function _flushLog() { + if (_logBuffer.length === 0) + return; + try { + fs.appendFileSync(DEBUG_LOG, _logBuffer.join("")); + } catch (e) {} + _logBuffer = []; + _logTimer = null; +} function debugLog(...args) { const msg = `[${new Date().toISOString()}] ` + args.join(" ") + ` `; - try { - fs.appendFileSync(DEBUG_LOG, msg); - } catch (e) {} + _logBuffer.push(msg); + if (!_logTimer) { + _logTimer = setTimeout(_flushLog, 100); + } console.log(...args); } debugLog("[Wo] Electron starting, NODE_VERSION=", process.version); @@ -219,9 +240,10 @@ app.commandLine.appendSwitch("enable-gpu-rasterization"); app.commandLine.appendSwitch("enable-zero-copy"); app.commandLine.appendSwitch("enable-native-gpu-memory-buffers"); app.commandLine.appendSwitch("disable-software-rasterizer"); -app.commandLine.appendSwitch("enable-features", "CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder"); -app.commandLine.appendSwitch("use-gl", "egl"); +app.commandLine.appendSwitch("enable-features", "CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder,SharedArrayBuffer,RawDraw"); app.commandLine.appendSwitch("enable-accelerated-video-decode"); +app.commandLine.appendSwitch("disable-frame-rate-limit"); +app.commandLine.appendSwitch("disable-gpu-vsync"); debugLog("[Wo] GPU acceleration flags applied"); var IPC_SOCKET = process.env.WO_IPC_SOCKET || "/run/user/1000/wo-ipc.sock"; debugLog("[Wo] WO_WINDOW_CONFIG env =", process.env.WO_WINDOW_CONFIG); @@ -249,9 +271,10 @@ var nativeDmabuf = null; var compositorRxBuffer = Buffer.alloc(64 * 1024); var rxWriteOffset = 0; var rxReadOffset = 0; -var surfaceBufferPending = new Map; -var surfaceBufferFlushScheduled = false; -var shmReadBufferCache = new Map; +var surfaceSabCache = new Map; +var surfaceUpdatePending = new Map; +var surfaceUpdateGeneration = 0; +var surfaceUpdateFlushScheduled = false; var shmFdCache = new Map; function rxAppend(chunk) { const needed = rxWriteOffset + chunk.length; @@ -304,28 +327,39 @@ function getOrOpenShmFd(windowName, pid, fd) { shmFdCache.set(windowName, { pid, fd, extFd }); return extFd; } -function getReusableShmBuffer(windowName, size) { - const existing = shmReadBufferCache.get(windowName); - if (existing && existing.length >= size) { +function getOrCreateSab(windowName, width2, height2, stride) { + const existing = surfaceSabCache.get(windowName); + const neededSize = stride * height2; + if (existing && existing.sab.byteLength >= neededSize && existing.width === width2 && existing.height === height2 && existing.stride === stride) { return existing; } - const next = Buffer.allocUnsafe(size); - shmReadBufferCache.set(windowName, next); - return next; + const sab = new SharedArrayBuffer(neededSize); + const entry = { sab, width: width2, height: height2, stride }; + surfaceSabCache.set(windowName, entry); + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.postMessage("wo:surface-sab", { + name: windowName, + sab, + width: width2, + height: height2, + stride + }); + } + return entry; } -function flushSurfaceBuffers() { - surfaceBufferFlushScheduled = false; +function flushSurfaceUpdates() { + surfaceUpdateFlushScheduled = false; if (!mainWindow || mainWindow.isDestroyed()) { - surfaceBufferPending.clear(); + surfaceUpdatePending.clear(); return; } - for (const entry of surfaceBufferPending.values()) { - mainWindow.webContents.send("wo:surface-buffer", entry); + for (const entry of surfaceUpdatePending.values()) { + mainWindow.webContents.send("wo:surface-update", entry); } - surfaceBufferPending.clear(); + surfaceUpdatePending.clear(); } var inFlightFrameSeqs = new Set; -var MAX_IN_FLIGHT_FRAMES = 2; +var MAX_IN_FLIGHT_FRAMES = 3; var appUsedDamageHelper = false; var pointerX = 0; var pointerY = 0; @@ -1173,6 +1207,23 @@ function connectToCompositor() { try { const parsed = JSON.parse(metadata); if (parsed && Array.isArray(parsed.windows)) { + const currentNames = new Set(parsed.windows.map((w) => w.name).filter(Boolean)); + for (const cachedName of surfaceSabCache.keys()) { + if (!currentNames.has(cachedName)) { + surfaceSabCache.delete(cachedName); + } + } + for (const cachedName of shmFdCache.keys()) { + if (!currentNames.has(cachedName)) { + const entry = shmFdCache.get(cachedName); + if (entry) { + try { + fs.closeSync(entry.extFd); + } catch {} + } + shmFdCache.delete(cachedName); + } + } compositorWindows = parsed.windows; mainWindow.webContents.send("wo:windows", compositorWindows); } @@ -1219,9 +1270,9 @@ function connectToCompositor() { try { const extFd = getOrOpenShmFd(windowName, pid, fd); const fullSize = sbStride * sbHeight; - const safeBuffer = getReusableShmBuffer(windowName, fullSize); + const sabEntry = getOrCreateSab(windowName, sbWidth, sbHeight, sbStride); const hasUsableRects = damageRects.length > 0 && damageRects.length < 64; - if (hasUsableRects) { + if (hasUsableRects && nativeDmabuf?.copyMmapDamageToSab) { let minY = sbHeight, maxY = 0; for (const r of damageRects) { const ry = Math.max(0, r.y); @@ -1232,36 +1283,34 @@ function connectToCompositor() { maxY = ryEnd; } if (minY < maxY) { - const readOffset = minY * sbStride; - const readLen = (maxY - minY) * sbStride; - let totalRead = 0; - while (totalRead < readLen) { - const bytesRead = fs.readSync(extFd, safeBuffer, readOffset + totalRead, readLen - totalRead, readOffset + totalRead); - if (bytesRead <= 0) - break; - totalRead += bytesRead; - } + nativeDmabuf.copyMmapDamageToSab(extFd, sabEntry.sab, sbStride, [ + { y: minY, h: maxY - minY } + ]); } + } else if (nativeDmabuf?.copyMmapToSab) { + nativeDmabuf.copyMmapToSab(extFd, sabEntry.sab, fullSize); } else { + const tmpBuf = Buffer.alloc(fullSize); let totalRead = 0; while (totalRead < fullSize) { - const bytesRead = fs.readSync(extFd, safeBuffer, totalRead, fullSize - totalRead, totalRead); + const bytesRead = fs.readSync(extFd, tmpBuf, totalRead, fullSize - totalRead, totalRead); if (bytesRead <= 0) break; totalRead += bytesRead; } + new Uint8Array(sabEntry.sab).set(new Uint8Array(tmpBuf.buffer, tmpBuf.byteOffset, tmpBuf.byteLength)); } - surfaceBufferPending.set(windowName, { + surfaceUpdatePending.set(windowName, { name: windowName, width: sbWidth, height: sbHeight, stride: sbStride, - pixels: safeBuffer, + generation: ++surfaceUpdateGeneration, damageRects: hasUsableRects ? damageRects : undefined }); - if (!surfaceBufferFlushScheduled) { - surfaceBufferFlushScheduled = true; - setImmediate(flushSurfaceBuffers); + if (!surfaceUpdateFlushScheduled) { + surfaceUpdateFlushScheduled = true; + setImmediate(flushSurfaceUpdates); } } catch (err) { const cached = shmFdCache.get(windowName); @@ -1396,7 +1445,12 @@ function connectToCompositor() { ipcSocket = null; dmabufSender = null; inFlightFrameSeqs.clear(); - compositorRxBuffer = Buffer.alloc(0); + compositorRxBuffer = Buffer.alloc(64 * 1024); + rxWriteOffset = 0; + rxReadOffset = 0; + surfaceSabCache.clear(); + surfaceUpdatePending.clear(); + closeShmFdCache(); scheduleIpcReconnect(); }); setTimeout(() => { @@ -1762,6 +1816,15 @@ async function createWindow() { }, show: CLIENT_MODE }); + mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { + callback({ + responseHeaders: { + ...details.responseHeaders, + "Cross-Origin-Opener-Policy": ["same-origin"], + "Cross-Origin-Embedder-Policy": ["require-corp"] + } + }); + }); debugLog("[Wo] BrowserWindow created, CLIENT_MODE=" + CLIENT_MODE + " offscreen=" + !CLIENT_MODE); setupIpcHandlers(); debugLog("[Wo] setupIpcHandlers completed"); @@ -1794,10 +1857,11 @@ async function createWindow() { }); const frameIntervalMs = Math.round(1000 / (WINDOW_CONFIG.fps ?? 60)); setInterval(() => { - if (mainWindow && !mainWindow.isDestroyed()) { + if (mainWindow && !mainWindow.isDestroyed() && inFlightFrameSeqs.size < MAX_IN_FLIGHT_FRAMES) { mainWindow.webContents.invalidate(); } }, frameIntervalMs); + let patchBuf = null; let skippedCount = 0; mainWindow.webContents.on("paint", (_event, dirty, image) => { if (!dmabufSender || !ipcConnected) { @@ -1819,7 +1883,7 @@ async function createWindow() { if (!damageBuffer) { damageBuffer = new DamageBuffer(size.width, size.height); } - const pixels = image.getBitmap(); + const pixels = image.toBitmap(); const pixelData = new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength); const isFullFrame = dirty.x === 0 && dirty.y === 0 && dirty.width === size.width && dirty.height === size.height; if (isFullFrame) { @@ -1831,7 +1895,11 @@ async function createWindow() { } else { const stride = size.width * 4; const patchStride = dirty.width * 4; - const patchData = new Uint8Array(dirty.width * dirty.height * 4); + const patchBytes = patchStride * dirty.height; + if (!patchBuf || patchBuf.byteLength < patchBytes) { + patchBuf = new Uint8Array(patchBytes); + } + const patchData = patchBuf.byteLength === patchBytes ? patchBuf : patchBuf.subarray(0, patchBytes); for (let row = 0;row < dirty.height; row++) { const srcOff = (dirty.y + row) * stride + dirty.x * 4; const dstOff = row * patchStride; @@ -1885,6 +1953,9 @@ async function createWindow() { } app.on("ready", async () => { debugLog("[Wo] App ready, CLIENT_MODE=", CLIENT_MODE, "WO_STANDALONE=", process.env.WO_STANDALONE); + const gpuInfo = app.getGPUFeatureStatus(); + debugLog("[Wo] GPU feature status:", JSON.stringify(gpuInfo)); + debugLog("[Wo] GPU info:", JSON.stringify(app.getGPUInfo("basic").catch(() => ({})))); try { startPortalUiBridge(); if (!CLIENT_MODE && process.env.WO_STANDALONE !== "1") { diff --git a/electron/native/wo_dmabuf.cc b/electron/native/wo_dmabuf.cc index 4e51808..81daffb 100644 --- a/electron/native/wo_dmabuf.cc +++ b/electron/native/wo_dmabuf.cc @@ -144,6 +144,51 @@ static std::map g_bufs; static std::mutex g_bufs_mutex; static uint32_t g_next_token = 1; +// ── GBM BO pool ────────────────────────────────────────────────────── +// Reuse GBM BOs of the same (width, height) to avoid alloc/dealloc +// overhead on every frame. The pool keeps up to two idle BOs per +// dimension so the sender can fill the next frame while the previous +// one is still in flight (double-buffering). +struct PoolKey { + uint32_t w, h; + bool operator<(const PoolKey &o) const { + return w < o.w || (w == o.w && h < o.h); + } +}; + +struct PoolEntry { + gbm_bo *bo; + uint32_t dst_stride; +}; + +static constexpr size_t MAX_POOL_PER_DIM = 2; +static std::map> g_bo_pool; +static std::mutex g_bo_pool_mutex; + +static gbm_bo *pool_acquire(uint32_t w, uint32_t h, uint32_t *out_stride) { + std::lock_guard lk(g_bo_pool_mutex); + auto it = g_bo_pool.find({w, h}); + if (it != g_bo_pool.end() && !it->second.empty()) { + PoolEntry pe = it->second.back(); + it->second.pop_back(); + *out_stride = pe.dst_stride; + return pe.bo; + } + return nullptr; +} + +static void pool_return(gbm_bo *bo, uint32_t w, uint32_t h, uint32_t stride) { + std::lock_guard lk(g_bo_pool_mutex); + PoolKey key{w, h}; + auto &vec = g_bo_pool[key]; + if (vec.size() >= MAX_POOL_PER_DIM) { + // Pool full — destroy the returned BO + gbm_bo_destroy(bo); + } else { + vec.push_back({bo, stride}); + } +} + static uint32_t store_buf(gbm_bo *bo, int fd) { std::lock_guard lk(g_bufs_mutex); uint32_t tok = g_next_token++; @@ -157,8 +202,13 @@ static void release_buf(uint32_t tok) { if (it == g_bufs.end()) return; ::close(it->second.dmabuf_fd); - gbm_bo_destroy(it->second.bo); + gbm_bo *bo = it->second.bo; + uint32_t w = gbm_bo_get_width(bo); + uint32_t h = gbm_bo_get_height(bo); + uint32_t stride = gbm_bo_get_stride(bo); g_bufs.erase(it); + // Return to pool for reuse instead of destroying + pool_return(bo, w, h, stride); } Napi::Value Init(const Napi::CallbackInfo &info) { @@ -195,44 +245,51 @@ Napi::Value ImportRgba(const Napi::CallbackInfo &info) { return env.Null(); } - gbm_bo *bo = nullptr; - - bo = gbm_bo_create_with_modifiers2(g_gbm, w, h, GBM_FORMAT_ARGB8888, - (const uint64_t[]){DRM_FORMAT_MOD_LINEAR}, - 1, - GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); + // Try to reuse a pooled BO of matching dimensions + uint32_t dst_stride = 0; + gbm_bo *bo = pool_acquire(w, h, &dst_stride); if (!bo) { - bo = gbm_bo_create(g_gbm, w, h, GBM_FORMAT_ARGB8888, - GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); - } - - if (!bo) { - bo = gbm_bo_create(g_gbm, w, h, GBM_FORMAT_ARGB8888, GBM_BO_USE_WRITE); - } - - if (!bo) { - Napi::Error::New( - env, "gbm_bo_create failed: all buffer creation methods exhausted") - .ThrowAsJavaScriptException(); - return env.Null(); + bo = gbm_bo_create_with_modifiers2(g_gbm, w, h, GBM_FORMAT_ARGB8888, + (const uint64_t[]){DRM_FORMAT_MOD_LINEAR}, + 1, + GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); + if (!bo) { + bo = gbm_bo_create(g_gbm, w, h, GBM_FORMAT_ARGB8888, + GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); + } + if (!bo) { + bo = gbm_bo_create(g_gbm, w, h, GBM_FORMAT_ARGB8888, GBM_BO_USE_WRITE); + } + if (!bo) { + Napi::Error::New( + env, "gbm_bo_create failed: all buffer creation methods exhausted") + .ThrowAsJavaScriptException(); + return env.Null(); + } } void *map_data = nullptr; - uint32_t dst_stride = 0; + uint32_t map_stride = 0; void *ptr = - gbm_bo_map(bo, 0, 0, w, h, GBM_BO_TRANSFER_WRITE, &dst_stride, &map_data); + gbm_bo_map(bo, 0, 0, w, h, GBM_BO_TRANSFER_WRITE, &map_stride, &map_data); if (!ptr) { gbm_bo_destroy(bo); Napi::Error::New(env, "gbm_bo_map failed").ThrowAsJavaScriptException(); return env.Null(); } + dst_stride = map_stride; const uint8_t *src = pixel_buf.Data(); uint8_t *dst = static_cast(ptr); - uint32_t row_bytes = std::min(src_stride, dst_stride); - for (uint32_t y = 0; y < h; ++y) - std::memcpy(dst + y * dst_stride, src + y * src_stride, row_bytes); + if (src_stride == dst_stride) { + // Fast path: strides match — single memcpy for entire image + std::memcpy(dst, src, static_cast(dst_stride) * h); + } else { + uint32_t row_bytes = std::min(src_stride, dst_stride); + for (uint32_t y = 0; y < h; ++y) + std::memcpy(dst + y * dst_stride, src + y * src_stride, row_bytes); + } gbm_bo_unmap(bo, map_data); @@ -508,6 +565,118 @@ Napi::Value MmapFd(const Napi::CallbackInfo &info) { hint); } +// ── Persistent mmap cache ───────────────────────────────────────────── +// CopyMmapToSab and CopyMmapDamageToSab are called on every frame for +// the same fd+size. Avoid the mmap/munmap syscall pair per frame by +// caching the mapping. Evict when fd or size changes. +struct MmapCacheEntry { + int fd; + void *ptr; + size_t size; +}; +static MmapCacheEntry g_mmap_cache{-1, MAP_FAILED, 0}; +static std::mutex g_mmap_cache_mutex; + +static void *mmap_cached(int fd, size_t size) { + std::lock_guard lk(g_mmap_cache_mutex); + if (g_mmap_cache.fd == fd && g_mmap_cache.size == size && + g_mmap_cache.ptr != MAP_FAILED) { + return g_mmap_cache.ptr; + } + // Evict old mapping + if (g_mmap_cache.ptr != MAP_FAILED) { + ::munmap(g_mmap_cache.ptr, g_mmap_cache.size); + } + void *ptr = ::mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); + g_mmap_cache = {fd, ptr, size}; + return ptr; +} + +// mmap a file descriptor into a region and copy into the backing store of a +// pre-existing SharedArrayBuffer. This is the fast path for SHM surface +// buffers: the compositor writes to a memfd, we mmap+memcpy into a SAB +// that is already shared with the Electron renderer — no structured-clone +// serialization across the Electron IPC boundary. +// +// copyMmapToSab(fd, sab, size, srcOffset?, dstOffset?) +// If damage rects are provided, only the specified rows are copied. +Napi::Value CopyMmapToSab(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + if (info.Length() < 3) { + Napi::TypeError::New(env, "copyMmapToSab(fd, sab, size [, srcOff, dstOff])") + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + int fd = info[0].As().Int32Value(); + auto arrayBuf = info[1].As(); + size_t size = static_cast(info[2].As().Int64Value()); + size_t src_off = (info.Length() > 3 && info[3].IsNumber()) + ? static_cast(info[3].As().Int64Value()) + : 0; + size_t dst_off = (info.Length() > 4 && info[4].IsNumber()) + ? static_cast(info[4].As().Int64Value()) + : 0; + + if (dst_off + size > arrayBuf.ByteLength()) { + Napi::RangeError::New(env, "copyMmapToSab: destination overflow") + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + void *ptr = mmap_cached(fd, src_off + size); + if (ptr == MAP_FAILED) { + Napi::Error::New(env, std::string("mmap failed: ") + ::strerror(errno)) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + std::memcpy(static_cast(arrayBuf.Data()) + dst_off, + static_cast(ptr) + src_off, size); + + return env.Undefined(); +} + +// Partial-damage variant: copy only specified row ranges from mmap'd fd +// into a SharedArrayBuffer. +// copyMmapDamageToSab(fd, sab, stride, rects: [{y, h}]) +Napi::Value CopyMmapDamageToSab(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + if (info.Length() < 4) { + Napi::TypeError::New(env, "copyMmapDamageToSab(fd, sab, stride, rects)") + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + int fd = info[0].As().Int32Value(); + auto arrayBuf = info[1].As(); + uint32_t stride = info[2].As().Uint32Value(); + auto rects = info[3].As(); + + size_t totalSize = arrayBuf.ByteLength(); + void *ptr = mmap_cached(fd, totalSize); + if (ptr == MAP_FAILED) { + Napi::Error::New(env, std::string("mmap failed: ") + ::strerror(errno)) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + uint8_t *src = static_cast(ptr); + uint8_t *dst = static_cast(arrayBuf.Data()); + + for (uint32_t i = 0; i < rects.Length(); i++) { + auto rect = rects.Get(i).As(); + uint32_t ry = rect.Get("y").As().Uint32Value(); + uint32_t rh = rect.Get("h").As().Uint32Value(); + size_t off = static_cast(ry) * stride; + size_t len = static_cast(rh) * stride; + if (off + len > totalSize) len = totalSize - off; + std::memcpy(dst + off, src + off, len); + } + + return env.Undefined(); +} + Napi::Object ModuleInit(Napi::Env env, Napi::Object exports) { exports.Set("init", Napi::Function::New(env, Init)); exports.Set("importRgba", Napi::Function::New(env, ImportRgba)); @@ -515,6 +684,8 @@ Napi::Object ModuleInit(Napi::Env env, Napi::Object exports) { exports.Set("sendFd", Napi::Function::New(env, SendFd)); exports.Set("recvFd", Napi::Function::New(env, RecvFd)); exports.Set("mmapFd", Napi::Function::New(env, MmapFd)); + exports.Set("copyMmapToSab", Napi::Function::New(env, CopyMmapToSab)); + exports.Set("copyMmapDamageToSab", Napi::Function::New(env, CopyMmapDamageToSab)); exports.Set("importDmabufTexture", Napi::Function::New(env, ImportDmabufTexture)); exports.Set("getTexture", Napi::Function::New(env, GetTexture)); exports.Set("releaseTexture", Napi::Function::New(env, ReleaseTexture)); diff --git a/electron/src/damage-buffer.ts b/electron/src/damage-buffer.ts index 92ef983..68685ee 100644 --- a/electron/src/damage-buffer.ts +++ b/electron/src/damage-buffer.ts @@ -60,6 +60,8 @@ type NativeDmabufModule = { sendFd: (socketFd: number, dmabufFd: number) => void; recvFd: (socketFd: number) => number; mmapFd: (fd: number, size: number) => Buffer; + copyMmapToSab: (fd: number, sab: SharedArrayBuffer, size: number, srcOff?: number, dstOff?: number) => void; + copyMmapDamageToSab: (fd: number, sab: SharedArrayBuffer, stride: number, rects: Array<{y: number; h: number}>) => void; importDmabufTexture: (windowName: string, fd: number, width: number, height: number, format: number) => { texture: number; width: number; height: number }; getTexture: (windowName: string) => { texture: number; width: number; height: number } | null; releaseTexture: (windowName: string) => void; @@ -105,7 +107,8 @@ export class DamageBuffer { applyFullFrame(frame: Uint8Array, frameStride: number = this.width * 4): void { if (frameStride === this.stride) { - Buffer.from(frame).copy(this.frame, 0, 0, this.frame.length); + // Fast path: direct typed-array copy, no intermediate Buffer wrapper + this.frame.set(new Uint8Array(frame.buffer, frame.byteOffset, this.frame.length)); return; } @@ -113,7 +116,10 @@ export class DamageBuffer { for (let y = 0; y < this.height; y += 1) { const srcStart = y * frameStride; const dstStart = y * this.stride; - Buffer.from(frame).copy(this.frame, dstStart, srcStart, srcStart + rowBytes); + this.frame.set( + new Uint8Array(frame.buffer, frame.byteOffset + srcStart, rowBytes), + dstStart, + ); } } @@ -130,7 +136,10 @@ export class DamageBuffer { for (let row = 0; row < rect.height; row += 1) { const srcStart = row * patchStride; const dstStart = (rect.y + row) * this.stride + dstStartX; - Buffer.from(patch.rgba).copy(this.frame, dstStart, srcStart, srcStart + rowBytes); + this.frame.set( + new Uint8Array(patch.rgba.buffer, patch.rgba.byteOffset + srcStart, rowBytes), + dstStart, + ); } } @@ -146,11 +155,35 @@ export class WoDmabufSender { private readonly windowName: string; private readonly native: NativeDmabufModule; private seq = 1n; + // Pre-allocated wire buffer: header (variable) + plane (16 bytes). + // Layout: MAGIC(4) + nameLen(4) + name(N) + seq(8) + w(4) + h(4) + fmt(4) + planes(4) + plane(16) + private readonly wireBuf: Buffer; + private readonly nameLen: number; + private readonly seqOffset: number; + private readonly widthOffset: number; + private readonly heightOffset: number; constructor(socket: Socket, windowName: string, native: NativeDmabufModule) { this.socket = socket; this.windowName = windowName; this.native = native; + + const nameBuf = Buffer.from(windowName, 'utf8'); + this.nameLen = nameBuf.length; + // header = 4+4+nameLen+8+4+4+4+4 = 32+nameLen, plus 16 bytes for plane + const totalLen = 32 + this.nameLen + 16; + this.wireBuf = Buffer.alloc(totalLen); + + // Static fields that never change + let off = 0; + this.wireBuf.writeUInt32LE(MAGIC.FRAME, off); off += 4; + this.wireBuf.writeUInt32LE(this.nameLen, off); off += 4; + nameBuf.copy(this.wireBuf, off); off += this.nameLen; + this.seqOffset = off; off += 8; + this.widthOffset = off; off += 4; + this.heightOffset = off; off += 4; + this.wireBuf.writeUInt32LE(ARGB8888, off); off += 4; + this.wireBuf.writeUInt32LE(1, off); // planes count = 1 } send(buffer: DamageBuffer): bigint { @@ -164,32 +197,19 @@ export class WoDmabufSender { try { const socketFd = getSocketFd(this.socket); - const nameBuf = Buffer.from(this.windowName, 'utf8'); - const header = Buffer.alloc(4 + 4 + nameBuf.length + 8 + 4 + 4 + 4 + 4); - let offset = 0; - header.writeUInt32LE(MAGIC.FRAME, offset); offset += 4; - header.writeUInt32LE(nameBuf.length, offset); offset += 4; - nameBuf.copy(header, offset); offset += nameBuf.length; - header.writeBigUInt64LE(this.seq, offset); offset += 8; - header.writeUInt32LE(buffer.getWidth(), offset); offset += 4; - header.writeUInt32LE(buffer.getHeight(), offset); offset += 4; - header.writeUInt32LE(ARGB8888, offset); offset += 4; - header.writeUInt32LE(1, offset); + // Write per-frame fields into the pre-allocated buffer + this.wireBuf.writeBigUInt64LE(this.seq, this.seqOffset); + this.wireBuf.writeUInt32LE(buffer.getWidth(), this.widthOffset); + this.wireBuf.writeUInt32LE(buffer.getHeight(), this.heightOffset); - const plane = Buffer.alloc(16); - plane.writeUInt32LE(imported.offset, 0); - plane.writeUInt32LE(imported.stride, 4); - plane.writeUInt32LE(imported.modifier_hi, 8); - plane.writeUInt32LE(imported.modifier_lo, 12); + // Plane data (last 16 bytes of wireBuf) + const planeOff = this.wireBuf.length - 16; + this.wireBuf.writeUInt32LE(imported.offset, planeOff); + this.wireBuf.writeUInt32LE(imported.stride, planeOff + 4); + this.wireBuf.writeUInt32LE(imported.modifier_hi, planeOff + 8); + this.wireBuf.writeUInt32LE(imported.modifier_lo, planeOff + 12); - // CRITICAL: We must use synchronous, unbuffered writes to the raw - // socket fd so that the header+plane bytes are in the kernel buffer - // BEFORE sendFd's sendmsg() call. Node's socket.write() is async - // and buffers in userspace — sendFd (a native sendmsg) would race - // ahead, delivering the SCM_RIGHTS ancillary data before the header - // bytes, corrupting the protocol framing on the compositor side. - const combined = Buffer.concat([header, plane]); - writeSync(socketFd, combined, 0, combined.length); + writeSync(socketFd, this.wireBuf, 0, this.wireBuf.length); this.native.sendFd(socketFd, imported.fd); const sentSeq = this.seq; diff --git a/electron/src/main.ts b/electron/src/main.ts index 2414b23..e751555 100644 --- a/electron/src/main.ts +++ b/electron/src/main.ts @@ -18,30 +18,44 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const require = createRequire(import.meta.url); -// Debug logging to file +// Debug logging to file — buffered to avoid synchronous I/O in hot paths const DEBUG_LOG = '/tmp/wo-electron-debug.log'; -function debugLog(...args: any[]) { - const msg = `[${new Date().toISOString()}] ` + args.join(' ') + '\n'; +let _logBuffer: string[] = []; +let _logTimer: ReturnType | null = null; +function _flushLog() { + if (_logBuffer.length === 0) return; try { - fs.appendFileSync(DEBUG_LOG, msg); + fs.appendFileSync(DEBUG_LOG, _logBuffer.join('')); } catch (e) { // ignore } + _logBuffer = []; + _logTimer = null; +} +function debugLog(...args: any[]) { + const msg = `[${new Date().toISOString()}] ` + args.join(' ') + '\n'; + _logBuffer.push(msg); + if (!_logTimer) { + _logTimer = setTimeout(_flushLog, 100); + } console.log(...args); } debugLog('[Wo] Electron starting, NODE_VERSION=', process.version); // GPU acceleration & rendering performance flags (must be set before app 'ready') +// Note: core flags (--in-process-gpu, --use-gl=egl, --ozone-platform=wayland) +// are set on the command line in the Rust spawn(). Flags below augment those. app.commandLine.appendSwitch('ignore-gpu-blocklist'); app.commandLine.appendSwitch('enable-gpu-rasterization'); app.commandLine.appendSwitch('enable-zero-copy'); app.commandLine.appendSwitch('enable-native-gpu-memory-buffers'); app.commandLine.appendSwitch('disable-software-rasterizer'); app.commandLine.appendSwitch('enable-features', - 'CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder'); -app.commandLine.appendSwitch('use-gl', 'egl'); + 'CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder,SharedArrayBuffer,RawDraw'); app.commandLine.appendSwitch('enable-accelerated-video-decode'); +app.commandLine.appendSwitch('disable-frame-rate-limit'); +app.commandLine.appendSwitch('disable-gpu-vsync'); debugLog('[Wo] GPU acceleration flags applied'); type ClientWindowInfo = { @@ -85,9 +99,28 @@ let nativeDmabuf: any = null; let compositorRxBuffer = Buffer.alloc(64 * 1024); let rxWriteOffset = 0; // next write position let rxReadOffset = 0; // next read position -const surfaceBufferPending = new Map }>(); -let surfaceBufferFlushScheduled = false; -const shmReadBufferCache = new Map(); +// ── Zero-copy surface buffer transfer via SharedArrayBuffer ── +// One SAB per window. The main process mmap's compositor memfd data into +// the SAB; the renderer reads directly from the same memory — no +// structured-clone serialization across the Electron IPC boundary. +type SabEntry = { + sab: SharedArrayBuffer; + width: number; + height: number; + stride: number; +}; +const surfaceSabCache = new Map(); +// Lightweight generation counter + damage rects sent via IPC instead of pixels +const surfaceUpdatePending = new Map; +}>(); +let surfaceUpdateGeneration = 0; +let surfaceUpdateFlushScheduled = false; const shmFdCache = new Map(); /** Append incoming chunk to the rx ring buffer, growing if needed. */ @@ -158,26 +191,39 @@ function getOrOpenShmFd(windowName: string, pid: number, fd: number): number { return extFd; } -function getReusableShmBuffer(windowName: string, size: number): Buffer { - const existing = shmReadBufferCache.get(windowName); - if (existing && existing.length >= size) { +function getOrCreateSab(windowName: string, width: number, height: number, stride: number): SabEntry { + const existing = surfaceSabCache.get(windowName); + const neededSize = stride * height; + if (existing && existing.sab.byteLength >= neededSize && existing.width === width && existing.height === height && existing.stride === stride) { return existing; } - const next = Buffer.allocUnsafe(size); - shmReadBufferCache.set(windowName, next); - return next; + const sab = new SharedArrayBuffer(neededSize); + const entry: SabEntry = { sab, width, height, stride }; + surfaceSabCache.set(windowName, entry); + // Tell renderer about the new SAB so it can reference it + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.postMessage('wo:surface-sab', { + name: windowName, + sab, + width, + height, + stride, + }); + } + return entry; } -function flushSurfaceBuffers() { - surfaceBufferFlushScheduled = false; - if (!mainWindow || mainWindow.isDestroyed()) { surfaceBufferPending.clear(); return; } - for (const entry of surfaceBufferPending.values()) { - mainWindow.webContents.send('wo:surface-buffer', entry); +function flushSurfaceUpdates() { + surfaceUpdateFlushScheduled = false; + if (!mainWindow || mainWindow.isDestroyed()) { surfaceUpdatePending.clear(); return; } + for (const entry of surfaceUpdatePending.values()) { + // Send only metadata (no pixels) — the renderer reads from SAB directly + mainWindow.webContents.send('wo:surface-update', entry); } - surfaceBufferPending.clear(); + surfaceUpdatePending.clear(); } const inFlightFrameSeqs = new Set(); -const MAX_IN_FLIGHT_FRAMES = 2; +const MAX_IN_FLIGHT_FRAMES = 3; // When the app uses compositor.submitDamageFrame() directly, skip OSR paint path to avoid double submission let appUsedDamageHelper = false; // Track pointer position for mouse button/scroll events (window-local coords) @@ -1084,6 +1130,23 @@ function connectToCompositor(): Promise { try { const parsed = JSON.parse(metadata); if (parsed && Array.isArray(parsed.windows)) { + // Clean up caches for windows that disappeared + const currentNames = new Set((parsed.windows as Array<{name?: string}>).map(w => w.name).filter(Boolean)); + for (const cachedName of surfaceSabCache.keys()) { + if (!currentNames.has(cachedName)) { + surfaceSabCache.delete(cachedName); + } + } + for (const cachedName of shmFdCache.keys()) { + if (!currentNames.has(cachedName)) { + const entry = shmFdCache.get(cachedName); + if (entry) { + try { fs.closeSync(entry.extFd); } catch { /* ignore */ } + } + shmFdCache.delete(cachedName); + } + } + compositorWindows = parsed.windows; mainWindow.webContents.send('wo:windows', compositorWindows); } @@ -1125,13 +1188,13 @@ function connectToCompositor(): Promise { try { const extFd = getOrOpenShmFd(windowName, pid, fd); const fullSize = sbStride * sbHeight; - const safeBuffer = getReusableShmBuffer(windowName, fullSize); - // Partial read: only read damaged rows from memfd. - // Empty damageRects = full frame update (always do full read). - // Also do full read on first frame for this window (buffer is uninitialized). + // Ensure a SAB exists for this window at the right dimensions + const sabEntry = getOrCreateSab(windowName, sbWidth, sbHeight, sbStride); + const hasUsableRects = damageRects.length > 0 && damageRects.length < 64; - if (hasUsableRects) { + if (hasUsableRects && nativeDmabuf?.copyMmapDamageToSab) { + // Partial damage: mmap and copy only damaged row bands into SAB let minY = sbHeight, maxY = 0; for (const r of damageRects) { const ry = Math.max(0, r.y); @@ -1140,36 +1203,36 @@ function connectToCompositor(): Promise { if (ryEnd > maxY) maxY = ryEnd; } if (minY < maxY) { - const readOffset = minY * sbStride; - const readLen = (maxY - minY) * sbStride; - let totalRead = 0; - while (totalRead < readLen) { - const bytesRead = fs.readSync(extFd, safeBuffer, readOffset + totalRead, readLen - totalRead, readOffset + totalRead); - if (bytesRead <= 0) break; - totalRead += bytesRead; - } + nativeDmabuf.copyMmapDamageToSab(extFd, sabEntry.sab, sbStride, [ + { y: minY, h: maxY - minY }, + ]); } + } else if (nativeDmabuf?.copyMmapToSab) { + // Full frame: mmap entire buffer into SAB + nativeDmabuf.copyMmapToSab(extFd, sabEntry.sab, fullSize); } else { - // Full read (no rects, too many rects, or first frame) + // Fallback: fs.readSync into a temporary buffer, then copy to SAB + const tmpBuf = Buffer.alloc(fullSize); let totalRead = 0; while (totalRead < fullSize) { - const bytesRead = fs.readSync(extFd, safeBuffer, totalRead, fullSize - totalRead, totalRead); + const bytesRead = fs.readSync(extFd, tmpBuf, totalRead, fullSize - totalRead, totalRead); if (bytesRead <= 0) break; totalRead += bytesRead; } + new Uint8Array(sabEntry.sab).set(new Uint8Array(tmpBuf.buffer, tmpBuf.byteOffset, tmpBuf.byteLength)); } - surfaceBufferPending.set(windowName, { + surfaceUpdatePending.set(windowName, { name: windowName, width: sbWidth, height: sbHeight, stride: sbStride, - pixels: safeBuffer, + generation: ++surfaceUpdateGeneration, damageRects: hasUsableRects ? damageRects : undefined, }); - if (!surfaceBufferFlushScheduled) { - surfaceBufferFlushScheduled = true; - setImmediate(flushSurfaceBuffers); + if (!surfaceUpdateFlushScheduled) { + surfaceUpdateFlushScheduled = true; + setImmediate(flushSurfaceUpdates); } } catch (err) { const cached = shmFdCache.get(windowName); @@ -1311,7 +1374,13 @@ function connectToCompositor(): Promise { ipcSocket = null; dmabufSender = null; inFlightFrameSeqs.clear(); - compositorRxBuffer = Buffer.alloc(0); + compositorRxBuffer = Buffer.alloc(64 * 1024); + rxWriteOffset = 0; + rxReadOffset = 0; + // Clean up surface caches + surfaceSabCache.clear(); + surfaceUpdatePending.clear(); + closeShmFdCache(); scheduleIpcReconnect(); }); @@ -1736,6 +1805,18 @@ async function createWindow() { show: CLIENT_MODE, }); + // Enable SharedArrayBuffer in the renderer by injecting + // cross-origin isolation headers on all responses. + mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { + callback({ + responseHeaders: { + ...details.responseHeaders, + 'Cross-Origin-Opener-Policy': ['same-origin'], + 'Cross-Origin-Embedder-Policy': ['require-corp'], + }, + }); + }); + debugLog('[Wo] BrowserWindow created, CLIENT_MODE=' + CLIENT_MODE + ' offscreen=' + !CLIENT_MODE); setupIpcHandlers(); @@ -1775,12 +1856,22 @@ async function createWindow() { mainWindow.webContents.on('did-finish-load', () => { mainWindow!.webContents.setFrameRate(WINDOW_CONFIG.fps ?? 60); }); + + // Invalidate periodically to ensure continuous repaints for dynamic + // content. We only invalidate when below the in-flight limit so we + // don't pile up redundant paints while the compositor is still + // processing previous frames. const frameIntervalMs = Math.round(1000 / (WINDOW_CONFIG.fps ?? 60)); setInterval(() => { - if (mainWindow && !mainWindow.isDestroyed()) { + if (mainWindow && !mainWindow.isDestroyed() && inFlightFrameSeqs.size < MAX_IN_FLIGHT_FRAMES) { mainWindow.webContents.invalidate(); } }, frameIntervalMs); + + // Reusable patch buffer to avoid allocating a new typed array on + // every partial-update paint event. + let patchBuf: Uint8Array | null = null; + let skippedCount = 0; mainWindow.webContents.on('paint', (_event, dirty, image) => { if (!dmabufSender || !ipcConnected) { @@ -1802,7 +1893,7 @@ async function createWindow() { if (!damageBuffer) { damageBuffer = new DamageBuffer(size.width, size.height); } - const pixels = image.getBitmap(); + const pixels = image.toBitmap(); const pixelData = new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength); // Use dirty rect to avoid full-frame copy when only a region changed @@ -1816,10 +1907,18 @@ async function createWindow() { fullFrame: pixelData, }); } else { - // Partial update — copy only the dirty region into the damage buffer + // Partial update — copy only the dirty region into the damage + // buffer. Reuse a single scratch buffer to avoid per-frame GC + // pressure from allocating a new Uint8Array every paint. const stride = size.width * 4; const patchStride = dirty.width * 4; - const patchData = new Uint8Array(dirty.width * dirty.height * 4); + const patchBytes = patchStride * dirty.height; + if (!patchBuf || patchBuf.byteLength < patchBytes) { + patchBuf = new Uint8Array(patchBytes); + } + const patchData = patchBuf.byteLength === patchBytes + ? patchBuf + : patchBuf.subarray(0, patchBytes); for (let row = 0; row < dirty.height; row++) { const srcOff = (dirty.y + row) * stride + dirty.x * 4; const dstOff = row * patchStride; @@ -1886,6 +1985,12 @@ async function createWindow() { app.on('ready', async () => { debugLog('[Wo] App ready, CLIENT_MODE=', CLIENT_MODE, 'WO_STANDALONE=', process.env.WO_STANDALONE); + + // Log GPU status so we can verify hardware acceleration is active. + const gpuInfo = app.getGPUFeatureStatus(); + debugLog('[Wo] GPU feature status:', JSON.stringify(gpuInfo)); + debugLog('[Wo] GPU info:', JSON.stringify(app.getGPUInfo('basic').catch(() => ({})))); + try { startPortalUiBridge(); diff --git a/electron/src/preload.ts b/electron/src/preload.ts index b2d23dd..d348bde 100644 --- a/electron/src/preload.ts +++ b/electron/src/preload.ts @@ -51,6 +51,7 @@ const compositorAPI: CompositorAPI = { }, // Surface buffer: Wayland window pixel content from compositor + // Legacy path for backward compatibility (full pixel copy via IPC) onSurfaceBuffer: (callback: (data: { name: string; width: number; height: number; stride: number; pixels: Buffer; damageRects?: Array<{x: number; y: number; width: number; height: number}> }) => void) => { const handler = (_event: Electron.IpcRendererEvent, data: { name: string; width: number; height: number; stride: number; pixels: Buffer; damageRects?: Array<{x: number; y: number; width: number; height: number}> }) => callback(data); @@ -58,6 +59,23 @@ const compositorAPI: CompositorAPI = { return () => ipcRenderer.removeListener('wo:surface-buffer', handler); }, + // Zero-copy surface buffer via SharedArrayBuffer. + // 'wo:surface-sab' delivers the SAB once per window (or on resize). + // 'wo:surface-update' is a lightweight signal (no pixels) when new data is ready. + onSurfaceSab: (callback: (data: { name: string; sab: SharedArrayBuffer; width: number; height: number; stride: number }) => void) => { + const handler = (_event: Electron.IpcRendererEvent, data: { name: string; sab: SharedArrayBuffer; width: number; height: number; stride: number }) => + callback(data); + ipcRenderer.on('wo:surface-sab', handler); + return () => ipcRenderer.removeListener('wo:surface-sab', handler); + }, + + onSurfaceUpdate: (callback: (data: { name: string; width: number; height: number; stride: number; generation: number; damageRects?: Array<{x: number; y: number; width: number; height: number}> }) => void) => { + const handler = (_event: Electron.IpcRendererEvent, data: { name: string; width: number; height: number; stride: number; generation: number; damageRects?: Array<{x: number; y: number; width: number; height: number}> }) => + callback(data); + ipcRenderer.on('wo:surface-update', handler); + return () => ipcRenderer.removeListener('wo:surface-update', handler); + }, + createDamageHelper: (options: RendererDamageHelperOptions): RendererDamageHelper => { return createRendererDamageHelper( async (payload: DamageFramePayload) => ipcRenderer.invoke('wo:submit-damage-frame', payload), diff --git a/electron/types/package.json b/electron/types/package.json index 509d3e8..660d5b0 100644 --- a/electron/types/package.json +++ b/electron/types/package.json @@ -2,7 +2,7 @@ "name": "@wl-wo/wo-types", "module": "index.ts", "type": "module", - "version": "0.3.0", + "version": "1.0.0", "devDependencies": { "@types/bun": "latest" }, diff --git a/electron/types/types.d.ts b/electron/types/types.d.ts index 686f2a1..7b9f371 100644 --- a/electron/types/types.d.ts +++ b/electron/types/types.d.ts @@ -299,6 +299,18 @@ export interface Compositor { * Fires when Wayland clients (e.g. OBS, grim) capture the screen via wlr-screencopy. */ onScreencopyEvent(callback: (data: ScreencopyEvent) => void): Unsubscribe; + + /** + * Receive a SharedArrayBuffer for a given Wayland surface. + * Sent once when the SAB is first allocated or reallocated. + */ + onSurfaceSab(callback: (data: SurfaceSabData) => void): Unsubscribe; + + /** + * Subscribe to lightweight surface update signals (no pixel payload). + * After receiving this, read pixels directly from the previously-sent SAB. + */ + onSurfaceUpdate(callback: (data: SurfaceUpdateData) => void): Unsubscribe; } /** Alias for Compositor to match preload.ts imports */ @@ -316,6 +328,30 @@ export interface SurfaceBufferData { damageRects?: DamageRect[]; } +/** + * SharedArrayBuffer registration for a Wayland surface. + * Sent once per window when the SAB is first created or reallocated. + */ +export interface SurfaceSabData { + name: string; + width: number; + height: number; + stride: number; + sab: SharedArrayBuffer; +} + +/** + * Lightweight surface update signal (no pixel data). + * The renderer should read from the previously-received SAB. + */ +export interface SurfaceUpdateData { + name: string; + width: number; + height: number; + stride: number; + damageRects?: DamageRect[]; +} + /** * Notification data from the compositor */ diff --git a/src/backend.rs b/src/backend.rs index 250de1b..b667445 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -8,6 +8,7 @@ use smithay::utils::{Physical, Rectangle}; use crate::dmabuf::ElectronTexture; /// Common interface for rendering frames across different backends +#[allow(dead_code)] pub trait RenderBackend { /// Begin rendering a new frame with the given size and transform fn begin_frame(&mut self, width: i32, height: i32) -> Result<()>; @@ -36,12 +37,14 @@ pub trait RenderBackend { } /// Frame abstraction for rendering operations +#[allow(dead_code)] pub struct Frame { pub transform: smithay::utils::Transform, pub output_rect: Rectangle, } impl Frame { + #[allow(dead_code)] pub fn new(output_rect: Rectangle) -> Self { Frame { transform: smithay::utils::Transform::Normal, diff --git a/src/cursor.rs b/src/cursor.rs index 5a28c96..144b601 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -7,7 +7,7 @@ use smithay::backend::allocator::Fourcc; use smithay::backend::renderer::gles::GlesRenderer; use smithay::backend::renderer::gles::GlesTexture; use smithay::backend::renderer::ImportMem; -use smithay::utils::{Buffer as BufferCoord, Size}; +use smithay::utils::Size; use std::collections::HashMap; use tracing::{info, warn}; diff --git a/src/dmabuf.rs b/src/dmabuf.rs index fab5623..9145d95 100644 --- a/src/dmabuf.rs +++ b/src/dmabuf.rs @@ -48,7 +48,6 @@ impl WindowDmabufCache { /// Returns Ok(true) if pixels were successfully updated. #[allow(dead_code)] pub fn update_pixels(&mut self, pixels: &[u8]) -> Result<()> { - use std::os::unix::io::AsRawFd; use nix::unistd; // Seek to start @@ -209,6 +208,7 @@ pub fn export_dmabuf_for_client(texture: &ElectronTexture) -> Dmabuf { } /// Re-import a cached DMABUF frame into a renderer (used during rendering). +#[allow(dead_code)] pub fn reimport_cached_frame( renderer: &mut GlesRenderer, cached: &CachedDmabufFrame, @@ -277,8 +277,6 @@ pub fn export_texture_as_dmabuf( // For now, we'll read pixels and create a DMABUF from them // A more optimal approach would use EGLImage to back the DMABUF, but // this is simpler and still zero-copy at the IPC boundary - use smithay::utils::Rectangle; - // This would need a mutable reference, which we don't have // So instead, for each window we'll create DMABUF on-demand after pixelreads anyhow::bail!("texture export not directly supported; use create_temp_dmabuf_from_pixels after reading pixels") diff --git a/src/electron.rs b/src/electron.rs index c256dda..75b7c1b 100644 --- a/src/electron.rs +++ b/src/electron.rs @@ -54,6 +54,7 @@ pub const MAGIC_SCREENCOPY_EVENT: u32 = 0x574F5345; // "WOSE" — screencopy cap #[repr(C, packed)] #[derive(Clone, Copy, Debug)] +#[allow(dead_code)] pub struct PlaneInfoWire { pub offset: u32, pub stride: u32, @@ -329,7 +330,6 @@ impl ElectronClientConnection { // For each plane: fd(4) + offset(8) + stride(4) + modifier(8) // Note: We'll collect actual plane data from dmabuf handles iterator - let mut plane_idx = 0; for _handle in dmabuf.handles() { // fd: will be sent via ancillary data, so we put 0 as placeholder buf[off..off + 4].copy_from_slice(&0u32.to_le_bytes()); @@ -345,7 +345,6 @@ impl ElectronClientConnection { let modifier_val: u64 = format.modifier.into(); buf[off..off + 8].copy_from_slice(&modifier_val.to_le_bytes()); off += 8; - plane_idx += 1; } // Send metadata with file descriptors as ancillary data @@ -600,25 +599,39 @@ impl ElectronProcess { .env("WO_IPC_SOCKET", ipc_socket) .env("WO_WINDOW_CONFIG", &serialized) .env("WO_DRM_RENDER_NODE", render_node) - // Headless Ozone backend: Electron is used purely for offscreen - // rendering — it must not try to connect to a Wayland or X11 - // display server (there is none for it to connect to; *we* are - // the compositor). The headless platform lets Chromium's OSR - // paint pipeline work while the native wo_dmabuf module handles - // GPU buffer allocation via its own GBM/EGL context on the - // render node. - .arg("--ozone-platform=headless") + // Wayland Ozone platform: Electron connects to our own Wayland + // compositor (WAYLAND_DISPLAY is already set in the env) for + // GPU/EGL initialisation. This gives Chromium a proper + // wayland-egl display so hardware-accelerated rasterisation + // works. The BrowserWindow still uses `offscreen: true`, so + // no Wayland surfaces are mapped — the connection is used + // solely for the EGL context. + // + // The previous `--ozone-platform=headless` approach failed + // because headless Ozone has no EGL display, causing the GPU + // subprocess to crash with "gl=none,angle=none not found" and + // silently falling back to ~1 FPS software rasterisation. + .arg("--ozone-platform=wayland") .arg("--no-sandbox") .arg("--disable-gpu-sandbox") - // Use the *software* GL implementation inside Chromium for its - // own compositing (we do not need hardware-accelerated web - // content — the paint callback gives us BGRA bitmaps that the - // native module imports into a GBM BO). This avoids Chromium - // fighting with the compositor over the DRM render node. - .arg("--use-gl=swiftshader") - // Make sure OSR (offscreen) rendering actually produces - // paint callbacks at the requested frame rate. - .arg("--enable-features=Vulkan") + // Run GPU code in the main Electron process to avoid the + // separate GPU subprocess which adds IPC overhead and can + // fail to initialise independently. + .arg("--in-process-gpu") + // Use native EGL (provided by wayland-egl through the Ozone + // Wayland platform) for Chromium's compositor. + .arg("--use-gl=egl") + // Disable GPU vsync — we are rendering offscreen, there is + // no physical display to synchronise with. + .arg("--disable-gpu-vsync") + // Enable GPU-accelerated rasterization and zero-copy features + // so OSR paint callbacks deliver GPU-backed bitmaps instead + // of slow CPU readbacks. + .arg("--enable-features=CanvasOopRasterization,Vulkan,VaapiVideoDecoder,RawDraw,SharedArrayBuffer") + // Use multiple raster threads for parallelised tile painting. + .arg("--num-raster-threads=4") + // Don't throttle renderers that lose focus. + .arg("--disable-renderer-backgrounding") .stdin(Stdio::null()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) @@ -1253,7 +1266,7 @@ fn write_nonblocking(fd: RawFd, buf: &[u8]) -> Result<()> { return match send(fd, buf, MsgFlags::MSG_DONTWAIT) { Ok(n) if n == buf.len() => Ok(()), Ok(_) => Ok(()), // partial write — drop silently, stream may be corrupt - Err(nix::errno::Errno::EAGAIN) | Err(nix::errno::Errno::EWOULDBLOCK) => Ok(()), + Err(nix::errno::Errno::EAGAIN) => Ok(()), Err(e) => Err(anyhow::anyhow!("IPC non-blocking write: {e}")), }; } @@ -1280,7 +1293,7 @@ fn write_nonblocking(fd: RawFd, buf: &[u8]) -> Result<()> { "IPC partial non-blocking write: {n}/{} bytes sent despite space check", buf.len() )), - Err(nix::errno::Errno::EAGAIN) | Err(nix::errno::Errno::EWOULDBLOCK) => Ok(()), + Err(nix::errno::Errno::EAGAIN) => Ok(()), Err(e) => Err(anyhow::anyhow!("IPC non-blocking write: {e}")), } } diff --git a/src/handlers/input.rs b/src/handlers/input.rs index 3d814a2..1471306 100644 --- a/src/handlers/input.rs +++ b/src/handlers/input.rs @@ -436,7 +436,7 @@ impl WoState { let pos: Point = self.pointer_location; let under_surface = self.space.element_under(pos); - if let Some((window, surface_loc)) = under_surface { + if let Some((window, _surface_loc)) = under_surface { let surface_opt = if let Some(x11) = window.x11_surface() { x11.wl_surface() } else { diff --git a/src/handlers/screencopy.rs b/src/handlers/screencopy.rs index 94f27d5..4e0c4ee 100644 --- a/src/handlers/screencopy.rs +++ b/src/handlers/screencopy.rs @@ -3,7 +3,6 @@ /// Implements `zwlr_screencopy_manager_v1` allowing clients like grim, OBS, etc. /// to request frame copies of outputs or output regions. -use std::collections::HashMap; use std::sync::{Arc, Mutex}; use tracing::{debug, info, warn}; use wayland_protocols_wlr::screencopy::v1::server::{ @@ -12,7 +11,7 @@ use wayland_protocols_wlr::screencopy::v1::server::{ }; use wayland_server::{ Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, - protocol::{wl_buffer::WlBuffer, wl_output::WlOutput, wl_shm}, + protocol::{wl_buffer::WlBuffer, wl_shm}, }; use crate::state::WoState; diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 01700ef..ee2a392 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -50,6 +50,7 @@ impl XdgShellHandler for WoState { self.ssd_windows.remove(&oid); + #[allow(deprecated)] let window = Window::new(surface); self.all_windows.push(window.clone()); @@ -246,7 +247,7 @@ impl XdgShellHandler for WoState { .find(|w| w.toplevel().map(|t| t == &surface).unwrap_or(false)) .cloned(); - if let Some(window) = window { + if let Some(_window) = window { use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::State as XdgState; let (ow, oh) = (self.output_size.0 as i32, self.output_size.1 as i32); diff --git a/src/handlers/xwayland.rs b/src/handlers/xwayland.rs index 6cd6dfa..379fafd 100644 --- a/src/handlers/xwayland.rs +++ b/src/handlers/xwayland.rs @@ -255,6 +255,7 @@ impl XwmHandler for WoState { let (ow, oh) = (self.output_size.0 as i32, self.output_size.1 as i32); let content_h = (oh - y_offset).max(1); + #[allow(deprecated)] if let Err(e) = window.configure(Some(Rectangle::from_loc_and_size((0, y_offset), (ow, content_h)))) { warn!("XWayland: fullscreen configure failed: {e}"); } @@ -300,6 +301,7 @@ impl XwmHandler for WoState { let suggest_w = (ow * 2 / 3).max(640).min(1600); let suggest_h = (oh * 2 / 3).max(480).min(1000); + #[allow(deprecated)] if let Err(e) = window.configure(Some(Rectangle::from_loc_and_size((0, 0), (suggest_w, suggest_h)))) { warn!("XWayland: unfullscreen configure failed: {e}"); } diff --git a/src/main.rs b/src/main.rs index a1d65a4..e38b99a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ use gbm::Modifier; use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; use smithay::backend::allocator::{Fourcc, Slot}; -use smithay::backend::renderer::element::{Element, RenderElement}; +use smithay::backend::renderer::element::Element; use smithay::wayland::xdg_activation::{XdgActivationToken, XdgActivationTokenData}; use smithay::{ backend::{ @@ -770,8 +770,6 @@ fn render_frame(data: &mut EventLoopData) { use smithay::reexports::wayland_server::Resource; use smithay::wayland::compositor::{with_states, Damage, SurfaceAttributes}; use smithay::wayland::shm::with_buffer_contents; - use std::os::unix::io::AsRawFd; - use nix::sys::memfd::MFdFlags; use tracing::debug; let pid = std::process::id(); diff --git a/src/nested.rs b/src/nested.rs index 8e34c1b..6c489ff 100644 --- a/src/nested.rs +++ b/src/nested.rs @@ -5,7 +5,6 @@ use anyhow::{Context, Result}; use drm_fourcc::DrmFourcc; -use nix::sys::memfd::MFdFlags; use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; use smithay::{ backend::{ @@ -501,6 +500,12 @@ pub fn run_nested(config: Config) -> Result<()> { }; if last_render.elapsed() < adaptive_frame_time { + // Sleep for the remaining frame budget instead of busy-looping. + // Cap the sleep to avoid missing events for too long. + let remaining = adaptive_frame_time.saturating_sub(last_render.elapsed()); + if remaining > Duration::from_micros(500) { + std::thread::sleep(remaining.min(Duration::from_millis(2))); + } continue; } last_render = std::time::Instant::now(); @@ -1144,7 +1149,17 @@ pub fn run_nested(config: Config) -> Result<()> { warn!("render+submit+callbacks total took {}ms!", render_total_ms); } - std::thread::sleep(Duration::from_micros(100)); + // Sleep for the remaining frame budget to avoid burning CPU. + // The per-frame work above (dispatch + render + submit + callbacks) eats + // some of the budget; sleep the rest so the next iteration starts close + // to the target frame boundary. + let elapsed = loop_t0.elapsed(); + let target = if input_events_pending { Duration::from_millis(8) } else { frame_time }; + if elapsed < target { + let remaining = target - elapsed; + // Don't sleep more than 4ms to stay responsive to new Wayland/IPC events + std::thread::sleep(remaining.min(Duration::from_millis(4))); + } // Log if the entire loop iteration was slow. let total_loop_ms = loop_t0.elapsed().as_millis(); diff --git a/src/state.rs b/src/state.rs index 1498fff..366e0f9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -612,7 +612,7 @@ impl WoState { /// The actual forwarding is handled by process_input_event. pub fn forward_input_to_electron( &mut self, - event: &smithay::backend::input::InputEvent, + _event: &smithay::backend::input::InputEvent, ) { // The actual forwarding is done by process_input_event which is called separately // This is a no-op wrapper that exists for API compatibility with nested.rs diff --git a/whatever.txt b/whatever.txt index b64ded8..62aee9e 100644 --- a/whatever.txt +++ b/whatever.txt @@ -1,578 +1,446 @@ -2026-03-09T19:05:04.695426Z  INFO wo: wo compositor starting -2026-03-09T19:05:04.696005Z  INFO wo::config: Loading config from "/home/hellings/.config/wo/config.toml" -2026-03-09T19:05:04.696508Z  INFO wo: Running in DRM/KMS mode -2026-03-09T19:05:04.698340Z  INFO wo: LibSeat session created successfully -2026-03-09T19:05:04.698373Z  INFO wo: libseat environment seat=seat0 libseat_backend=logind -2026-03-09T19:05:04.698483Z  INFO wo: Session notifier registered for VT switching -2026-03-09T19:05:04.698605Z  INFO wo: Session is active, ready to open devices -2026-03-09T19:05:04.698634Z  INFO wo: Using seat: seat0 -2026-03-09T19:05:04.701686Z  INFO wo: trying DRM devices in priority order candidates=["/dev/dri/card1", "/dev/dri/card2"] -2026-03-09T19:05:04.701715Z  INFO wo: Opening DRM device: "/dev/dri/card1" -2026-03-09T19:05:04.702165Z  INFO wo: using GPU gpu=/dev/dri/card1 -2026-03-09T19:05:04.702199Z  WARN smithay::backend::drm::device::fd: Unable to become drm master, assuming unprivileged mode -2026-03-09T19:05:04.702984Z  INFO wo: DRM device created atomic=true -2026-03-09T19:05:05.166132Z  INFO wo::electron: IPC socket listening socket="/run/user/1000/wo-ipc.sock" -2026-03-09T19:05:05.166366Z  INFO wo: resolved render node card=/dev/dri/card1 render_node=/dev/dri/renderD128 -2026-03-09T19:05:05.166392Z  INFO wo::electron: Spawning Electron for window 'main' with config: WindowConfig { name: "main", url: Some("http://localhost:5173"), html: None, css: None, js: None, width: 2560, height: 1600, x: 0, y: 0, z_order: -1073741824, fps: 60, format: "ARGB8888", focusable: false, interactive: false, floating: false } -2026-03-09T19:05:05.166450Z  INFO wo::electron: Serialized config: {"name":"main","url":"http://localhost:5173","html":null,"css":null,"js":null,"width":2560,"height":1600,"x":0,"y":0,"z_order":-1073741824,"fps":60,"format":"ARGB8888","focusable":false,"interactive":false,"floating":false} -2026-03-09T19:05:05.166833Z  INFO wo::electron: spawned Electron process window=main pid=3988 render_node="/dev/dri/renderD128" -2026-03-09T19:05:05.166880Z  INFO wo: using swapchain format/modifiers from renderer swapchain_format=DrmFourcc(XR24) modifiers=14 -2026-03-09T19:05:05.229616Z  INFO wo: Wayland socket bound socket=wo-0 -2026-03-09T19:05:05.229850Z  INFO wo: Kickstarting VBlank-driven rendering -2026-03-09T19:05:05.229867Z  INFO wo: First render: output=2560x1600, windows=1, bg=Color32F([0.0, 0.0, 0.0, 1.0]) -2026-03-09T19:05:05.232882Z  INFO wo: render_frame #0: textures_rendered=0, gles_cache=0, windows=1 -2026-03-09T19:05:05.233523Z  INFO wo: Performing modeset commit (commit_pending=false, first_frame=true, textures=0) -2026-03-09T19:05:05.234462Z  INFO wo: First successful page flip (commit_pending=false) -[Wo] Electron starting, NODE_VERSION= v22.22.0 -[Wo] WO_WINDOW_CONFIG env = {"name":"main","url":"http://localhost:5173","html":null,"css":null,"js":null,"width":2560,"height":1600,"x":0,"y":0,"z_order":-1073741824,"fps":60,"format":"ARGB8888","focusable":false,"interactive":false,"floating":false} -[Wo] WINDOW_CONFIG parsed = {"name":"main","url":"http://localhost:5173","html":null,"css":null,"js":null,"width":2560,"height":1600,"x":0,"y":0,"z_order":-1073741824,"fps":60,"format":"ARGB8888","focusable":false,"interactive":false,"floating":false} -[Wo] App ready, CLIENT_MODE= false WO_STANDALONE= undefined -[Wo] Will connect to compositor -[Wo] Connecting to compositor IPC: /run/user/1000/wo-ipc.sock -[Wo] Portal UI bridge listening at /tmp/wo-portal-ui.sock -[Wo] IPC connected, sent HELLO message -2026-03-09T19:05:05.740058Z  INFO wo::electron: Electron window connected window=main -[Wo] DMABUF sender initialized -[Wo] Creating window -[Wo] createWindow starting -[Wo] preloadPath=/home/hellings/Documents/wo/electron/dist/preload.js -[Wo] BrowserWindow created, CLIENT_MODE=false offscreen=true -[Wo] setupIpcHandlers completed -[Wo] Loading content URL: http://localhost:5173 +warning: unused import: `Buffer as BufferCoord` + --> src/cursor.rs:10:22 + | +10 | use smithay::utils::{Buffer as BufferCoord, Size}; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default - VITE v8.0.0-beta.16 ready in 368 ms +warning: unused import: `std::os::unix::io::AsRawFd` + --> src/dmabuf.rs:51:13 + | +51 |warning: unused import: `Buffer as BufferCoord` + --> src/cursor.rs:10:22 + | +10 | use smithay::utils::{Buffer as BufferCoord, Size}; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default - ➜ Local: http://localhost:5173/ - ➜ Network: use --host to expose -[Wo] Content loaded from: http://localhost:5173 -[Wo] Page initialized, setting up rendering -[Wo] Setting up OSR rendering, FPS=60 -[Wo] createWindow completed successfully -[Wo] Window created successfully -[Wo] ERROR sending frame: Error: gbm_bo_create failed: all buffer creation methods exhausted -2026-03-09T19:05:06.078535Z  INFO wo::electron: received FRAME, sending ACK window=main seq=1 -2026-03-09T19:05:06.091525Z  INFO wo::electron: received FRAME, sending ACK window=main seq=2 -2026-03-09T19:05:06.115813Z  INFO wo::electron: received FRAME, sending ACK window=main seq=3 -2026-03-09T19:05:06.135055Z  INFO wo::electron: received FRAME, sending ACK window=main seq=4 -2026-03-09T19:05:06.158107Z  INFO wo::electron: received FRAME, sending ACK window=main seq=5 -2026-03-09T19:05:06.167813Z  INFO wo::electron: received FRAME, sending ACK window=main seq=6 -2026-03-09T19:05:06.185891Z  INFO wo::electron: received FRAME, sending ACK window=main seq=7 -2026-03-09T19:05:06.215319Z  INFO wo::electron: received FRAME, sending ACK window=main seq=8 -2026-03-09T19:05:06.229324Z  INFO wo::electron: received FRAME, sending ACK window=main seq=9 -[Wo] SKIPPED paint due to frame backpressure inFlight=2 -2026-03-09T19:05:06.253232Z  INFO wo::electron: received FRAME, sending ACK window=main seq=10 -2026-03-09T19:05:06.267600Z  INFO wo::electron: received FRAME, sending ACK window=main seq=11 -2026-03-09T19:05:06.291503Z  INFO wo::electron: received FRAME, sending ACK window=main seq=12 -2026-03-09T19:05:06.306499Z  INFO wo::electron: received FRAME, sending ACK window=main seq=13 -2026-03-09T19:05:06.326881Z  INFO wo::electron: received FRAME, sending ACK window=main seq=14 -2026-03-09T19:05:06.344775Z  INFO wo::electron: received FRAME, sending ACK window=main seq=15 -2026-03-09T19:05:06.359875Z  INFO wo::electron: received FRAME, sending ACK window=main seq=16 -2026-03-09T19:05:06.379902Z  INFO wo::electron: received FRAME, sending ACK window=main seq=17 -2026-03-09T19:05:06.396090Z  INFO wo::electron: received FRAME, sending ACK window=main seq=18 -2026-03-09T19:05:06.418605Z  INFO wo::electron: received FRAME, sending ACK window=main seq=19 -2026-03-09T19:05:06.433523Z  INFO wo::electron: received FRAME, sending ACK window=main seq=20 -2026-03-09T19:05:06.451557Z  INFO wo::electron: received FRAME, sending ACK window=main seq=21 -2026-03-09T19:05:06.468400Z  INFO wo::electron: received FRAME, sending ACK window=main seq=22 -2026-03-09T19:05:06.487784Z  INFO wo::electron: received FRAME, sending ACK window=main seq=23 -2026-03-09T19:05:06.508063Z  INFO wo::electron: received FRAME, sending ACK window=main seq=24 -2026-03-09T19:05:06.524830Z  INFO wo::electron: received FRAME, sending ACK window=main seq=25 -2026-03-09T19:05:06.540826Z  INFO wo::electron: received FRAME, sending ACK window=main seq=26 -2026-03-09T19:05:06.560716Z  INFO wo::electron: received FRAME, sending ACK window=main seq=27 -2026-03-09T19:05:06.577925Z  INFO wo::electron: received FRAME, sending ACK window=main seq=28 -2026-03-09T19:05:06.594433Z  INFO wo::electron: received FRAME, sending ACK window=main seq=29 -2026-03-09T19:05:06.613620Z  INFO wo::electron: received FRAME, sending ACK window=main seq=30 -2026-03-09T19:05:06.630279Z  INFO wo::electron: received FRAME, sending ACK window=main seq=31 -2026-03-09T19:05:06.646498Z  INFO wo::electron: received FRAME, sending ACK window=main seq=32 -2026-03-09T19:05:06.665370Z  INFO wo::electron: received FRAME, sending ACK window=main seq=33 -2026-03-09T19:05:06.681783Z  INFO wo::electron: received FRAME, sending ACK window=main seq=34 -2026-03-09T19:05:06.698679Z  INFO wo::electron: received FRAME, sending ACK window=main seq=35 -2026-03-09T19:05:06.717124Z  INFO wo::electron: received FRAME, sending ACK window=main seq=36 -2026-03-09T19:05:06.733661Z  INFO wo::electron: received FRAME, sending ACK window=main seq=37 -2026-03-09T19:05:06.750732Z  INFO wo::electron: received FRAME, sending ACK window=main seq=38 -2026-03-09T19:05:06.768398Z  INFO wo::electron: received FRAME, sending ACK window=main seq=39 -2026-03-09T19:05:06.786759Z  INFO wo::electron: received FRAME, sending ACK window=main seq=40 -2026-03-09T19:05:06.805011Z  INFO wo::electron: received FRAME, sending ACK window=main seq=41 -2026-03-09T19:05:06.820089Z  INFO wo::electron: received FRAME, sending ACK window=main seq=42 -2026-03-09T19:05:06.841008Z  INFO wo::electron: received FRAME, sending ACK window=main seq=43 -2026-03-09T19:05:06.858484Z  INFO wo::electron: received FRAME, sending ACK window=main seq=44 -2026-03-09T19:05:06.873345Z  INFO wo::electron: received FRAME, sending ACK window=main seq=45 -2026-03-09T19:05:06.895251Z  INFO wo::electron: received FRAME, sending ACK window=main seq=46 -2026-03-09T19:05:06.913572Z  INFO wo::electron: received FRAME, sending ACK window=main seq=47 -2026-03-09T19:05:06.928565Z  INFO wo::electron: received FRAME, sending ACK window=main seq=48 -2026-03-09T19:05:06.946778Z  INFO wo::electron: received FRAME, sending ACK window=main seq=49 -2026-03-09T19:05:06.963032Z  INFO wo::electron: received FRAME, sending ACK window=main seq=50 -2026-03-09T19:05:06.981865Z  INFO wo::electron: received FRAME, sending ACK window=main seq=51 -2026-03-09T19:05:07.003166Z  INFO wo::electron: received FRAME, sending ACK window=main seq=52 -2026-03-09T19:05:07.019432Z  INFO wo::electron: received FRAME, sending ACK window=main seq=53 -2026-03-09T19:05:07.036505Z  INFO wo::electron: received FRAME, sending ACK window=main seq=54 -2026-03-09T19:05:07.055273Z  INFO wo::electron: received FRAME, sending ACK window=main seq=55 -2026-03-09T19:05:07.071839Z  INFO wo::electron: received FRAME, sending ACK window=main seq=56 -2026-03-09T19:05:07.089251Z  INFO wo::electron: received FRAME, sending ACK window=main seq=57 -2026-03-09T19:05:07.107110Z  INFO wo::electron: received FRAME, sending ACK window=main seq=58 -2026-03-09T19:05:07.125533Z  INFO wo::electron: received FRAME, sending ACK window=main seq=59 -2026-03-09T19:05:07.143999Z  INFO wo::electron: received FRAME, sending ACK window=main seq=60 -2026-03-09T19:05:07.158213Z  INFO wo::electron: received FRAME, sending ACK window=main seq=61 -2026-03-09T19:05:07.180784Z  INFO wo::electron: received FRAME, sending ACK window=main seq=62 -2026-03-09T19:05:07.198923Z  INFO wo::electron: received FRAME, sending ACK window=main seq=63 -2026-03-09T19:05:07.214403Z  INFO wo::electron: received FRAME, sending ACK window=main seq=64 -2026-03-09T19:05:07.235149Z  INFO wo::electron: received FRAME, sending ACK window=main seq=65 -2026-03-09T19:05:07.253179Z  INFO wo::electron: received FRAME, sending ACK window=main seq=66 -2026-03-09T19:05:07.270568Z  INFO wo::electron: received FRAME, sending ACK window=main seq=67 -2026-03-09T19:05:07.285814Z  INFO wo::electron: received FRAME, sending ACK window=main seq=68 -2026-03-09T19:05:07.304510Z  INFO wo::electron: received FRAME, sending ACK window=main seq=69 -2026-03-09T19:05:07.319567Z  INFO wo::electron: received FRAME, sending ACK window=main seq=70 -2026-03-09T19:05:07.342323Z  INFO wo::electron: received FRAME, sending ACK window=main seq=71 -2026-03-09T19:05:07.361423Z  INFO wo::electron: received FRAME, sending ACK window=main seq=72 -2026-03-09T19:05:07.371420Z  INFO wo: First DMABUF imported for 'main' (2560x1600, total_textures=1) -2026-03-09T19:05:07.371855Z  INFO wo: Spawned portal service: "/home/hellings/Documents/wo/target/debug/wo-portal" -2026-03-09T19:05:07.373260Z  INFO wo: render_frame #1: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.374775Z  INFO wo_portal: xdg-desktop-portal-wo starting -2026-03-09T19:05:07.374902Z  INFO wo::config: Loading config from "/home/hellings/.config/wo/config.toml" -2026-03-09T19:05:07.376323Z  INFO wo::electron: received FRAME, sending ACK window=main seq=73 -2026-03-09T19:05:07.378710Z  INFO wo_portal: Portal service registered as org.freedesktop.impl.portal.desktop.wo -2026-03-09T19:05:07.378751Z  INFO wo_portal: Compositor IPC socket: /run/user/1000/wo-ipc.sock -2026-03-09T19:05:07.378764Z  INFO wo_portal: Listening for screen capture requests... -2026-03-09T19:05:07.385634Z  INFO wo: render_frame #2: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.398533Z  INFO wo: render_frame #3: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.398557Z  INFO wo::electron: received FRAME, sending ACK window=main seq=74 -2026-03-09T19:05:07.408768Z  INFO wo: render_frame #4: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.414881Z  INFO wo::electron: received FRAME, sending ACK window=main seq=75 -2026-03-09T19:05:07.424981Z  INFO wo: render_frame #5: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.429527Z  INFO wo::electron: received FRAME, sending ACK window=main seq=76 -2026-03-09T19:05:07.441735Z  INFO wo: render_frame #6: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.449042Z  INFO wo::electron: received FRAME, sending ACK window=main seq=77 -2026-03-09T19:05:07.458571Z  INFO wo: render_frame #7: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.467037Z  INFO wo::electron: received FRAME, sending ACK window=main seq=78 -2026-03-09T19:05:07.475323Z  INFO wo: render_frame #8: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.482430Z  INFO wo::electron: received FRAME, sending ACK window=main seq=79 -2026-03-09T19:05:07.492752Z  INFO wo: render_frame #9: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.506061Z  INFO wo::electron: received FRAME, sending ACK window=main seq=80 -2026-03-09T19:05:07.510418Z  INFO wo: render_frame #10: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.519103Z  INFO wo::electron: received FRAME, sending ACK window=main seq=81 -2026-03-09T19:05:07.527895Z  INFO wo: render_frame #11: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.541595Z  INFO wo: render_frame #12: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.543444Z  INFO wo::electron: received FRAME, sending ACK window=main seq=82 -2026-03-09T19:05:07.558040Z  INFO wo::electron: received FRAME, sending ACK window=main seq=83 -2026-03-09T19:05:07.558618Z  INFO wo: render_frame #13: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.575042Z  INFO wo: render_frame #14: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.580487Z  INFO wo::electron: received FRAME, sending ACK window=main seq=84 -2026-03-09T19:05:07.592113Z  INFO wo: render_frame #15: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.598258Z  INFO wo::electron: received FRAME, sending ACK window=main seq=85 -2026-03-09T19:05:07.608849Z  INFO wo: render_frame #16: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.615962Z  INFO wo::electron: received FRAME, sending ACK window=main seq=86 -2026-03-09T19:05:07.626569Z  INFO wo: render_frame #17: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.636117Z  INFO wo::electron: received FRAME, sending ACK window=main seq=87 -2026-03-09T19:05:07.642524Z  INFO wo: render_frame #18: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.655159Z  INFO wo::electron: received FRAME, sending ACK window=main seq=88 -2026-03-09T19:05:07.659312Z  INFO wo: render_frame #19: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.671247Z  INFO wo::electron: received FRAME, sending ACK window=main seq=89 -2026-03-09T19:05:07.675380Z  INFO wo: render_frame #20: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.691736Z  INFO wo: render_frame #21: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.695107Z  INFO wo::electron: received FRAME, sending ACK window=main seq=90 -2026-03-09T19:05:07.708598Z  INFO wo: render_frame #22: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.710449Z  INFO wo::electron: received FRAME, sending ACK window=main seq=91 -2026-03-09T19:05:07.725305Z  INFO wo: render_frame #23: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.729455Z  INFO wo::electron: received FRAME, sending ACK window=main seq=92 -2026-03-09T19:05:07.741999Z  INFO wo: render_frame #24: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.742543Z  INFO wo::electron: received FRAME, sending ACK window=main seq=93 -2026-03-09T19:05:07.759094Z  INFO wo: render_frame #25: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.767284Z  INFO wo::electron: received FRAME, sending ACK window=main seq=94 -2026-03-09T19:05:07.775382Z  INFO wo: render_frame #26: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.781747Z  INFO wo::electron: received FRAME, sending ACK window=main seq=95 -2026-03-09T19:05:07.792691Z  INFO wo: render_frame #27: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.804420Z  INFO wo::electron: received FRAME, sending ACK window=main seq=96 -2026-03-09T19:05:07.808976Z  INFO wo: render_frame #28: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.819069Z  INFO wo::electron: received FRAME, sending ACK window=main seq=97 -2026-03-09T19:05:07.826573Z  INFO wo: render_frame #29: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.839607Z  INFO wo::electron: received FRAME, sending ACK window=main seq=98 -2026-03-09T19:05:07.842139Z  INFO wo: render_frame #30: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.852177Z  INFO wo::electron: received FRAME, sending ACK window=main seq=99 -2026-03-09T19:05:07.859664Z  INFO wo: render_frame #31: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.873405Z  INFO wo::electron: received FRAME, sending ACK window=main seq=100 -2026-03-09T19:05:07.875306Z  INFO wo: render_frame #32: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.889434Z  INFO wo::electron: received FRAME, sending ACK window=main seq=101 -2026-03-09T19:05:07.892493Z  INFO wo: render_frame #33: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.910140Z  INFO wo::electron: received FRAME, sending ACK window=main seq=102 -2026-03-09T19:05:07.911559Z  INFO wo: render_frame #34: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.925015Z  INFO wo::electron: received FRAME, sending ACK window=main seq=103 -2026-03-09T19:05:07.933414Z  INFO wo: render_frame #35: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.945412Z  INFO wo::electron: received FRAME, sending ACK window=main seq=104 -2026-03-09T19:05:07.955034Z  INFO wo: render_frame #36: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.964758Z  INFO wo::electron: received FRAME, sending ACK window=main seq=105 -2026-03-09T19:05:07.967527Z  INFO wo: render_frame #37: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.975407Z  INFO wo: render_frame #38: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.977139Z  INFO wo::electron: received FRAME, sending ACK window=main seq=106 -2026-03-09T19:05:07.992236Z  INFO wo: render_frame #39: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:07.994447Z  INFO wo::electron: received FRAME, sending ACK window=main seq=107 -2026-03-09T19:05:08.009142Z  INFO wo: render_frame #40: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.012236Z  INFO wo::electron: received FRAME, sending ACK window=main seq=108 -2026-03-09T19:05:08.025700Z  INFO wo: render_frame #41: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.031393Z  INFO wo::electron: received FRAME, sending ACK window=main seq=109 -2026-03-09T19:05:08.042188Z  INFO wo: render_frame #42: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.043748Z  INFO wo::electron: received FRAME, sending ACK window=main seq=110 -2026-03-09T19:05:08.060693Z  INFO wo: render_frame #43: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.072965Z  INFO wo::electron: received FRAME, sending ACK window=main seq=111 -2026-03-09T19:05:08.075576Z  INFO wo: render_frame #44: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.085621Z  INFO wo::electron: received FRAME, sending ACK window=main seq=112 -2026-03-09T19:05:08.092495Z  INFO wo: render_frame #45: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.102376Z  INFO wo::electron: received FRAME, sending ACK window=main seq=113 -2026-03-09T19:05:08.110177Z  INFO wo: render_frame #46: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.119066Z  INFO wo::electron: received FRAME, sending ACK window=main seq=114 -2026-03-09T19:05:08.126233Z  INFO wo: render_frame #47: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.135287Z  INFO wo::electron: received FRAME, sending ACK window=main seq=115 -2026-03-09T19:05:08.142536Z  INFO wo: render_frame #48: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.150631Z  INFO wo::electron: received FRAME, sending ACK window=main seq=116 -2026-03-09T19:05:08.159303Z  INFO wo: render_frame #49: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.171184Z  INFO wo::electron: received FRAME, sending ACK window=main seq=117 -2026-03-09T19:05:08.175739Z  INFO wo: render_frame #50: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.190803Z  INFO wo::electron: received FRAME, sending ACK window=main seq=118 -2026-03-09T19:05:08.192719Z  INFO wo: render_frame #51: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.209300Z  INFO wo: render_frame #52: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.211910Z  INFO wo::electron: received FRAME, sending ACK window=main seq=119 -2026-03-09T19:05:08.226057Z  INFO wo: render_frame #53: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.226385Z  INFO wo::electron: received FRAME, sending ACK window=main seq=120 -2026-03-09T19:05:08.241468Z  INFO wo::electron: received FRAME, sending ACK window=main seq=121 -2026-03-09T19:05:08.242524Z  INFO wo: render_frame #54: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.259309Z  INFO wo: render_frame #55: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.259672Z  INFO wo::electron: received FRAME, sending ACK window=main seq=122 -2026-03-09T19:05:08.275543Z  INFO wo::electron: received FRAME, sending ACK window=main seq=123 -2026-03-09T19:05:08.275965Z  INFO wo: render_frame #56: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.290724Z  INFO wo::electron: received FRAME, sending ACK window=main seq=124 -2026-03-09T19:05:08.293454Z  INFO wo: render_frame #57: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.308995Z  INFO wo: render_frame #58: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.311290Z  INFO wo::electron: received FRAME, sending ACK window=main seq=125 -2026-03-09T19:05:08.326174Z  INFO wo: render_frame #59: textures_rendered=1, gles_cache=1, windows=1 -2026-03-09T19:05:08.332259Z  INFO wo::electron: received FRAME, sending ACK window=main seq=126 -2026-03-09T19:05:08.347320Z  INFO wo::electron: received FRAME, sending ACK window=main seq=127 -2026-03-09T19:05:08.367845Z  INFO wo::electron: received FRAME, sending ACK window=main seq=128 -2026-03-09T19:05:08.386642Z  INFO wo::electron: received FRAME, sending ACK window=main seq=129 -2026-03-09T19:05:08.408885Z  INFO wo::electron: received FRAME, sending ACK window=main seq=130 -2026-03-09T19:05:08.428166Z  INFO wo::electron: received FRAME, sending ACK window=main seq=131 -2026-03-09T19:05:08.446778Z  INFO wo::electron: received FRAME, sending ACK window=main seq=132 -2026-03-09T19:05:08.461172Z  INFO wo::electron: received FRAME, sending ACK window=main seq=133 -2026-03-09T19:05:08.484331Z  INFO wo::electron: received FRAME, sending ACK window=main seq=134 -2026-03-09T19:05:08.506797Z  INFO wo::electron: received FRAME, sending ACK window=main seq=135 -2026-03-09T19:05:08.527019Z  INFO wo::electron: received FRAME, sending ACK window=main seq=136 -2026-03-09T19:05:08.544423Z  INFO wo::electron: received FRAME, sending ACK window=main seq=137 -2026-03-09T19:05:08.565329Z  INFO wo::electron: received FRAME, sending ACK window=main seq=138 -2026-03-09T19:05:08.583494Z  INFO wo::electron: received FRAME, sending ACK window=main seq=139 -2026-03-09T19:05:08.605203Z  INFO wo::electron: received FRAME, sending ACK window=main seq=140 -2026-03-09T19:05:08.757098Z  INFO wo::electron: received FRAME, sending ACK window=main seq=141 -2026-03-09T19:05:08.776883Z  INFO wo::electron: received FRAME, sending ACK window=main seq=142 -2026-03-09T19:05:08.797115Z  INFO wo::electron: received FRAME, sending ACK window=main seq=143 -2026-03-09T19:05:08.817753Z  INFO wo::electron: received FRAME, sending ACK window=main seq=144 -2026-03-09T19:05:08.837605Z  INFO wo::electron: received FRAME, sending ACK window=main seq=145 -[Wo] Load timeout, continuing -2026-03-09T19:05:08.858544Z  INFO wo::electron: received FRAME, sending ACK window=main seq=146 -2026-03-09T19:05:08.879656Z  INFO wo::electron: received FRAME, sending ACK window=main seq=147 -2026-03-09T19:05:08.896826Z  INFO wo::electron: received FRAME, sending ACK window=main seq=148 -2026-03-09T19:05:08.917898Z  INFO wo::electron: received FRAME, sending ACK window=main seq=149 -2026-03-09T19:05:08.937260Z  INFO wo::electron: received FRAME, sending ACK window=main seq=150 -2026-03-09T19:05:08.958668Z  INFO wo::electron: received FRAME, sending ACK window=main seq=151 -2026-03-09T19:05:08.974782Z  INFO wo::electron: received FRAME, sending ACK window=main seq=152 -2026-03-09T19:05:09.000150Z  INFO wo::electron: received FRAME, sending ACK window=main seq=153 -2026-03-09T19:05:09.016152Z  INFO wo::electron: received FRAME, sending ACK window=main seq=154 -2026-03-09T19:05:09.035956Z  INFO wo::electron: received FRAME, sending ACK window=main seq=155 -2026-03-09T19:05:09.055953Z  INFO wo::electron: received FRAME, sending ACK window=main seq=156 -2026-03-09T19:05:09.070892Z  INFO wo::electron: received FRAME, sending ACK window=main seq=157 -2026-03-09T19:05:09.087604Z  INFO wo::electron: received FRAME, sending ACK window=main seq=158 -2026-03-09T19:05:09.112960Z  INFO wo::electron: received FRAME, sending ACK window=main seq=159 -2026-03-09T19:05:09.143908Z  INFO wo::electron: received FRAME, sending ACK window=main seq=160 -2026-03-09T19:05:09.158543Z  INFO wo::electron: received FRAME, sending ACK window=main seq=161 -2026-03-09T19:05:09.181403Z  INFO wo::electron: received FRAME, sending ACK window=main seq=162 -2026-03-09T19:05:09.199534Z  INFO wo::electron: received FRAME, sending ACK window=main seq=163 -2026-03-09T19:05:09.215902Z  INFO wo::electron: received FRAME, sending ACK window=main seq=164 -2026-03-09T19:05:09.231086Z  INFO wo::electron: received FRAME, sending ACK window=main seq=165 -2026-03-09T19:05:09.247916Z  INFO wo::electron: received FRAME, sending ACK window=main seq=166 -2026-03-09T19:05:09.263202Z  INFO wo::electron: received FRAME, sending ACK window=main seq=167 -2026-03-09T19:05:09.279943Z  INFO wo::electron: received FRAME, sending ACK window=main seq=168 -2026-03-09T19:05:09.302232Z  INFO wo::electron: received FRAME, sending ACK window=main seq=169 -2026-03-09T19:05:09.322814Z  INFO wo::electron: received FRAME, sending ACK window=main seq=170 -2026-03-09T19:05:09.335478Z  INFO wo::electron: received FRAME, sending ACK window=main seq=171 -2026-03-09T19:05:09.358890Z  INFO wo::electron: received FRAME, sending ACK window=main seq=172 -2026-03-09T19:05:09.381262Z  INFO wo::electron: received FRAME, sending ACK window=main seq=173 -2026-03-09T19:05:09.395508Z  INFO wo::electron: received FRAME, sending ACK window=main seq=174 -2026-03-09T19:05:09.417419Z  INFO wo::electron: received FRAME, sending ACK window=main seq=175 -2026-03-09T19:05:09.433241Z  INFO wo::electron: received FRAME, sending ACK window=main seq=176 -2026-03-09T19:05:09.449443Z  INFO wo::electron: received FRAME, sending ACK window=main seq=177 -2026-03-09T19:05:09.464904Z  INFO wo::electron: received FRAME, sending ACK window=main seq=178 -2026-03-09T19:05:09.481937Z  INFO wo::electron: received FRAME, sending ACK window=main seq=179 -2026-03-09T19:05:09.497275Z  INFO wo::electron: received FRAME, sending ACK window=main seq=180 -2026-03-09T19:05:09.514493Z  INFO wo::electron: received FRAME, sending ACK window=main seq=181 -2026-03-09T19:05:09.537129Z  INFO wo::electron: received FRAME, sending ACK window=main seq=182 -2026-03-09T19:05:09.551380Z  INFO wo::electron: received FRAME, sending ACK window=main seq=183 -2026-03-09T19:05:09.574504Z  INFO wo::electron: received FRAME, sending ACK window=main seq=184 -2026-03-09T19:05:09.588673Z  INFO wo::electron: received FRAME, sending ACK window=main seq=185 -2026-03-09T19:05:09.610532Z  INFO wo::electron: received FRAME, sending ACK window=main seq=186 -2026-03-09T19:05:09.626399Z  INFO wo::electron: received FRAME, sending ACK window=main seq=187 -2026-03-09T19:05:09.643221Z  INFO wo::electron: received FRAME, sending ACK window=main seq=188 -2026-03-09T19:05:09.656890Z  INFO wo::electron: received FRAME, sending ACK window=main seq=189 -2026-03-09T19:05:09.676851Z  INFO wo::electron: received FRAME, sending ACK window=main seq=190 -2026-03-09T19:05:09.689396Z  INFO wo::electron: received FRAME, sending ACK window=main seq=191 -2026-03-09T19:05:09.705067Z  INFO wo::electron: received FRAME, sending ACK window=main seq=192 -2026-03-09T19:05:09.727534Z  INFO wo::electron: received FRAME, sending ACK window=main seq=193 -2026-03-09T19:05:09.749999Z  INFO wo::electron: received FRAME, sending ACK window=main seq=194 -2026-03-09T19:05:09.765554Z  INFO wo::electron: received FRAME, sending ACK window=main seq=195 -2026-03-09T19:05:09.784934Z  INFO wo::electron: received FRAME, sending ACK window=main seq=196 -2026-03-09T19:05:09.802931Z  INFO wo::electron: received FRAME, sending ACK window=main seq=197 -2026-03-09T19:05:09.818907Z  INFO wo::electron: received FRAME, sending ACK window=main seq=198 -2026-03-09T19:05:09.835515Z  INFO wo::electron: received FRAME, sending ACK window=main seq=199 -2026-03-09T19:05:09.858681Z  INFO wo::electron: received FRAME, sending ACK window=main seq=200 -2026-03-09T19:05:09.875952Z  INFO wo::electron: received FRAME, sending ACK window=main seq=201 -2026-03-09T19:05:09.894321Z  INFO wo::electron: received FRAME, sending ACK window=main seq=202 -2026-03-09T19:05:09.916169Z  INFO wo::electron: received FRAME, sending ACK window=main seq=203 -2026-03-09T19:05:09.928981Z  INFO wo::electron: received FRAME, sending ACK window=main seq=204 -2026-03-09T19:05:09.952677Z  INFO wo::electron: received FRAME, sending ACK window=main seq=205 -2026-03-09T19:05:09.967260Z  INFO wo::electron: received FRAME, sending ACK window=main seq=206 -2026-03-09T19:05:09.990725Z  INFO wo::electron: received FRAME, sending ACK window=main seq=207 -2026-03-09T19:05:10.006487Z  INFO wo::electron: received FRAME, sending ACK window=main seq=208 -2026-03-09T19:05:10.021208Z  INFO wo::electron: received FRAME, sending ACK window=main seq=209 -2026-03-09T19:05:10.035614Z  INFO wo::electron: received FRAME, sending ACK window=main seq=210 -2026-03-09T19:05:10.059276Z  INFO wo::electron: received FRAME, sending ACK window=main seq=211 -2026-03-09T19:05:10.077026Z  INFO wo::electron: received FRAME, sending ACK window=main seq=212 -2026-03-09T19:05:10.095038Z  INFO wo::electron: received FRAME, sending ACK window=main seq=213 -2026-03-09T19:05:10.107985Z  INFO wo::electron: received FRAME, sending ACK window=main seq=214 -2026-03-09T19:05:10.133539Z  INFO wo::electron: received FRAME, sending ACK window=main seq=215 -2026-03-09T19:05:10.149316Z  INFO wo::electron: received FRAME, sending ACK window=main seq=216 -2026-03-09T19:05:10.168667Z  INFO wo::electron: received FRAME, sending ACK window=main seq=217 -2026-03-09T19:05:10.185506Z  INFO wo::electron: received FRAME, sending ACK window=main seq=218 -2026-03-09T19:05:10.201283Z  INFO wo::electron: received FRAME, sending ACK window=main seq=219 -2026-03-09T19:05:10.220526Z  INFO wo::electron: received FRAME, sending ACK window=main seq=220 -2026-03-09T19:05:10.239132Z  INFO wo::electron: received FRAME, sending ACK window=main seq=221 -2026-03-09T19:05:10.254284Z  INFO wo::electron: received FRAME, sending ACK window=main seq=222 -2026-03-09T19:05:10.277162Z  INFO wo::electron: received FRAME, sending ACK window=main seq=223 -2026-03-09T19:05:10.291921Z  INFO wo::electron: received FRAME, sending ACK window=main seq=224 -2026-03-09T19:05:10.314433Z  INFO wo::electron: received FRAME, sending ACK window=main seq=225 -2026-03-09T19:05:10.328169Z  INFO wo::electron: received FRAME, sending ACK window=main seq=226 -2026-03-09T19:05:10.347406Z  INFO wo::electron: received FRAME, sending ACK window=main seq=227 -2026-03-09T19:05:10.360267Z  INFO wo::electron: received FRAME, sending ACK window=main seq=228 -2026-03-09T19:05:10.388525Z  INFO wo::electron: received FRAME, sending ACK window=main seq=229 -2026-03-09T19:05:10.402887Z  INFO wo::electron: received FRAME, sending ACK window=main seq=230 -2026-03-09T19:05:10.425009Z  INFO wo::electron: received FRAME, sending ACK window=main seq=231 -2026-03-09T19:05:10.446980Z  INFO wo::electron: received FRAME, sending ACK window=main seq=232 -2026-03-09T19:05:10.462289Z  INFO wo::electron: received FRAME, sending ACK window=main seq=233 -2026-03-09T19:05:10.482716Z  INFO wo::electron: received FRAME, sending ACK window=main seq=234 -2026-03-09T19:05:10.499518Z  INFO wo::electron: received FRAME, sending ACK window=main seq=235 -2026-03-09T19:05:10.519049Z  INFO wo::electron: received FRAME, sending ACK window=main seq=236 -2026-03-09T19:05:10.531616Z  INFO wo::electron: received FRAME, sending ACK window=main seq=237 -2026-03-09T19:05:10.554883Z  INFO wo::electron: received FRAME, sending ACK window=main seq=238 -2026-03-09T19:05:10.568809Z  INFO wo::electron: received FRAME, sending ACK window=main seq=239 -2026-03-09T19:05:10.588641Z  INFO wo::electron: received FRAME, sending ACK window=main seq=240 -2026-03-09T19:05:10.607131Z  INFO wo::electron: received FRAME, sending ACK window=main seq=241 -2026-03-09T19:05:10.625324Z  INFO wo::electron: received FRAME, sending ACK window=main seq=242 -2026-03-09T19:05:10.642669Z  INFO wo::electron: received FRAME, sending ACK window=main seq=243 -2026-03-09T19:05:10.660453Z  INFO wo::electron: received FRAME, sending ACK window=main seq=244 -2026-03-09T19:05:10.674928Z  INFO wo::electron: received FRAME, sending ACK window=main seq=245 -2026-03-09T19:05:10.696541Z  INFO wo::electron: received FRAME, sending ACK window=main seq=246 -2026-03-09T19:05:10.715069Z  INFO wo::electron: received FRAME, sending ACK window=main seq=247 -2026-03-09T19:05:10.729069Z  INFO wo::electron: received FRAME, sending ACK window=main seq=248 -2026-03-09T19:05:10.749994Z  INFO wo::electron: received FRAME, sending ACK window=main seq=249 -2026-03-09T19:05:10.768351Z  INFO wo::electron: received FRAME, sending ACK window=main seq=250 -2026-03-09T19:05:10.785358Z  INFO wo::electron: received FRAME, sending ACK window=main seq=251 -2026-03-09T19:05:10.805945Z  INFO wo::electron: received FRAME, sending ACK window=main seq=252 -2026-03-09T19:05:10.824142Z  INFO wo::electron: received FRAME, sending ACK window=main seq=253 -2026-03-09T19:05:10.840256Z  INFO wo::electron: received FRAME, sending ACK window=main seq=254 -2026-03-09T19:05:10.860929Z  INFO wo::electron: received FRAME, sending ACK window=main seq=255 -2026-03-09T19:05:10.879418Z  INFO wo::electron: received FRAME, sending ACK window=main seq=256 -2026-03-09T19:05:10.893761Z  INFO wo::electron: received FRAME, sending ACK window=main seq=257 -2026-03-09T19:05:10.915569Z  INFO wo::electron: received FRAME, sending ACK window=main seq=258 -2026-03-09T19:05:10.933922Z  INFO wo::electron: received FRAME, sending ACK window=main seq=259 -2026-03-09T19:05:10.948233Z  INFO wo::electron: received FRAME, sending ACK window=main seq=260 -2026-03-09T19:05:10.969982Z  INFO wo::electron: received FRAME, sending ACK window=main seq=261 -2026-03-09T19:05:10.988772Z  INFO wo::electron: received FRAME, sending ACK window=main seq=262 -2026-03-09T19:05:11.003425Z  INFO wo::electron: received FRAME, sending ACK window=main seq=263 -2026-03-09T19:05:11.024974Z  INFO wo::electron: received FRAME, sending ACK window=main seq=264 -2026-03-09T19:05:11.043851Z  INFO wo::electron: received FRAME, sending ACK window=main seq=265 -2026-03-09T19:05:11.057928Z  INFO wo::electron: received FRAME, sending ACK window=main seq=266 -2026-03-09T19:05:11.079831Z  INFO wo::electron: received FRAME, sending ACK window=main seq=267 -2026-03-09T19:05:11.098666Z  INFO wo::electron: received FRAME, sending ACK window=main seq=268 -2026-03-09T19:05:11.113216Z  INFO wo::electron: received FRAME, sending ACK window=main seq=269 -2026-03-09T19:05:11.134778Z  INFO wo::electron: received FRAME, sending ACK window=main seq=270 -2026-03-09T19:05:11.153050Z  INFO wo::electron: received FRAME, sending ACK window=main seq=271 -2026-03-09T19:05:11.167202Z  INFO wo::electron: received FRAME, sending ACK window=main seq=272 -2026-03-09T19:05:11.188755Z  INFO wo::electron: received FRAME, sending ACK window=main seq=273 -2026-03-09T19:05:11.207161Z  INFO wo::electron: received FRAME, sending ACK window=main seq=274 -2026-03-09T19:05:11.221273Z  INFO wo::electron: received FRAME, sending ACK window=main seq=275 -2026-03-09T19:05:11.242745Z  INFO wo::electron: received FRAME, sending ACK window=main seq=276 -2026-03-09T19:05:11.261659Z  INFO wo::electron: received FRAME, sending ACK window=main seq=277 -2026-03-09T19:05:11.275941Z  INFO wo::electron: received FRAME, sending ACK window=main seq=278 -2026-03-09T19:05:11.297677Z  INFO wo::electron: received FRAME, sending ACK window=main seq=279 -2026-03-09T19:05:11.316634Z  INFO wo::electron: received FRAME, sending ACK window=main seq=280 -2026-03-09T19:05:11.331408Z  INFO wo::electron: received FRAME, sending ACK window=main seq=281 -2026-03-09T19:05:11.351880Z  INFO wo::electron: received FRAME, sending ACK window=main seq=282 -2026-03-09T19:05:11.370290Z  INFO wo::electron: received FRAME, sending ACK window=main seq=283 -2026-03-09T19:05:11.384324Z  INFO wo::electron: received FRAME, sending ACK window=main seq=284 -2026-03-09T19:05:11.405958Z  INFO wo::electron: received FRAME, sending ACK window=main seq=285 -2026-03-09T19:05:11.424713Z  INFO wo::electron: received FRAME, sending ACK window=main seq=286 -2026-03-09T19:05:11.439068Z  INFO wo::electron: received FRAME, sending ACK window=main seq=287 -2026-03-09T19:05:11.460789Z  INFO wo::electron: received FRAME, sending ACK window=main seq=288 -2026-03-09T19:05:11.479149Z  INFO wo::electron: received FRAME, sending ACK window=main seq=289 -2026-03-09T19:05:11.493560Z  INFO wo::electron: received FRAME, sending ACK window=main seq=290 -2026-03-09T19:05:11.514261Z  INFO wo::electron: received FRAME, sending ACK window=main seq=291 -2026-03-09T19:05:11.532751Z  INFO wo::electron: received FRAME, sending ACK window=main seq=292 -2026-03-09T19:05:11.546625Z  INFO wo::electron: received FRAME, sending ACK window=main seq=293 -2026-03-09T19:05:11.568310Z  INFO wo::electron: received FRAME, sending ACK window=main seq=294 -2026-03-09T19:05:11.587304Z  INFO wo::electron: received FRAME, sending ACK window=main seq=295 -2026-03-09T19:05:11.602688Z  INFO wo::electron: received FRAME, sending ACK window=main seq=296 -2026-03-09T19:05:11.622009Z  INFO wo::electron: received FRAME, sending ACK window=main seq=297 -2026-03-09T19:05:11.640316Z  INFO wo::electron: received FRAME, sending ACK window=main seq=298 -2026-03-09T19:05:11.656712Z  INFO wo::electron: received FRAME, sending ACK window=main seq=299 -2026-03-09T19:05:11.677363Z  INFO wo::electron: received FRAME, sending ACK window=main seq=300 -2026-03-09T19:05:11.696314Z  INFO wo::electron: received FRAME, sending ACK window=main seq=301 -2026-03-09T19:05:11.710829Z  INFO wo::electron: received FRAME, sending ACK window=main seq=302 -2026-03-09T19:05:11.731520Z  INFO wo::electron: received FRAME, sending ACK window=main seq=303 -2026-03-09T19:05:11.749776Z  INFO wo::electron: received FRAME, sending ACK window=main seq=304 -2026-03-09T19:05:11.764112Z  INFO wo::electron: received FRAME, sending ACK window=main seq=305 -2026-03-09T19:05:11.785723Z  INFO wo::electron: received FRAME, sending ACK window=main seq=306 -2026-03-09T19:05:11.804143Z  INFO wo::electron: received FRAME, sending ACK window=main seq=307 -2026-03-09T19:05:11.818835Z  INFO wo::electron: received FRAME, sending ACK window=main seq=308 -2026-03-09T19:05:11.839330Z  INFO wo::electron: received FRAME, sending ACK window=main seq=309 -2026-03-09T19:05:11.857515Z  INFO wo::electron: received FRAME, sending ACK window=main seq=310 -2026-03-09T19:05:11.871818Z  INFO wo::electron: received FRAME, sending ACK window=main seq=311 -2026-03-09T19:05:11.893550Z  INFO wo::electron: received FRAME, sending ACK window=main seq=312 -2026-03-09T19:05:11.911740Z  INFO wo::electron: received FRAME, sending ACK window=main seq=313 -2026-03-09T19:05:11.926315Z  INFO wo::electron: received FRAME, sending ACK window=main seq=314 -2026-03-09T19:05:11.947679Z  INFO wo::electron: received FRAME, sending ACK window=main seq=315 -2026-03-09T19:05:11.966154Z  INFO wo::electron: received FRAME, sending ACK window=main seq=316 -2026-03-09T19:05:11.981056Z  INFO wo::electron: received FRAME, sending ACK window=main seq=317 -2026-03-09T19:05:12.001704Z  INFO wo::electron: received FRAME, sending ACK window=main seq=318 -2026-03-09T19:05:12.019779Z  INFO wo::electron: received FRAME, sending ACK window=main seq=319 -2026-03-09T19:05:12.034249Z  INFO wo::electron: received FRAME, sending ACK window=main seq=320 -2026-03-09T19:05:12.055935Z  INFO wo::electron: received FRAME, sending ACK window=main seq=321 -2026-03-09T19:05:12.074940Z  INFO wo::electron: received FRAME, sending ACK window=main seq=322 -2026-03-09T19:05:12.089564Z  INFO wo::electron: received FRAME, sending ACK window=main seq=323 -2026-03-09T19:05:12.110190Z  INFO wo::electron: received FRAME, sending ACK window=main seq=324 -2026-03-09T19:05:12.140751Z  INFO wo::electron: received FRAME, sending ACK window=main seq=325 -2026-03-09T19:05:12.164765Z  INFO wo::electron: received FRAME, sending ACK window=main seq=326 -2026-03-09T19:05:12.177277Z  INFO wo::electron: received FRAME, sending ACK window=main seq=327 -2026-03-09T19:05:12.202364Z  INFO wo::electron: received FRAME, sending ACK window=main seq=328 -2026-03-09T19:05:12.218435Z  INFO wo::electron: received FRAME, sending ACK window=main seq=329 -2026-03-09T19:05:12.239689Z  INFO wo::electron: received FRAME, sending ACK window=main seq=330 -2026-03-09T19:05:12.255865Z  INFO wo::electron: received FRAME, sending ACK window=main seq=331 -2026-03-09T19:05:12.271073Z  INFO wo::electron: received FRAME, sending ACK window=main seq=332 -2026-03-09T19:05:12.286363Z  INFO wo::electron: received FRAME, sending ACK window=main seq=333 -2026-03-09T19:05:12.312331Z  INFO wo::electron: received FRAME, sending ACK window=main seq=334 -2026-03-09T19:05:12.328733Z  INFO wo::electron: received FRAME, sending ACK window=main seq=335 -2026-03-09T19:05:12.346479Z  INFO wo::electron: received FRAME, sending ACK window=main seq=336 -2026-03-09T19:05:12.373025Z  INFO wo::electron: received FRAME, sending ACK window=main seq=337 -2026-03-09T19:05:12.390977Z  INFO wo::electron: received FRAME, sending ACK window=main seq=338 -2026-03-09T19:05:12.407722Z  INFO wo::electron: received FRAME, sending ACK window=main seq=339 -2026-03-09T19:05:12.424472Z  INFO wo::electron: received FRAME, sending ACK window=main seq=340 -2026-03-09T19:05:12.441822Z  INFO wo::electron: received FRAME, sending ACK window=main seq=341 -2026-03-09T19:05:12.460188Z  INFO wo::electron: received FRAME, sending ACK window=main seq=342 -2026-03-09T19:05:12.477883Z  INFO wo::electron: received FRAME, sending ACK window=main seq=343 -2026-03-09T19:05:12.492498Z  INFO wo::electron: received FRAME, sending ACK window=main seq=344 -2026-03-09T19:05:12.513175Z  INFO wo::electron: received FRAME, sending ACK window=main seq=345 -2026-03-09T19:05:12.531739Z  INFO wo::electron: received FRAME, sending ACK window=main seq=346 -2026-03-09T19:05:12.545860Z  INFO wo::electron: received FRAME, sending ACK window=main seq=347 -2026-03-09T19:05:12.567429Z  INFO wo::electron: received FRAME, sending ACK window=main seq=348 -2026-03-09T19:05:12.586376Z  INFO wo::electron: received FRAME, sending ACK window=main seq=349 -2026-03-09T19:05:12.601073Z  INFO wo::electron: received FRAME, sending ACK window=main seq=350 -2026-03-09T19:05:12.621546Z  INFO wo::electron: received FRAME, sending ACK window=main seq=351 -2026-03-09T19:05:12.640222Z  INFO wo::electron: received FRAME, sending ACK window=main seq=352 -2026-03-09T19:05:12.654652Z  INFO wo::electron: received FRAME, sending ACK window=main seq=353 -2026-03-09T19:05:12.675364Z  INFO wo::electron: received FRAME, sending ACK window=main seq=354 -2026-03-09T19:05:12.693906Z  INFO wo::electron: received FRAME, sending ACK window=main seq=355 -2026-03-09T19:05:12.708264Z  INFO wo::electron: received FRAME, sending ACK window=main seq=356 -2026-03-09T19:05:12.729829Z  INFO wo::electron: received FRAME, sending ACK window=main seq=357 -2026-03-09T19:05:12.748635Z  INFO wo::electron: received FRAME, sending ACK window=main seq=358 -2026-03-09T19:05:12.762763Z  INFO wo::electron: received FRAME, sending ACK window=main seq=359 -2026-03-09T19:05:12.787127Z  INFO wo::electron: received FRAME, sending ACK window=main seq=360 -2026-03-09T19:05:12.803766Z  INFO wo::electron: received FRAME, sending ACK window=main seq=361 -2026-03-09T19:05:12.818997Z  INFO wo::electron: received FRAME, sending ACK window=main seq=362 -2026-03-09T19:05:12.837389Z  INFO wo::electron: received FRAME, sending ACK window=main seq=363 -2026-03-09T19:05:12.852794Z  INFO wo::electron: received FRAME, sending ACK window=main seq=364 -2026-03-09T19:05:12.873555Z  INFO wo::electron: received FRAME, sending ACK window=main seq=365 -2026-03-09T19:05:12.891577Z  INFO wo::electron: received FRAME, sending ACK window=main seq=366 -2026-03-09T19:05:12.905672Z  INFO wo::electron: received FRAME, sending ACK window=main seq=367 -2026-03-09T19:05:12.927162Z  INFO wo::electron: received FRAME, sending ACK window=main seq=368 -2026-03-09T19:05:12.947907Z  INFO wo::electron: received FRAME, sending ACK window=main seq=369 -2026-03-09T19:05:12.963667Z  INFO wo::electron: received FRAME, sending ACK window=main seq=370 -2026-03-09T19:05:12.982352Z  INFO wo::electron: received FRAME, sending ACK window=main seq=371 -2026-03-09T19:05:13.000854Z  INFO wo::electron: received FRAME, sending ACK window=main seq=372 -2026-03-09T19:05:13.015132Z  INFO wo::electron: received FRAME, sending ACK window=main seq=373 -2026-03-09T19:05:13.036803Z  INFO wo::electron: received FRAME, sending ACK window=main seq=374 -2026-03-09T19:05:13.056234Z  INFO wo::electron: received FRAME, sending ACK window=main seq=375 -2026-03-09T19:05:13.071863Z  INFO wo::electron: received FRAME, sending ACK window=main seq=376 -2026-03-09T19:05:13.091266Z  INFO wo::electron: received FRAME, sending ACK window=main seq=377 -2026-03-09T19:05:13.109715Z  INFO wo::electron: received FRAME, sending ACK window=main seq=378 -2026-03-09T19:05:13.123733Z  INFO wo::electron: received FRAME, sending ACK window=main seq=379 -2026-03-09T19:05:13.145413Z  INFO wo::electron: received FRAME, sending ACK window=main seq=380 -2026-03-09T19:05:13.163979Z  INFO wo::electron: received FRAME, sending ACK window=main seq=381 -2026-03-09T19:05:13.178681Z  INFO wo::electron: received FRAME, sending ACK window=main seq=382 -2026-03-09T19:05:13.199512Z  INFO wo::electron: received FRAME, sending ACK window=main seq=383 -2026-03-09T19:05:13.218213Z  INFO wo::electron: received FRAME, sending ACK window=main seq=384 -2026-03-09T19:05:13.232930Z  INFO wo::electron: received FRAME, sending ACK window=main seq=385 -2026-03-09T19:05:13.253482Z  INFO wo::electron: received FRAME, sending ACK window=main seq=386 -2026-03-09T19:05:13.272062Z  INFO wo::electron: received FRAME, sending ACK window=main seq=387 -2026-03-09T19:05:13.287568Z  INFO wo::electron: received FRAME, sending ACK window=main seq=388 -2026-03-09T19:05:13.306257Z  INFO wo::electron: received FRAME, sending ACK window=main seq=389 -2026-03-09T19:05:13.324128Z  INFO wo::electron: received FRAME, sending ACK window=main seq=390 -2026-03-09T19:05:13.340022Z  INFO wo::electron: received FRAME, sending ACK window=main seq=391 -2026-03-09T19:05:13.360564Z  INFO wo::electron: received FRAME, sending ACK window=main seq=392 -2026-03-09T19:05:13.379982Z  INFO wo::electron: received FRAME, sending ACK window=main seq=393 -2026-03-09T19:05:13.394802Z  INFO wo::electron: received FRAME, sending ACK window=main seq=394 -2026-03-09T19:05:13.415633Z  INFO wo::electron: received FRAME, sending ACK window=main seq=395 -2026-03-09T19:05:13.439081Z  INFO wo::electron: received FRAME, sending ACK window=main seq=396 -2026-03-09T19:05:13.457838Z  INFO wo::electron: received FRAME, sending ACK window=main seq=397 -2026-03-09T19:05:13.473913Z  INFO wo::electron: received FRAME, sending ACK window=main seq=398 -2026-03-09T19:05:13.494966Z  INFO wo::electron: received FRAME, sending ACK window=main seq=399 -2026-03-09T19:05:13.508544Z  INFO wo::electron: received FRAME, sending ACK window=main seq=400 -2026-03-09T19:05:13.523620Z  INFO wo::electron: received FRAME, sending ACK window=main seq=401 -2026-03-09T19:05:13.539501Z  INFO wo::electron: received FRAME, sending ACK window=main seq=402 -2026-03-09T19:05:13.563298Z  INFO wo::electron: received FRAME, sending ACK window=main seq=403 -2026-03-09T19:05:13.579611Z  INFO wo::electron: received FRAME, sending ACK window=main seq=404 -2026-03-09T19:05:13.596487Z  INFO wo::electron: received FRAME, sending ACK window=main seq=405 -2026-03-09T19:05:13.616174Z  INFO wo::electron: received FRAME, sending ACK window=main seq=406 -2026-03-09T19:05:13.631073Z  INFO wo::electron: received FRAME, sending ACK window=main seq=407 -2026-03-09T19:05:13.646849Z  INFO wo::electron: received FRAME, sending ACK window=main seq=408 -2026-03-09T19:05:13.664197Z  INFO wo::electron: received FRAME, sending ACK window=main seq=409 -2026-03-09T19:05:13.690689Z  INFO wo::electron: received FRAME, sending ACK window=main seq=410 -2026-03-09T19:05:13.708523Z  INFO wo::electron: received FRAME, sending ACK window=main seq=411 -2026-03-09T19:05:13.722851Z  INFO wo::electron: received FRAME, sending ACK window=main seq=412 -2026-03-09T19:05:13.743455Z  INFO wo::electron: received FRAME, sending ACK window=main seq=413 -2026-03-09T19:05:13.763570Z  INFO wo::electron: received FRAME, sending ACK window=main seq=414 -2026-03-09T19:05:13.782992Z  INFO wo::electron: received FRAME, sending ACK window=main seq=415 -2026-03-09T19:05:13.801121Z  INFO wo::electron: received FRAME, sending ACK window=main seq=416 -2026-03-09T19:05:13.825578Z  INFO wo::electron: received FRAME, sending ACK window=main seq=417 -2026-03-09T19:05:13.838722Z  INFO wo::electron: received FRAME, sending ACK window=main seq=418 -2026-03-09T19:05:13.855922Z  INFO wo::electron: received FRAME, sending ACK window=main seq=419 -2026-03-09T19:05:13.876609Z  INFO wo::electron: received FRAME, sending ACK window=main seq=420 -2026-03-09T19:05:13.900907Z  INFO wo::electron: received FRAME, sending ACK window=main seq=421 -2026-03-09T19:05:13.916321Z  INFO wo::electron: received FRAME, sending ACK window=main seq=422 -2026-03-09T19:05:13.935874Z  INFO wo::electron: received FRAME, sending ACK window=main seq=423 -2026-03-09T19:05:13.955127Z  INFO wo::electron: received FRAME, sending ACK window=main seq=424 -2026-03-09T19:05:13.978076Z  INFO wo::electron: received FRAME, sending ACK window=main seq=425 -2026-03-09T19:05:14.000845Z  INFO wo::electron: received FRAME, sending ACK window=main seq=426 -2026-03-09T19:05:14.017284Z  INFO wo::electron: received FRAME, sending ACK window=main seq=427 -2026-03-09T19:05:14.038754Z  INFO wo::electron: received FRAME, sending ACK window=main seq=428 -2026-03-09T19:05:14.056086Z  INFO wo::electron: received FRAME, sending ACK window=main seq=429 -2026-03-09T19:05:14.073033Z  INFO wo::electron: received FRAME, sending ACK window=main seq=430 -2026-03-09T19:05:14.093819Z  INFO wo::electron: received FRAME, sending ACK window=main seq=431 -2026-03-09T19:05:14.110765Z  INFO wo::electron: received FRAME, sending ACK window=main seq=432 -2026-03-09T19:05:14.131288Z  INFO wo::electron: received FRAME, sending ACK window=main seq=433 -2026-03-09T19:05:14.147637Z  INFO wo::electron: received FRAME, sending ACK window=main seq=434 -2026-03-09T19:05:14.162861Z  INFO wo::electron: received FRAME, sending ACK window=main seq=435 -2026-03-09T19:05:14.180438Z  INFO wo::electron: received FRAME, sending ACK window=main seq=436 -2026-03-09T19:05:14.194591Z  INFO wo::electron: received FRAME, sending ACK window=main seq=437 -2026-03-09T19:05:14.220352Z  INFO wo::electron: received FRAME, sending ACK window=main seq=438 -2026-03-09T19:05:14.237740Z  INFO wo::electron: received FRAME, sending ACK window=main seq=439 -2026-03-09T19:05:14.253159Z  INFO wo::electron: received FRAME, sending ACK window=main seq=440 -2026-03-09T19:05:14.276561Z  INFO wo::electron: received FRAME, sending ACK window=main seq=441 -2026-03-09T19:05:14.291959Z  INFO wo::electron: received FRAME, sending ACK window=main seq=442 -2026-03-09T19:05:14.312479Z  INFO wo::electron: received FRAME, sending ACK window=main seq=443 -2026-03-09T19:05:14.325942Z  INFO wo::electron: received FRAME, sending ACK window=main seq=444 -2026-03-09T19:05:14.344599Z  INFO wo::electron: received FRAME, sending ACK window=main seq=445 -2026-03-09T19:05:14.360100Z  INFO wo::electron: received FRAME, sending ACK window=main seq=446 -2026-03-09T19:05:14.379636Z  INFO wo::electron: received FRAME, sending ACK window=main seq=447 -2026-03-09T19:05:14.393127Z  INFO wo::electron: received FRAME, sending ACK window=main seq=448 -2026-03-09T19:05:14.410253Z  INFO wo::electron: received FRAME, sending ACK window=main seq=449 -2026-03-09T19:05:14.433364Z  INFO wo::electron: received FRAME, sending ACK window=main seq=450 -2026-03-09T19:05:14.452105Z  INFO wo::electron: received FRAME, sending ACK window=main seq=451 -2026-03-09T19:05:14.470881Z  INFO wo::electron: received FRAME, sending ACK window=main seq=452 -2026-03-09T19:05:14.492229Z  INFO wo::electron: received FRAME, sending ACK window=main seq=453 -2026-03-09T19:05:14.512036Z  INFO wo::electron: received FRAME, sending ACK window=main seq=454 -[Wo] ERROR sending frame: Error: EPIPE: broken pipe, write -[Wo] ERROR sending frame: Error: EPIPE: broken pipe, write -[Wo] IPC connection closed -[Wo] SKIPPED paint (dmabufSender=false ipcConnected=false) +warning: unused import: `std::os::unix::io::AsRawFd` + --> src/dmabuf.rs:51:13 + | +51 | use std::os::unix::io::AsRawFd; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `smithay::utils::Rectangle` + --> src/dmabuf.rs:280:9 + | +280 | use smithay::utils::Rectangle; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `std::collections::HashMap` + --> src/handlers/screencopy.rs:6:5 + | +6 | use std::collections::HashMap; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `wl_output::WlOutput` + --> src/handlers/screencopy.rs:15:37 + | +15 | protocol::{wl_buffer::WlBuffer, wl_output::WlOutput, wl_shm}, + | ^^^^^^^^^^^^^^^^^^^ + + use std::os::unix::io::AsRawFd; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `smithay::utils::Rectangle` + --> src/dmabuf.rs:280:9 + | +280 | use smithay::utils::Rectangle; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `std::collections::HashMap` + --> src/handlers/screencopy.rs:6:5 + | +6 | use std::collections::HashMap; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `wl_output::WlOutput` + --> src/handlers/screencopy.rs:15:37 + | +15 | protocol::{wl_buffer::WlBuffer, wl_output::WlOutput, wl_shm}, + | ^^^^^^^^^^^^^^^^^^^ + +warning: use of deprecated associated function `smithay::desktop::Window::new` + --> src/handlers/xdg_shell.rs:53:30 + | +53 | let window = Window::new(surface); + | ^^^ + | + = notewarning: use of deprecated associated function `smithay::desktop::Window::new` + --> src/handlers/xdg_shell.rs:53:30 + | +53 | let window = Window::new(surface); + | ^^^ + | + = note: `#[warn(deprecated)]` on by default + +: `#[warn(deprecated)]` on by default + +warning: use of deprecated associated function `smithay::utils::Rectangle::::from_loc_and_size`: use new or from_size + --> src/handlers/xwayland.rs:258:58 + | +258 |warning: use of deprecated associated function `smithay::utils::Rectangle::::from_loc_and_size`: use new or from_size + --> src/handlers/xwayland.rs:258:58 + | +258 | if let Err(e) = window.configure(Some(Rectangle::from_loc_and_size((0, y_offset), (ow, content_h)))) { + | ^^^^^^^^^^^^^^^^^ + + if let Err(e) = window.configure(Some(Rectangle::from_loc_and_size((0, y_offset), (ow, content_h)))) { + | ^^^^^^^^^^^^^^^^^ + +warning: use of deprecated associated function `smithay::utils::Rectangle::::from_loc_and_size`: use new or from_size + --> src/handlers/xwayland.rs:303:58 + | +303 | if let Err(e) = window.configure(Some(Rectangle::from_loc_and_size((0, 0), (suggest_w, suggest_h)))) { + | ^^^^^^^^^^^^^^^^^ + +warning: variable `plane_idx` is assigned to, but never used + --> src/electron.rs:332:13 + | +332 | let mut plane_idx = 0; + | ^^^^^^^^^^^^^ + | + warning: use of deprecated associated function `smithay::utils::Rectangle::::from_loc_and_size`: use new or from_size + --> src/handlers/xwayland.rs:303:58 + | +303 | if let Err(e) = window.configure(Some(Rectangle::from_loc_and_size((0, 0), (suggest_w, suggest_h)))) { + | ^^^^^^^^^^^^^^^^^ + +warning: variable `plane_idx` is assigned to, but never used + --> src/electron.rs:332:13 + | +332 | let mut plane_idx = 0; + | ^^^^^^^^^^^^^ + | + = note: consider using `_plane_idx` instead + = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + += note: consider using `_plane_idx` instead + = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + +warning: value assigned to `plane_idx` is never read + --> src/electron.rs:348:13 + | +348 | plane_idx += 1; + | ^^^^^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + = notewarning: value assigned to `plane_idx` is never read + --> src/electron.rs:348:13 + | +348 | plane_idx += 1; + | ^^^^^^^^^^^^^^ + | + = help: maybe it is overwritten before being read? + = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default + +: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default + +warning: unreachable pattern + --> src/electron.rs:1259:46 + | +1259 | Err(nix::errno::Errno::EAGAIN) | Err(nix::errno::Errno::EWOULDBLOCK) => Ok(()), + | ------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no value can reach this + | warning: unreachable pattern + --> src/electron.rs:1259:46 + | +1259 | Err(nix::errno::Errno::EAGAIN) | Err(nix::errno::Errno::EWOULDBLOCK) => Ok(()), + | ------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no value can reach this + | | + | matches all the relevant values + | + = note: `#[warn(unreachable_patterns)]` (part of `#[warn(unused)]`) on by default + +| + | matches all the relevant values + | + = note: `#[warn(unreachable_patterns)]` (part of `#[warn(unused)]`) on by default + +warning: unreachable pattern + --> src/electron.rs:1286:42 + | +1286 | Err(nix::errno::Errno::EAGAIN) | Err(nix::errno::Errno::EWOULDBLOCK) => Ok(()), + | ------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unreachable pattern + --> src/electron.rs:1286:42 + | +1286 | Err(nix::errno::Errno::EAGAIN) | Err(nix::errno::Errno::EWOULDBLOCK) => Ok(()), + | ------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no value can reach this + | | + | matches all the relevant values + +no value can reach this + | | + | matches all the relevant values + +warning: unused variable: `surface_loc` + --> src/handlers/input.rs:439:42 + | +439 | if let Some((window, surface_loc)) = under_surface { + | ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_surface_loc`warning: unused variable: `surface_loc` + --> src/handlers/input.rs:439:42 + | +439 | if let Some((window, surface_loc)) = under_surface { + | ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_surface_loc` + + + +warning: unused variable: `window` + --> src/handlers/xdg_shell.rs:249:21 + | +249 | if let Some(window) = window { + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_window` + +warning: unused variable: `window` + --> src/handlers/xdg_shell.rs:249:21 + | +249 | if let Some(window) = window { + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_window` + +warning: unused variable: `event` + --> src/state.rs:615:9 + | +615 | event: &smithay::backend::input::InputEvent, + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_event` + +warning: unused variable: `event` + --> src/state.rs:615:9 + | +615 | event: &smithay::backend::input::InputEvent, + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_event` + +warning: `wo` (lib) generated 15 warnings (run `cargo fix --lib -p wo` to apply 8 suggestions) +warning: `wo` (lib) generated 15 warnings (run `cargo fix --lib -p wo` to apply 8 suggestions) +warning: unused import: `nix::sys::memfd::MFdFlags` + --> src/nested.rs:8:5 + | +8warning: unused import: `nix::sys::memfd::MFdFlags` + --> src/nested.rs:8:5 + | +8 | use nix::sys::memfd::MFdFlags; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + + | use nix::sys::memfd::MFdFlags; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `std::os::unix::io::AsRawFd` + --> src/main.rs:773:17 + | +773 warning: unused import: `std::os::unix::io::AsRawFd` + --> src/main.rs:773:17 + | +773 | use std::os::unix::io::AsRawFd; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +| use std::os::unix::io::AsRawFd; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `nix::sys::memfd::MFdFlags` + --> src/main.rs:774:17 + | +774 warning: unused import: `nix::sys::memfd::MFdFlags` + --> src/main.rs:774:17 + | +774 | use nix::sys::memfd::MFdFlags; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +| use nix::sys::memfd::MFdFlags; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `RenderElement` + --> src/main.rs:18:52 + | +18warning: unused import: `RenderElement` + --> src/main.rs:18:52 + | +18 | use smithay::backend::renderer::element::{Element, RenderElement}; + | ^^^^^^^^^^^^^ + + | use smithay::backend::renderer::element::{Element, RenderElement}; + | ^^^^^^^^^^^^^ + +warning: trait `RenderBackend` is never used + --> src/backend.rs:11:11 + | +11warning: trait `RenderBackend` is never used + --> src/backend.rs:11:11 + | +11 | pub trait RenderBackend { + | ^^^^^^^^^^^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + + | pub trait RenderBackend { + | ^^^^^^^^^^^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + +warning: struct `Frame` is never constructed + --> src/backend.rs:39:12 + | +39warning: struct `Frame` is never constructed + --> src/backend.rs:39:12 + | +39 | pub struct Frame { + | ^^^^^ + + | pub struct Frame { + | ^^^^^ + +warning: associated function `new` is never used + --> src/backend.rs:45:12 + | +44 warning: associated function `new` is never used + --> src/backend.rs:45:12 + | +44 | impl Frame { + | ---------- associated function in this implementation +45 | pub fn new(output_rect: Rectangle) -> Self { + | ^^^ + +| impl Frame { + | ---------- associated function in this implementation +45 | pub fn new(output_rect: Rectangle) -> Self { + | ^^^ + +warning: function `reimport_cached_frame` is never used + --> src/dmabuf.rs:212:8 + | +212 warning: function `reimport_cached_frame` is never used + --> src/dmabuf.rs:212:8 + | +212 | pub fn reimport_cached_frame( + | ^^^^^^^^^^^^^^^^^^^^^ + +| pub fn reimport_cached_frame( + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `PlaneInfoWire` is never constructed + --> src/electron.rs:57:12 + | +warning: struct `PlaneInfoWire` is never constructed + --> src/electron.rs:57:12 + | +57 | pub struct PlaneInfoWire { + | ^^^^^^^^^^^^^ + +57 | pub struct PlaneInfoWire { + | ^^^^^^^^^^^^^ + +warning: `wo` (bin "wo") generated 24 warnings (15 duplicates) (run `cargo fix --bin "wo" -p wo` to apply 3 suggestions) +warning: `wo` (bin "wo") generated 24 warnings (15 duplicates) (run `cargo fix --bin "wo" -p wo` to apply 3 suggestions) + Finished `release` profile [optimized] target(s) in 0.20s + Finished `release` profile [optimized] target(s) in 0.20s + Running `target/release/wo` + Running `target/release/wo` +Xwayland glamor: GBM Wayland interfaces not available +Failed to initialize glamor, falling back to sw +Xwayland glamor: GBM Wayland interfaces not available +Failed to initialize glamor, falling back to sw +(II) IGLX: Loaded and initialized swrast +(II) GLX: Initialized DRISWRAST GL provider for screen 0 +(II) IGLX: Loaded and initialized swrast +(II) GLX: Initialized DRISWRAST GL provider for screen 0 +The XKEYBOARD keymap compiler (xkbcomp) reports: +> Warning: Multiple symbols for level 1/group 1 on key +> Using F23, ignoring XF86TouchpadOff +> Warning: Symbol map for key redefined +> Using last definition for conflicting fields +> Warning: Symbol map for key redefined +> Using last definition for conflicting fields +The XKEYBOARD keymap compiler (xkbcomp) reports: +> Warning: Multiple symbols for level 1/group 1 on key +> Using F23, ignoring XF86TouchpadOff +> Warning: Symbol map for key redefined +> Using last definition for conflicting fields +> Warning: Symbol map for key redefined +> Using last definition for conflicting fields +Errors from xkbcomp are not fatal to the X server +Errors from xkbcomp are not fatal to the X server +The XKEYBOARD keymap compiler (xkbcomp) reports: +> Warning: Unsupported maximum keycode 709, clipping. +> X11 cannot support keycodes above 255. +The XKEYBOARD keymap compiler (xkbcomp) reports: +> Warning: Unsupported maximum keycode 709, clipping. +> X11 cannot support keycodes above 255. +> Warning: Virtual modifier Hyper multiply defined +> Using 0, ignoring 0 +> Warning: Virtual modifier ScrollLock multiply defined +> Using 0, ignoring 0 +> Warning: Virtual modifier Hyper multiply defined +> Using 0, ignoring 0 +> Warning: Virtual modifier ScrollLock multiply defined +> Using 0, ignoring 0 +Errors from xkbcomp are not fatal to the X server +Errors from xkbcomp are not fatal to the X server +[75913:0310/190621.144695:ERROR:ui/gl/init/gl_factory.cc:102] Requested GL implementation (gl=none,angle=none) not found in allowed implementations: [(gl=egl-angle,angle=default)]. +[75913:0310/190621.144695:ERROR:ui/gl/init/gl_factory.cc:102] Requested GL implementation (gl=none,angle=none) not found in allowed implementations: [(gl=egl-angle,angle=default)]. +[75913:0310/190621.145938:ERROR:components/viz/service/main/viz_main_impl.cc:189] Exiting GPU process due to errors during initialization +[75913:0310/190621.145938:ERROR:components/viz/service/main/viz_main_impl.cc:189] Exiting GPU process due to errors during initialization +[75933:0310/190621.205869:ERROR:ui/gl/init/gl_factory.cc:102] Requested GL implementation (gl=none,angle=none) not found in allowed implementations: [(gl=egl-angle,angle=default)]. +[75933:0310/190621.205869:ERROR:ui/gl/init/gl_factory.cc:102] Requested GL implementation (gl=none,angle=none) not found in allowed implementations: [(gl=egl-angle,angle=default)]. +[75933:0310/190621.207105:ERROR:components/viz/service/main/viz_main_impl.cc:189] Exiting GPU process due to errors during initialization +[75933:0310/190621.207105:ERROR:components/viz/service/main/viz_main_impl.cc:189] Exiting GPU process due to errors during initialization +[75966:0310/190621.260351:ERROR:ui/gl/init/gl_factory.cc:102] Requested GL implementation (gl=none,angle=none) not found in allowed implementations: [(gl=egl-angle,angle=default)]. +[75966:0310/190621.260351:ERROR:ui/gl/init/gl_factory.cc:102] Requested GL implementation (gl=none,angle=none) not found in allowed implementations: [(gl=egl-angle,angle=default)]. +[75966:0310/190621.261808:ERROR:components/viz/service/main/viz_main_impl.cc:189] Exiting GPU process due to errors during initialization +[75966:0310/190621.261808:ERROR:components/viz/service/main/viz_main_impl.cc:189] Exiting GPU process due to errors during initialization +(node:75859) DeprecationWarning: getBitmap() is deprecated, use toBitmap() instead. +(Use `electron --trace-deprecation ...` to show where the warning was created) +(node:75859) DeprecationWarning: getBitmap() is deprecated, use toBitmap() instead. +(Use `electron --trace-deprecation ...` to show where the warning was created) +Error sending from webFrameMain: Error: Failed to serialize arguments + at s.send (node:electron/js2c/browser_init:2:100266) + at b.send (node:electron/js2c/browser_init:2:84687) + at getOrCreateSab (file:///home/hellings/Documents/wo/electron/dist/main.js:318:28) + at Socket. (file:///home/hellings/Documents/wo/electron/dist/main.js:1251:30) + at Socket.emit (node:events:519:28) + at addChunk (node:internal/streams/readable:561:12) + at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) + at Readable.push (node:internal/streams/readable:392:5) + at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) +Error sending from webFrameMain: Error: Failed to serialize arguments + at s.send (node:electron/js2c/browser_init:2:100266) + at b.send (node:electron/js2c/browser_init:2:84687) + at getOrCreateSab (file:///home/hellings/Documents/wo/electron/dist/main.js:318:28) + at Socket. (file:///home/hellings/Documents/wo/electron/dist/main.js:1251:30) + at Socket.emit (node:events:519:28) + at addChunk (node:internal/streams/readable:561:12) + at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) + at Readable.push (node:internal/streams/readable:392:5) + at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) +Error sending from webFrameMain: Error: Failed to serialize arguments + at s.send (node:electron/js2c/browser_init:2:100266) + at b.send (node:electron/js2c/browser_init:2:84687) + at getOrCreateSab (file:///home/hellings/Documents/wo/electron/dist/main.js:318:28) + at Socket. (file:///home/hellings/Documents/wo/electron/dist/main.js:1251:30) + at Socket.emit (node:events:519:28) + at addChunk (node:internal/streams/readable:561:12) + at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) + at Readable.push (node:internal/streams/readable:392:5) + at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) +Error sending from webFrameMain: Error: Failed to serialize arguments + at s.send (node:electron/js2c/browser_init:2:100266) + at b.send (node:electron/js2c/browser_init:2:84687) + at getOrCreateSab (file:///home/hellings/Documents/wo/electron/dist/main.js:318:28) + at Socket. (file:///home/hellings/Documents/wo/electron/dist/main.js:1251:30) + at Socket.emit (node:events:519:28) + at addChunk (node:internal/streams/readable:561:12) + at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) + at Readable.push (node:internal/streams/readable:392:5) + at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) +Key repeat discarded, Wayland compositor doesn't seem to be processing events fast enough! +Key repeat discarded, Wayland compositor doesn't seem to be processing events fast enough! +(EE) failed to read Wayland events: Broken pipe +(EE) failed to read Wayland events: Broken pipe +[75859:0310/190654.025906:ERROR:mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:732] Message 0 rejected by interface blink.mojom.WidgetHost +[75859:0310/190654.025906:ERROR:mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:732] Message 0 rejected by interface blink.mojom.WidgetHost +[75859:0310/190654.035184:ERROR:mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:732] Message 4 rejected by interface blink.mojom.WidgetHost +[75859:0310/190654.035184:ERROR:mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:732] Message 4 rejected by interface blink.mojom.WidgetHost +[75859:0310/190654.058269:ERROR:mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:732] Message 5 rejected by interface blink.mojom.WidgetHost +[75859:0310/190654.058269:ERROR:mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:732] Message 5 rejected by interface blink.mojom.WidgetHost +[75859:0310/190654.068805:ERROR:content/browser/network_service_instance_impl.cc:613] Network service crashed, restarting service. +[75859:0310/190654.068805:ERROR:content/browser/network_service_instance_impl.cc:613] Network service crashed, restarting service. +[75859:0310/190654.077683:ERROR:content/browser/gpu/gpu_process_host.cc:999] GPU process exited unexpectedly: exit_code=15 +[75859:0310/190654.077683:ERROR:content/browser/gpu/gpu_process_host.cc:999] GPU process exited unexpectedly: exit_code=15