Further optimization passes

This commit is contained in:
Allie H 2026-03-11 17:35:46 -04:00
parent ff54140d62
commit 68b4916f64
12 changed files with 574 additions and 881 deletions

View file

@ -235,15 +235,22 @@ function debugLog(...args) {
console.log(...args); console.log(...args);
} }
debugLog("[Wo] Electron starting, NODE_VERSION=", process.version); debugLog("[Wo] Electron starting, NODE_VERSION=", process.version);
app.commandLine.appendSwitch("use-gl", "angle");
app.commandLine.appendSwitch("use-angle", "vulkan");
app.commandLine.appendSwitch("ozone-platform", "wayland");
app.commandLine.appendSwitch("no-sandbox");
app.commandLine.appendSwitch("disable-gpu-sandbox");
app.commandLine.appendSwitch("ignore-gpu-blocklist"); app.commandLine.appendSwitch("ignore-gpu-blocklist");
app.commandLine.appendSwitch("enable-gpu-rasterization"); app.commandLine.appendSwitch("enable-gpu-rasterization");
app.commandLine.appendSwitch("enable-zero-copy"); app.commandLine.appendSwitch("enable-zero-copy");
app.commandLine.appendSwitch("enable-native-gpu-memory-buffers"); app.commandLine.appendSwitch("enable-native-gpu-memory-buffers");
app.commandLine.appendSwitch("disable-software-rasterizer"); app.commandLine.appendSwitch("disable-software-rasterizer");
app.commandLine.appendSwitch("enable-features", "CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder,SharedArrayBuffer,RawDraw"); app.commandLine.appendSwitch("enable-features", "CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder,SharedArrayBuffer,RawDraw,DefaultANGLEVulkan,VulkanFromANGLE");
app.commandLine.appendSwitch("enable-accelerated-video-decode"); app.commandLine.appendSwitch("enable-accelerated-video-decode");
app.commandLine.appendSwitch("disable-frame-rate-limit"); app.commandLine.appendSwitch("disable-frame-rate-limit");
app.commandLine.appendSwitch("disable-gpu-vsync"); app.commandLine.appendSwitch("disable-gpu-vsync");
app.commandLine.appendSwitch("num-raster-threads", "4");
app.commandLine.appendSwitch("disable-renderer-backgrounding");
debugLog("[Wo] GPU acceleration flags applied"); debugLog("[Wo] GPU acceleration flags applied");
var IPC_SOCKET = process.env.WO_IPC_SOCKET || "/run/user/1000/wo-ipc.sock"; 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); debugLog("[Wo] WO_WINDOW_CONFIG env =", process.env.WO_WINDOW_CONFIG);

View file

@ -2,6 +2,7 @@ import { app, BrowserWindow, ipcMain } from 'electron';
import { createConnection, createServer, Socket, Server } from 'net'; import { createConnection, createServer, Socket, Server } from 'net';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { dirname, resolve as resolvePath, join as joinPath } from 'path'; import { dirname, resolve as resolvePath, join as joinPath } from 'path';
import { randomUUID } from 'crypto';
import fs from 'fs'; import fs from 'fs';
import { createRequire } from 'module'; import { createRequire } from 'module';
import { MAGIC, stringToBuffer } from './protocol.js'; import { MAGIC, stringToBuffer } from './protocol.js';
@ -44,18 +45,25 @@ function debugLog(...args: any[]) {
debugLog('[Wo] Electron starting, NODE_VERSION=', process.version); debugLog('[Wo] Electron starting, NODE_VERSION=', process.version);
// GPU acceleration & rendering performance flags (must be set before app 'ready') // GPU acceleration & rendering performance flags (must be set before app 'ready')
// Note: core flags (--in-process-gpu, --use-gl=egl, --ozone-platform=wayland) // Chromium flags MUST go through appendSwitch — process argv flags are not
// are set on the command line in the Rust spawn(). Flags below augment those. // reliably forwarded by Electron to Chromium's command-line parser.
app.commandLine.appendSwitch('use-gl', 'angle');
app.commandLine.appendSwitch('use-angle', 'vulkan');
app.commandLine.appendSwitch('ozone-platform', 'wayland');
app.commandLine.appendSwitch('no-sandbox');
app.commandLine.appendSwitch('disable-gpu-sandbox');
app.commandLine.appendSwitch('ignore-gpu-blocklist'); app.commandLine.appendSwitch('ignore-gpu-blocklist');
app.commandLine.appendSwitch('enable-gpu-rasterization'); app.commandLine.appendSwitch('enable-gpu-rasterization');
app.commandLine.appendSwitch('enable-zero-copy'); app.commandLine.appendSwitch('enable-zero-copy');
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers'); app.commandLine.appendSwitch('enable-native-gpu-memory-buffers');
app.commandLine.appendSwitch('disable-software-rasterizer'); app.commandLine.appendSwitch('disable-software-rasterizer');
app.commandLine.appendSwitch('enable-features', app.commandLine.appendSwitch('enable-features',
'CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder,SharedArrayBuffer,RawDraw'); 'CanvasOopRasterization,Vulkan,VaapiVideoDecoder,VaapiVideoEncoder,SharedArrayBuffer,RawDraw,DefaultANGLEVulkan,VulkanFromANGLE');
app.commandLine.appendSwitch('enable-accelerated-video-decode'); app.commandLine.appendSwitch('enable-accelerated-video-decode');
app.commandLine.appendSwitch('disable-frame-rate-limit'); app.commandLine.appendSwitch('disable-frame-rate-limit');
app.commandLine.appendSwitch('disable-gpu-vsync'); app.commandLine.appendSwitch('disable-gpu-vsync');
app.commandLine.appendSwitch('num-raster-threads', '4');
app.commandLine.appendSwitch('disable-renderer-backgrounding');
debugLog('[Wo] GPU acceleration flags applied'); debugLog('[Wo] GPU acceleration flags applied');
type ClientWindowInfo = { type ClientWindowInfo = {
@ -84,8 +92,8 @@ let ipcSocket: Socket | null = null;
let portalUiServer: Server | null = null; let portalUiServer: Server | null = null;
type PendingPortalRequest = { type PendingPortalRequest = {
socket: Socket; socket: Socket;
kind: 'screen_share'; requestId: string;
sessionId: string; payload: any;
}; };
const portalPendingRequests = new Map<string, PendingPortalRequest>(); const portalPendingRequests = new Map<string, PendingPortalRequest>();
const PORTAL_UI_SOCKET = '/tmp/wo-portal-ui.sock'; const PORTAL_UI_SOCKET = '/tmp/wo-portal-ui.sock';
@ -202,7 +210,7 @@ function getOrCreateSab(windowName: string, width: number, height: number, strid
surfaceSabCache.set(windowName, entry); surfaceSabCache.set(windowName, entry);
// Tell renderer about the new SAB so it can reference it // Tell renderer about the new SAB so it can reference it
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.postMessage('wo:surface-sab', { mainWindow.webContents.send('wo:surface-sab', {
name: windowName, name: windowName,
sab, sab,
width, width,
@ -231,7 +239,8 @@ let pointerX = 0;
let pointerY = 0; let pointerY = 0;
function respondToPortalRequest(requestId: string, params: { function respondToPortalRequest(requestId: string, params: {
allowed: boolean; response?: unknown;
allowed?: boolean;
type?: string; type?: string;
windowName?: unknown; windowName?: unknown;
}): { ok: boolean; error?: string } { }): { ok: boolean; error?: string } {
@ -240,19 +249,22 @@ function respondToPortalRequest(requestId: string, params: {
return { ok: false, error: `No pending portal request: ${requestId}` }; return { ok: false, error: `No pending portal request: ${requestId}` };
} }
let response: Record<string, unknown>; const responsePayload =
if (pending.kind === 'screen_share') { params.response !== undefined
response = { ? params.response
: {
allowed: Boolean(params.allowed), allowed: Boolean(params.allowed),
sourceType: params.type === 'window' ? 'Window' : 'Monitor', sourceType: params.type === 'window' ? 'Window' : 'Monitor',
windowName: typeof params.windowName === 'string' ? params.windowName : null, windowName: typeof params.windowName === 'string' ? params.windowName : null,
}; };
} else {
response = { allowed: false }; if (responsePayload === undefined) {
portalPendingRequests.delete(requestId);
return { ok: false, error: 'Missing portal response payload' };
} }
try { try {
pending.socket.write(`${JSON.stringify(response)}\n`); pending.socket.write(`${JSON.stringify(responsePayload)}\n`);
pending.socket.end(); pending.socket.end();
portalPendingRequests.delete(requestId); portalPendingRequests.delete(requestId);
return { ok: true }; return { ok: true };
@ -276,64 +288,59 @@ function startPortalUiBridge() {
socket.on('data', (chunk: Buffer) => { socket.on('data', (chunk: Buffer) => {
received += chunk.toString('utf8'); received += chunk.toString('utf8');
const nl = received.indexOf('\n'); let nl = received.indexOf('\n');
if (nl < 0) return; while (nl >= 0) {
const line = received.slice(0, nl).trim(); const line = received.slice(0, nl).trim();
received = received.slice(nl + 1); received = received.slice(nl + 1);
if (!line) return;
if (line.length > 0) {
try { try {
const req = JSON.parse(line) as { const req = JSON.parse(line) as Record<string, unknown>;
type?: string; const requestId = String(
appName?: string; (req?.requestId as string | undefined)
sessionId?: string; ?? (req?.sessionId as string | undefined)
}; ?? (req?.id as string | undefined)
?? randomUUID(),
);
if (req.type !== 'screen_share_request' || !req.sessionId) { portalPendingRequests.set(requestId, { socket, requestId, payload: req });
socket.end('{"allowed":false,"reason":"invalid_request"}\n');
return;
}
const requestId = req.sessionId; // Auto-timeout if UI does not respond within 90s
portalPendingRequests.set(requestId, {
socket,
kind: 'screen_share',
sessionId: req.sessionId,
});
// Auto-deny if UI does not respond within 90s
socket.setTimeout(90_000, () => { socket.setTimeout(90_000, () => {
const pending = portalPendingRequests.get(requestId); const pending = portalPendingRequests.get(requestId);
if (pending) { if (pending) {
portalPendingRequests.delete(requestId); portalPendingRequests.delete(requestId);
pending.socket.end('{"allowed":false,"reason":"timeout"}\n'); pending.socket.end(JSON.stringify({ error: 'timeout', requestId }) + '\n');
} }
}); });
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
const payload = { mainWindow.webContents.send('wo:portal-request', {
requestId, requestId,
kind: 'screen_share', payload: req,
appName: req.appName || 'Application', kind: typeof req?.type === 'string'
sessionId: req.sessionId, ? (req.type as string)
}; : typeof req?.kind === 'string'
? (req.kind as string)
mainWindow.webContents.send('wo:portal-request', payload); : undefined,
});
} else { } else {
portalPendingRequests.delete(requestId); portalPendingRequests.delete(requestId);
socket.end('{"allowed":false,"reason":"no_ui"}\n'); socket.end(JSON.stringify({ error: 'no_ui', requestId }) + '\n');
} }
} catch { } catch {
socket.end('{"allowed":false,"reason":"invalid_json"}\n'); socket.end('{"error":"invalid_json"}\n');
}
}
nl = received.indexOf('\n');
} }
}); });
socket.on('close', () => { socket.on('close', () => {
for (const [sessionId, pending] of portalPendingRequests.entries()) { for (const [requestId, pending] of portalPendingRequests.entries()) {
if (pending.socket === socket) { if (pending.socket === socket) {
portalPendingRequests.delete(sessionId); portalPendingRequests.delete(requestId);
break; break;
} }
} }
@ -356,7 +363,7 @@ function startPortalUiBridge() {
function stopPortalUiBridge() { function stopPortalUiBridge() {
for (const pending of portalPendingRequests.values()) { for (const pending of portalPendingRequests.values()) {
try { try {
pending.socket.end('{"allowed":false,"reason":"shutdown"}\n'); pending.socket.end(JSON.stringify({ error: 'shutdown', requestId: pending.requestId }) + '\n');
} catch { } catch {
// ignore // ignore
} }
@ -402,321 +409,22 @@ let clientWindows: ClientWindowInfo[] = parseClientWindows();
// Live window list received from the compositor via WOWM metadata messages. // Live window list received from the compositor via WOWM metadata messages.
let compositorWindows: Record<string, unknown>[] = []; let compositorWindows: Record<string, unknown>[] = [];
type StatusNotifierTrayItem = { type DbBus = 'user' | 'system';
id: string; function runBusctl(bus: DbBus, args: string[]): { ok: boolean; stdout: string; stderr: string; exitCode: number } {
title: string;
status: 'active' | 'passive' | 'attention';
icon: string;
iconDataUrl?: string;
service: string;
objectPath: string;
menuPath?: string;
hasMenu: boolean;
};
function parseQuotedStrings(output: string): string[] {
const matches = output.matchAll(/"((?:[^"\\]|\\.)*)"/g);
const values: string[] = [];
for (const match of matches) {
const raw = match[1];
values.push(raw.replace(/\\"/g, '"').replace(/\\\\/g, '\\'));
}
return values;
}
function runBusctlUser(args: string[]): string {
try { try {
const { execFileSync } = require('child_process') as typeof import('child_process'); const { spawnSync } = require('child_process') as typeof import('child_process');
return execFileSync('busctl', ['--user', ...args], { const res = spawnSync('busctl', [bus === 'system' ? '--system' : '--user', ...args], {
encoding: 'utf8', encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'], stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
} catch {
return '';
}
}
function parseServiceAndPath(itemRef: string): { service: string; objectPath: string } | null {
const slashIdx = itemRef.indexOf('/');
if (slashIdx > 0) {
return {
service: itemRef.slice(0, slashIdx),
objectPath: itemRef.slice(slashIdx),
};
}
if (!itemRef) {
return null;
}
return {
service: itemRef,
objectPath: '/StatusNotifierItem',
};
}
function parseBusctlStringValue(output: string): string {
const quoted = parseQuotedStrings(output);
if (quoted.length > 0) {
return quoted[0];
}
const parts = output.split(/\s+/);
return parts.length >= 2 ? parts.slice(1).join(' ').trim() : '';
}
function getBusProperty(service: string, objectPath: string, iface: string, prop: string): string {
const out = runBusctlUser(['get-property', service, objectPath, iface, prop]);
return out ? parseBusctlStringValue(out) : '';
}
function mapTrayIcon(iconName: string, title: string): string {
const n = iconName.toLowerCase();
const t = title.toLowerCase();
if (n.includes('network') || t.includes('network') || t.includes('wifi')) return 'mdi:wifi';
if (n.includes('audio') || n.includes('volume') || t.includes('volume')) return 'mdi:volume-high';
if (n.includes('battery') || t.includes('battery')) return 'mdi:battery';
if (n.includes('bluetooth') || t.includes('bluetooth')) return 'mdi:bluetooth';
if (n.includes('telegram') || t.includes('telegram')) return 'mdi:telegram';
if (n.includes('discord') || t.includes('discord')) return 'mdi:discord';
if (n.includes('steam') || t.includes('steam')) return 'mdi:steam';
if (n.includes('dropbox') || t.includes('dropbox')) return 'mdi:dropbox';
if (n.includes('mail') || t.includes('mail')) return 'mdi:email-outline';
if (n.includes('kde') || t.includes('kde')) return 'mdi:kde';
return 'mdi:circle-medium';
}
function iconPathCandidates(iconName: string): string[] {
const direct = iconName.startsWith('/') ? [iconName] : [];
if (iconName.startsWith('/')) {
return direct;
}
const exts = ['png', 'svg'];
const bases = [
'/usr/share/pixmaps',
'/usr/share/icons/hicolor/16x16/apps',
'/usr/share/icons/hicolor/22x22/apps',
'/usr/share/icons/hicolor/24x24/apps',
'/usr/share/icons/hicolor/32x32/apps',
'/usr/share/icons/hicolor/48x48/apps',
'/usr/share/icons/hicolor/scalable/apps',
'/usr/share/icons/breeze/apps/22',
'/usr/share/icons/breeze/apps/24',
'/usr/share/icons/breeze/apps/32',
];
const results: string[] = [];
for (const base of bases) {
for (const ext of exts) {
results.push(`${base}/${iconName}.${ext}`);
}
}
return results;
}
function toDataUrlForIconFile(iconPath: string): string | undefined {
try {
if (!fs.existsSync(iconPath)) {
return undefined;
}
const lower = iconPath.toLowerCase();
const mime = lower.endsWith('.svg') ? 'image/svg+xml' : lower.endsWith('.png') ? 'image/png' : '';
if (!mime) {
return undefined;
}
const base64 = fs.readFileSync(iconPath).toString('base64');
return `data:${mime};base64,${base64}`;
} catch {
return undefined;
}
}
function resolveTrayIconDataUrl(iconName: string): string | undefined {
for (const candidate of iconPathCandidates(iconName)) {
const dataUrl = toDataUrlForIconFile(candidate);
if (dataUrl) {
return dataUrl;
}
}
return undefined;
}
function getStatusNotifierTrayItems(): StatusNotifierTrayItem[] {
const watcherCall = runBusctlUser([
'call',
'org.kde.StatusNotifierWatcher',
'/StatusNotifierWatcher',
'org.kde.StatusNotifierWatcher',
'RegisteredStatusNotifierItems',
]);
if (!watcherCall) {
return [];
}
const refs = parseQuotedStrings(watcherCall);
const results: StatusNotifierTrayItem[] = [];
for (const itemRef of refs) {
const parsed = parseServiceAndPath(itemRef);
if (!parsed) {
continue;
}
const statusRaw = getBusProperty(parsed.service, parsed.objectPath, 'org.kde.StatusNotifierItem', 'Status');
const title =
getBusProperty(parsed.service, parsed.objectPath, 'org.kde.StatusNotifierItem', 'Title')
|| getBusProperty(parsed.service, parsed.objectPath, 'org.kde.StatusNotifierItem', 'Id')
|| parsed.service;
const iconName =
getBusProperty(parsed.service, parsed.objectPath, 'org.kde.StatusNotifierItem', 'AttentionIconName')
|| getBusProperty(parsed.service, parsed.objectPath, 'org.kde.StatusNotifierItem', 'IconName');
const menuPath = getBusProperty(parsed.service, parsed.objectPath, 'org.kde.StatusNotifierItem', 'Menu');
const iconDataUrl = iconName ? resolveTrayIconDataUrl(iconName) : undefined;
let status: StatusNotifierTrayItem['status'] = 'active';
const s = statusRaw.toLowerCase();
if (s.includes('passive')) {
status = 'passive';
} else if (s.includes('attention') || s.includes('needsattention')) {
status = 'attention';
}
results.push({
id: `${parsed.service}${parsed.objectPath}`,
title,
status,
icon: mapTrayIcon(iconName || title, title),
iconDataUrl,
service: parsed.service,
objectPath: parsed.objectPath,
menuPath: menuPath || undefined,
hasMenu: Boolean(menuPath && menuPath !== '/NO_DBUSMENU'),
}); });
} return {
ok: res.status === 0,
return results; stdout: (res.stdout as string | undefined)?.trim?.() || '',
} stderr: (res.stderr as string | undefined)?.trim?.() || '',
exitCode: res.status ?? -1,
function activateStatusNotifierItem(service: string, objectPath: string): boolean {
const out = runBusctlUser([
'call',
service,
objectPath,
'org.kde.StatusNotifierItem',
'Activate',
'ii',
'0',
'0',
]);
return out.length > 0;
}
function secondaryActivateStatusNotifierItem(service: string, objectPath: string): boolean {
const out = runBusctlUser([
'call',
service,
objectPath,
'org.kde.StatusNotifierItem',
'SecondaryActivate',
'ii',
'0',
'0',
]);
return out.length > 0;
}
function openStatusNotifierContextMenu(service: string, objectPath: string, x: number, y: number): boolean {
const out = runBusctlUser([
'call',
service,
objectPath,
'org.kde.StatusNotifierItem',
'ContextMenu',
'ii',
String(Math.trunc(x)),
String(Math.trunc(y)),
]);
return out.length > 0;
}
type DBusMenuItem = {
id: string;
label: string;
enabled: boolean;
children?: DBusMenuItem[];
};
function getDBusMenuLayout(service: string, menuPath: string): DBusMenuItem[] {
try {
const out = runBusctlUser([
'call',
service,
menuPath.startsWith('/') ? menuPath : '/com/canonical/dbusmenu',
'com.canonical.dbusmenu',
'GetLayout',
'u',
'0',
]);
if (!out) {
return [];
}
const lines = out.split('\n').filter(l => l.trim().length > 0);
const items: DBusMenuItem[] = [];
let depth = 0;
for (const line of lines) {
const trimmed = line.trim();
const newDepth = (line.match(/^\s*/)?.[0]?.length ?? 0) / 2;
if (trimmed.startsWith('(') && trimmed.includes('"')) {
const quoted = parseQuotedStrings(trimmed);
if (quoted.length > 0) {
const label = quoted[0];
const item: DBusMenuItem = {
id: label,
label,
enabled: !line.toLowerCase().includes('disabled'),
}; };
} catch (error: any) {
if (newDepth === 0) { return { ok: false, stdout: '', stderr: String(error), exitCode: -1 };
items.push(item);
} else if (items.length > 0 && newDepth > depth) {
const parent = items[items.length - 1];
if (!parent.children) {
parent.children = [];
}
parent.children.push(item);
}
}
depth = newDepth;
}
}
return items;
} catch {
return [];
}
}
function triggerDBusMenuEvent(service: string, menuPath: string, menuId: string): boolean {
try {
const out = runBusctlUser([
'call',
service,
menuPath.startsWith('/') ? menuPath : '/com/canonical/dbusmenu',
'com.canonical.dbusmenu',
'Event',
'sis',
menuId,
'clicked',
'',
]);
return out.length > 0;
} catch {
return false;
} }
} }
@ -1263,17 +971,56 @@ function connectToCompositor(): Promise<void> {
const dmabufFormat = compositorRxBuffer.readUInt32LE(off); off += 4; const dmabufFormat = compositorRxBuffer.readUInt32LE(off); off += 4;
const numPlanes = compositorRxBuffer.readUInt32LE(off); off += 4; const numPlanes = compositorRxBuffer.readUInt32LE(off); off += 4;
const totalSize = (off - rxReadOffset) + (numPlanes * 24); const planeSectionSize = numPlanes * 24;
const totalSize = (off - rxReadOffset) + planeSectionSize;
if (rxAvailable() < totalSize) break; if (rxAvailable() < totalSize) break;
const planes: Array<{ offset: bigint; stride: number; modifier: bigint }> = [];
let planeOff = off;
for (let i = 0; i < numPlanes; i++) {
planeOff += 4; // fd placeholder
const planeOffset = compositorRxBuffer.readBigUInt64LE(planeOff); planeOff += 8;
const planeStride = compositorRxBuffer.readUInt32LE(planeOff); planeOff += 4;
const modifier = compositorRxBuffer.readBigUInt64LE(planeOff); planeOff += 8;
planes.push({ offset: planeOffset, stride: planeStride, modifier });
}
try { try {
if (nativeDmabuf && ipcSocket) { if (nativeDmabuf && ipcSocket) {
const socketFd = (ipcSocket as any)?._handle?.fd; const socketFd = (ipcSocket as any)?._handle?.fd;
if (typeof socketFd === 'number' && socketFd >= 0) { if (typeof socketFd === 'number' && socketFd >= 0) {
const dmabufFd = nativeDmabuf.recvFd(socketFd); const planeFds: number[] = [];
if (dmabufFd >= 0) { for (let i = 0; i < numPlanes; i++) {
const fd = nativeDmabuf.recvFd(socketFd);
if (fd >= 0) planeFds.push(fd);
}
const primaryFd = planeFds.find((fd) => fd >= 0);
const primaryPlane = planes[0];
const stride = primaryPlane?.stride || dmabufW * 4;
const offset = primaryPlane ? Number(primaryPlane.offset) : 0;
if (primaryFd !== undefined) {
try { try {
const textureInfo = nativeDmabuf.importDmabufTexture(dmabufName, dmabufFd, dmabufW, dmabufH, dmabufFormat); // Map DMABUF into SAB so renderer can reuse existing surface path
const sabEntry = getOrCreateSab(dmabufName, dmabufW, dmabufH, stride);
const copySize = offset + stride * dmabufH;
nativeDmabuf.copyMmapToSab(primaryFd, sabEntry.sab, copySize, offset);
surfaceUpdatePending.set(dmabufName, {
name: dmabufName,
width: dmabufW,
height: dmabufH,
stride,
generation: ++surfaceUpdateGeneration,
});
if (!surfaceUpdateFlushScheduled) {
surfaceUpdateFlushScheduled = true;
setImmediate(flushSurfaceUpdates);
}
// Preserve existing DMABUF texture notification for any consumers
const textureInfo = nativeDmabuf.importDmabufTexture(dmabufName, primaryFd, dmabufW, dmabufH, dmabufFormat);
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('wo:dmabuf-frame', { mainWindow.webContents.send('wo:dmabuf-frame', {
name: dmabufName, name: dmabufName,
@ -1283,7 +1030,9 @@ function connectToCompositor(): Promise<void> {
}); });
} }
} finally { } finally {
fs.closeSync(dmabufFd); for (const fd of planeFds) {
try { fs.closeSync(fd); } catch { /* ignore */ }
}
} }
} }
} }
@ -1704,74 +1453,71 @@ function setupIpcHandlers() {
return { ok: true }; return { ok: true };
} }
case 'tray_list': {
return getStatusNotifierTrayItems();
}
case 'tray_activate': {
const service = String(params.service || '');
const objectPath = String(params.objectPath || '');
if (!service || !objectPath) {
return { ok: false, error: 'Missing service or objectPath' };
}
const ok = activateStatusNotifierItem(service, objectPath);
return ok ? { ok: true } : { ok: false, error: 'Activation failed' };
}
case 'tray_secondary_activate': {
const service = String(params.service || '');
const objectPath = String(params.objectPath || '');
if (!service || !objectPath) {
return { ok: false, error: 'Missing service or objectPath' };
}
const ok = secondaryActivateStatusNotifierItem(service, objectPath);
return ok ? { ok: true } : { ok: false, error: 'Secondary activation failed' };
}
case 'tray_context_menu': {
const service = String(params.service || '');
const objectPath = String(params.objectPath || '');
const x = Number(params.x ?? 0);
const y = Number(params.y ?? 0);
if (!service || !objectPath) {
return { ok: false, error: 'Missing service or objectPath' };
}
const ok = openStatusNotifierContextMenu(service, objectPath, x, y);
return ok ? { ok: true } : { ok: false, error: 'Context menu failed' };
}
case 'tray_menu_items': {
const service = String(params.service || '');
const menuPath = String(params.menuPath || '');
if (!service || !menuPath) {
return [];
}
return getDBusMenuLayout(service, menuPath);
}
case 'tray_menu_event': {
const service = String(params.service || '');
const menuPath = String(params.menuPath || '');
const menuId = String(params.menuId || '');
if (!service || !menuPath || !menuId) {
return { ok: false, error: 'Missing parameters' };
}
const ok = triggerDBusMenuEvent(service, menuPath, menuId);
return ok ? { ok: true } : { ok: false, error: 'Menu event failed' };
}
case 'portal_respond': { case 'portal_respond': {
const requestId = String(params.requestId || ''); const requestId = String(params.requestId || '');
if (!requestId) { if (!requestId) {
return { ok: false, error: 'Missing portal requestId' }; return { ok: false, error: 'Missing portal requestId' };
} }
return respondToPortalRequest(requestId, { return respondToPortalRequest(requestId, {
allowed: Boolean(params.allowed), response: params.response,
allowed: params.allowed,
type: typeof params.type === 'string' ? params.type : undefined, type: typeof params.type === 'string' ? params.type : undefined,
windowName: params.windowName, windowName: params.windowName,
}); });
} }
case 'dbus_call': {
const bus: DbBus = params.bus === 'system' ? 'system' : 'user';
const service = String(params.service || '');
const objectPath = String(params.objectPath || '');
const iface = String(params.interface || params.iface || '');
const method = String(params.method || '');
const signature = typeof params.signature === 'string' ? params.signature : '';
const argList: any[] = Array.isArray(params.args) ? params.args : [];
if (!service || !objectPath || !iface || !method) {
return { ok: false, error: 'Missing dbus parameters' };
}
const args = ['call', service, objectPath, iface, method, signature || ''];
if (signature) {
args.push(...argList.map((v) => String(v)));
}
const result = runBusctl(bus, args);
return result;
}
case 'dbus_get_property': {
const bus: DbBus = params.bus === 'system' ? 'system' : 'user';
const service = String(params.service || '');
const objectPath = String(params.objectPath || '');
const iface = String(params.interface || params.iface || '');
const prop = String(params.property || params.prop || '');
if (!service || !objectPath || !iface || !prop) {
return { ok: false, error: 'Missing dbus parameters' };
}
const result = runBusctl(bus, ['get-property', service, objectPath, iface, prop]);
return result;
}
case 'load_local_file': {
const filePath = String(params.path || '');
if (!filePath) {
return { ok: false, error: 'Missing path' };
}
if (!fs.existsSync(filePath)) {
return { ok: false, error: `Path does not exist: ${filePath}` };
}
if (!mainWindow || mainWindow.isDestroyed()) {
return { ok: false, error: 'Main window unavailable' };
}
try {
debugLog('[Wo] Loading local file:', filePath);
await mainWindow.loadFile(filePath);
return { ok: true };
} catch (error) {
return { ok: false, error: String(error) };
}
}
default: default:
return { ok: false, error: `Unknown syscall: ${type}` }; return { ok: false, error: `Unknown syscall: ${type}` };
} }
@ -1830,11 +1576,15 @@ async function createWindow() {
} }
// In offscreen mode, we need actual content to trigger paint events // In offscreen mode, we need actual content to trigger paint events
// Load the configured URL or fallback to minimal page // Load the configured URL/file or fallback to minimal page
debugLog('[Wo] Loading content URL: ' + (url || 'minimal fallback')); debugLog('[Wo] Loading content URL: ' + (url || html || 'minimal fallback'));
try { try {
const pageUrl = url || 'data:text/html,<!DOCTYPE html><html><head><style>body{background:white;margin:0;}</style></head><body></body></html>'; const pageUrl = url
const loadPromise = mainWindow!.loadURL(pageUrl); || (html ? `file://${html}` : null)
|| 'data:text/html,<!DOCTYPE html><html><head><style>body{background:white;margin:0;}</style></head><body></body></html>';
const loadPromise = pageUrl.startsWith('file://')
? mainWindow!.loadURL(pageUrl)
: mainWindow!.loadURL(pageUrl);
// Give it time to load but don't wait forever // Give it time to load but don't wait forever
await Promise.race([ await Promise.race([
loadPromise, loadPromise,

View file

@ -120,15 +120,13 @@ const compositorAPI: CompositorAPI = {
// Generic portal request event for client-provided popup/approval handlers // Generic portal request event for client-provided popup/approval handlers
onPortalRequest: (callback: (data: { onPortalRequest: (callback: (data: {
requestId: string; requestId: string;
kind: string; payload: any;
appName?: string; kind?: string;
sessionId?: string;
}) => void) => { }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, data: { const handler = (_event: Electron.IpcRendererEvent, data: {
requestId: string; requestId: string;
kind: string; payload: any;
appName?: string; kind?: string;
sessionId?: string;
}) => callback(data); }) => callback(data);
ipcRenderer.on('wo:portal-request', handler); ipcRenderer.on('wo:portal-request', handler);
return () => ipcRenderer.removeListener('wo:portal-request', handler); return () => ipcRenderer.removeListener('wo:portal-request', handler);

View file

@ -2,7 +2,7 @@
"name": "@wl-wo/wo-types", "name": "@wl-wo/wo-types",
"module": "index.ts", "module": "index.ts",
"type": "module", "type": "module",
"version": "1.0.0", "version": "1.0.1",
"devDependencies": { "devDependencies": {
"@types/bun": "latest" "@types/bun": "latest"
}, },

View file

@ -88,7 +88,10 @@ export interface SyscallLogoutParams { [key: string]: any; }
export interface SyscallLockParams { [key: string]: any; } export interface SyscallLockParams { [key: string]: any; }
export interface SyscallSleepParams { [key: string]: any; } export interface SyscallSleepParams { [key: string]: any; }
export interface SyscallNotifyParams { id?: string; title: string; body?: string; icon?: string; timeout?: number; [key: string]: any; } export interface SyscallNotifyParams { id?: string; title: string; body?: string; icon?: string; timeout?: number; [key: string]: any; }
export interface SyscallPortalRespondParams { requestId: string; allowed: boolean; type?: string; windowName?: string | null; [key: string]: any; } export interface SyscallPortalRespondParams { requestId: string; response?: any; allowed?: boolean; type?: string; windowName?: string | null; [key: string]: any; }
export interface SyscallDbusCallParams { service: string; objectPath: string; interface: string; method: string; signature?: string; args?: any[]; bus?: 'user' | 'system'; [key: string]: any; }
export interface SyscallDbusGetPropertyParams { service: string; objectPath: string; interface: string; property: string; bus?: 'user' | 'system'; [key: string]: any; }
export interface SyscallLoadLocalFileParams { path: string; [key: string]: any; }
export interface SyscallLaunchResult { ok: boolean; pid?: number; error?: string; } export interface SyscallLaunchResult { ok: boolean; pid?: number; error?: string; }
export interface SyscallExecResult { ok: boolean; pid?: number; error?: string; } export interface SyscallExecResult { ok: boolean; pid?: number; error?: string; }
@ -102,12 +105,14 @@ export interface SyscallLockResult { ok: boolean; error?: string; }
export interface SyscallSleepResult { ok: boolean; error?: string; } export interface SyscallSleepResult { ok: boolean; error?: string; }
export interface SyscallNotifyResult { ok: boolean; error?: string; } export interface SyscallNotifyResult { ok: boolean; error?: string; }
export interface SyscallPortalRespondResult { ok: boolean; error?: string; } export interface SyscallPortalRespondResult { ok: boolean; error?: string; }
export interface SyscallDbusCallResult { ok: boolean; stdout?: string; stderr?: string; exitCode?: number; error?: string; }
export interface SyscallDbusGetPropertyResult { ok: boolean; stdout?: string; stderr?: string; exitCode?: number; error?: string; }
export interface SyscallLoadLocalFileResult { ok: boolean; error?: string; }
export interface PortalRequestEvent { export interface PortalRequestEvent {
requestId: string; requestId: string;
kind: string; payload: any;
appName?: string; kind?: string;
sessionId?: string;
} }
export interface ScreencopyEvent { export interface ScreencopyEvent {
@ -217,6 +222,9 @@ export interface Compositor {
syscall(type: 'sleep', params?: SyscallSleepParams): Promise<SyscallSleepResult>; syscall(type: 'sleep', params?: SyscallSleepParams): Promise<SyscallSleepResult>;
syscall(type: 'notify', params: SyscallNotifyParams): Promise<SyscallNotifyResult>; syscall(type: 'notify', params: SyscallNotifyParams): Promise<SyscallNotifyResult>;
syscall(type: 'portal_respond', params: SyscallPortalRespondParams): Promise<SyscallPortalRespondResult>; syscall(type: 'portal_respond', params: SyscallPortalRespondParams): Promise<SyscallPortalRespondResult>;
syscall(type: 'dbus_call', params: SyscallDbusCallParams): Promise<SyscallDbusCallResult>;
syscall(type: 'dbus_get_property', params: SyscallDbusGetPropertyParams): Promise<SyscallDbusGetPropertyResult>;
syscall(type: 'load_local_file', params: SyscallLoadLocalFileParams): Promise<SyscallLoadLocalFileResult>;
syscall(type: string, params?: any): Promise<any>; syscall(type: string, params?: any): Promise<any>;
/** /**

48
electron/whatever.txt Normal file
View file

@ -0,0 +1,48 @@
Finished `release` profile [optimized] target(s) in 0.12s
Finished `release` profile [optimized] target(s) in 0.12s
Running `/home/hellings/Documents/wo/target/release/wo`
Running `/home/hellings/Documents/wo/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 <FK23>
> The XKEYBOARD keymap compiler (xkbcomp) reports:
> Warning: Multiple symbols for level 1/group 1 on key <FK23>
> Using F23, ignoring XF86TouchpadOff
> Warning: Symbol map for key <FK23> redefined
> Using last definition for conflicting fields
> Warning: Symbol map for key <FK24> redefined
> Using last definition for conflicting fields
Using F23, ignoring XF86TouchpadOff
> Warning: Symbol map for key <FK23> redefined
> Using last definition for conflicting fields
> Warning: Symbol map for key <FK24> 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: > Warning: Virtual modifier Hyper multiply defined
> Using 0, ignoring 0
> Warning: Virtual modifier ScrollLock multiply defined
> Using 0, ignoring 0
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
[4120:0311/124914.666158:ERROR:gpu/ipc/service/gpu_init.cc:209] Vulkan not supported with in process gpu
[4120:0311/124914.666158:ERROR:gpu/ipc/service/gpu_init.cc:209] Vulkan not supported with in process gpu
(EE) failed to read Wayland events: Broken pipe
(EE) failed to read Wayland events: Broken pipe

View file

@ -296,6 +296,8 @@ impl ElectronClientConnection {
// Get buffer properties // Get buffer properties
let num_planes = dmabuf.num_planes(); let num_planes = dmabuf.num_planes();
let offsets: Vec<u32> = dmabuf.offsets().collect();
let strides: Vec<u32> = dmabuf.strides().collect();
let (w, h) = { let (w, h) = {
let size = dmabuf.size(); let size = dmabuf.size();
(size.w as u32, size.h as u32) (size.w as u32, size.h as u32)
@ -330,15 +332,14 @@ impl ElectronClientConnection {
// For each plane: fd(4) + offset(8) + stride(4) + modifier(8) // For each plane: fd(4) + offset(8) + stride(4) + modifier(8)
// Note: We'll collect actual plane data from dmabuf handles iterator // Note: We'll collect actual plane data from dmabuf handles iterator
for _handle in dmabuf.handles() { for plane_idx in 0..num_planes {
// fd: will be sent via ancillary data, so we put 0 as placeholder // fd placeholder — actual fds are sent via SCM_RIGHTS ancillary data
buf[off..off + 4].copy_from_slice(&0u32.to_le_bytes()); buf[off..off + 4].copy_from_slice(&0u32.to_le_bytes());
off += 4; off += 4;
// offset: 0 for now (can be extended later) let offset = offsets.get(plane_idx).copied().unwrap_or(0);
buf[off..off + 8].copy_from_slice(&0u64.to_le_bytes()); buf[off..off + 8].copy_from_slice(&(offset as u64).to_le_bytes());
off += 8; off += 8;
// stride: will depend on format (approximate) let stride = strides.get(plane_idx).copied().unwrap_or(w * 4);
let stride = (w * 4) as u32; // ARGB8888 = 4 bytes/pixel, adjust as needed
buf[off..off + 4].copy_from_slice(&stride.to_le_bytes()); buf[off..off + 4].copy_from_slice(&stride.to_le_bytes());
off += 4; off += 4;
// modifier (8 bytes) - DrmModifier converts to u64 // modifier (8 bytes) - DrmModifier converts to u64
@ -595,43 +596,30 @@ impl ElectronProcess {
let main_js = app_dir.join("dist/main.js"); let main_js = app_dir.join("dist/main.js");
let child = Command::new(electron_bin) let child = Command::new(electron_bin)
.arg(&main_js)
.env("WO_IPC_SOCKET", ipc_socket) .env("WO_IPC_SOCKET", ipc_socket)
.env("WO_WINDOW_CONFIG", &serialized) .env("WO_WINDOW_CONFIG", &serialized)
.env("WO_DRM_RENDER_NODE", render_node) .env("WO_DRM_RENDER_NODE", render_node)
// Wayland Ozone platform: Electron connects to our own Wayland // ── Chromium flags ──────────────────────────────────────
// compositor (WAYLAND_DISPLAY is already set in the env) for // These MUST appear before the app script on the command
// GPU/EGL initialisation. This gives Chromium a proper // line. Electron treats everything after the script path
// wayland-egl display so hardware-accelerated rasterisation // as application arguments (process.argv), NOT Chromium
// works. The BrowserWindow still uses `offscreen: true`, so // switches. Flags here also reach the GPU subprocess
// no Wayland surfaces are mapped — the connection is used // (critical for --in-process-gpu and --use-gl).
// solely for the EGL context.
// //
// The previous `--ozone-platform=headless` approach failed // Wayland Ozone platform: Electron connects to our own
// because headless Ozone has no EGL display, causing the GPU // Wayland compositor (WAYLAND_DISPLAY is already set in
// subprocess to crash with "gl=none,angle=none not found" and // the env) for GPU/EGL initialisation.
// silently falling back to ~1 FPS software rasterisation.
.arg("--ozone-platform=wayland") .arg("--ozone-platform=wayland")
.arg("--no-sandbox") .arg("--no-sandbox")
.arg("--disable-gpu-sandbox") .arg("--disable-gpu-sandbox")
// Run GPU code in the main Electron process to avoid the .arg("--use-gl=angle")
// separate GPU subprocess which adds IPC overhead and can .arg("--use-angle=vulkan")
// 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") .arg("--disable-gpu-vsync")
// Enable GPU-accelerated rasterization and zero-copy features .arg("--enable-features=CanvasOopRasterization,Vulkan,VaapiVideoDecoder,RawDraw,SharedArrayBuffer,DefaultANGLEVulkan,VulkanFromANGLE")
// 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") .arg("--num-raster-threads=4")
// Don't throttle renderers that lose focus.
.arg("--disable-renderer-backgrounding") .arg("--disable-renderer-backgrounding")
// ── App entry point (must come after Chromium flags) ────
.arg(&main_js)
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.stderr(Stdio::inherit()) .stderr(Stdio::inherit())

View file

@ -165,7 +165,7 @@ impl WoState {
self.pointer_location = new_pos; self.pointer_location = new_pos;
// Process active grab (interactive move/resize) before normal dispatch // Process active grab (interactive move/resize) before normal dispatch
if let Some(ref grab) = self.grab_state.clone() { if let Some(grab) = self.grab_state.clone() {
match grab { match grab {
GrabState::Move { GrabState::Move {
window, window,
@ -178,7 +178,18 @@ impl WoState {
initial_window_location.x + dx as i32, initial_window_location.x + dx as i32,
initial_window_location.y + dy as i32, initial_window_location.y + dy as i32,
); );
let prev_loc = self
.space
.element_location(&window)
.unwrap_or(initial_window_location);
self.space.map_element(window.clone(), new_loc, false); self.space.map_element(window.clone(), new_loc, false);
self.translate_dialog_children(
&window,
new_loc.0 - prev_loc.x,
new_loc.1 - prev_loc.y,
);
self.metadata_dirty = true;
} }
GrabState::Resize { GrabState::Resize {
surface, surface,
@ -192,11 +203,11 @@ impl WoState {
let dx = (self.pointer_location.x - initial_pointer.x) as i32; let dx = (self.pointer_location.x - initial_pointer.x) as i32;
let dy = (self.pointer_location.y - initial_pointer.y) as i32; let dy = (self.pointer_location.y - initial_pointer.y) as i32;
let (mut new_w, mut new_h) = *initial_window_size; let (mut new_w, mut new_h) = initial_window_size;
let (mut new_x, mut new_y) = let (mut new_x, mut new_y) =
(initial_window_location.x, initial_window_location.y); (initial_window_location.x, initial_window_location.y);
match *edges { match edges {
ResizeEdge::Right | ResizeEdge::TopRight | ResizeEdge::BottomRight => { ResizeEdge::Right | ResizeEdge::TopRight | ResizeEdge::BottomRight => {
new_w = (new_w + dx).max(1); new_w = (new_w + dx).max(1);
} }
@ -206,7 +217,7 @@ impl WoState {
} }
_ => {} _ => {}
} }
match *edges { match edges {
ResizeEdge::Bottom | ResizeEdge::BottomLeft | ResizeEdge::BottomRight => { ResizeEdge::Bottom | ResizeEdge::BottomLeft | ResizeEdge::BottomRight => {
new_h = (new_h + dy).max(1); new_h = (new_h + dy).max(1);
} }
@ -218,14 +229,24 @@ impl WoState {
} }
// Respect client min/max size constraints. // Respect client min/max size constraints.
let (clamped_w, clamped_h) = self.clamp_to_size_hints(window, new_w, new_h); let (clamped_w, clamped_h) = self.clamp_to_size_hints(&window, new_w, new_h);
surface.with_pending_state(|s| { surface.with_pending_state(|s| {
s.size = Some((clamped_w, clamped_h).into()); s.size = Some((clamped_w, clamped_h).into());
}); });
surface.send_configure(); surface.send_configure();
let prev_loc = self
.space
.element_location(&window)
.unwrap_or(initial_window_location);
self.space self.space
.map_element(window.clone(), (new_x, new_y), false); .map_element(window.clone(), (new_x, new_y), false);
self.translate_dialog_children(
&window,
new_x - prev_loc.x,
new_y - prev_loc.y,
);
self.metadata_dirty = true;
} }
} }
return; return;
@ -243,7 +264,7 @@ impl WoState {
self.pointer_location = new_pos; self.pointer_location = new_pos;
// Process active grab (interactive move/resize) before normal dispatch // Process active grab (interactive move/resize) before normal dispatch
if let Some(ref grab) = self.grab_state.clone() { if let Some(grab) = self.grab_state.clone() {
match grab { match grab {
GrabState::Move { GrabState::Move {
window, window,
@ -256,7 +277,17 @@ impl WoState {
initial_window_location.x + dx as i32, initial_window_location.x + dx as i32,
initial_window_location.y + dy as i32, initial_window_location.y + dy as i32,
); );
let prev_loc = self
.space
.element_location(&window)
.unwrap_or(initial_window_location);
self.space.map_element(window.clone(), new_loc, false); self.space.map_element(window.clone(), new_loc, false);
self.translate_dialog_children(
&window,
new_loc.0 - prev_loc.x,
new_loc.1 - prev_loc.y,
);
self.metadata_dirty = true;
} }
GrabState::Resize { GrabState::Resize {
surface, surface,
@ -270,11 +301,11 @@ impl WoState {
let dx = (self.pointer_location.x - initial_pointer.x) as i32; let dx = (self.pointer_location.x - initial_pointer.x) as i32;
let dy = (self.pointer_location.y - initial_pointer.y) as i32; let dy = (self.pointer_location.y - initial_pointer.y) as i32;
let (mut new_w, mut new_h) = *initial_window_size; let (mut new_w, mut new_h) = initial_window_size;
let (mut new_x, mut new_y) = let (mut new_x, mut new_y) =
(initial_window_location.x, initial_window_location.y); (initial_window_location.x, initial_window_location.y);
match *edges { match edges {
ResizeEdge::Right | ResizeEdge::TopRight | ResizeEdge::BottomRight => { ResizeEdge::Right | ResizeEdge::TopRight | ResizeEdge::BottomRight => {
new_w = (new_w + dx).max(1); new_w = (new_w + dx).max(1);
} }
@ -284,7 +315,7 @@ impl WoState {
} }
_ => {} _ => {}
} }
match *edges { match edges {
ResizeEdge::Bottom | ResizeEdge::BottomLeft | ResizeEdge::BottomRight => { ResizeEdge::Bottom | ResizeEdge::BottomLeft | ResizeEdge::BottomRight => {
new_h = (new_h + dy).max(1); new_h = (new_h + dy).max(1);
} }
@ -296,14 +327,24 @@ impl WoState {
} }
// Respect client min/max size constraints. // Respect client min/max size constraints.
let (clamped_w, clamped_h) = self.clamp_to_size_hints(window, new_w, new_h); let (clamped_w, clamped_h) = self.clamp_to_size_hints(&window, new_w, new_h);
surface.with_pending_state(|s| { surface.with_pending_state(|s| {
s.size = Some((clamped_w, clamped_h).into()); s.size = Some((clamped_w, clamped_h).into());
}); });
surface.send_configure(); surface.send_configure();
let prev_loc = self
.space
.element_location(&window)
.unwrap_or(initial_window_location);
self.space self.space
.map_element(window.clone(), (new_x, new_y), false); .map_element(window.clone(), (new_x, new_y), false);
self.translate_dialog_children(
&window,
new_x - prev_loc.x,
new_y - prev_loc.y,
);
self.metadata_dirty = true;
} }
} }
return; return;

View file

@ -1708,6 +1708,14 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> {
} }
} }
// Bind the Wayland listening socket BEFORE spawning Electron so that
// Chromium's Ozone/Wayland backend can connect immediately. Without
// this, `--ozone-platform=wayland` fails with "Connection refused"
// because the socket is not yet accepting connections.
let wayland_socket =
ListeningSocket::bind(&config.compositor.socket_name).context("binding Wayland socket")?;
info!(socket = %config.compositor.socket_name, "Wayland socket bound");
std::env::set_var("WAYLAND_DISPLAY", &config.compositor.socket_name); std::env::set_var("WAYLAND_DISPLAY", &config.compositor.socket_name);
// Propagate now; DISPLAY will be re-propagated once XWayland is ready. // Propagate now; DISPLAY will be re-propagated once XWayland is ready.
crate::state::propagate_environment(&["WAYLAND_DISPLAY"]); crate::state::propagate_environment(&["WAYLAND_DISPLAY"]);
@ -1805,10 +1813,6 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> {
}; };
let last_render = std::time::Instant::now(); let last_render = std::time::Instant::now();
let wayland_socket =
ListeningSocket::bind(&config.compositor.socket_name).context("binding Wayland socket")?;
info!(socket = %config.compositor.socket_name, "Wayland socket bound");
// Register the Wayland display fd with calloop so that client messages // Register the Wayland display fd with calloop so that client messages
// (surface commits, etc.) wake the event loop. Without this, the loop // (surface commits, etc.) wake the event loop. Without this, the loop
// only wakes on DRM VBlank / libinput / session events and rendering // only wakes on DRM VBlank / libinput / session events and rendering
@ -2167,7 +2171,14 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> {
.and_then(|p| p.get("y")) .and_then(|p| p.get("y"))
.and_then(|v| v.as_i64()) .and_then(|v| v.as_i64())
.unwrap_or(0) as i32; .unwrap_or(0) as i32;
data.state.space.map_element(window, (x, y + th), false); let prev_loc = data.state.space.element_location(&window).unwrap_or_default();
data.state.space.map_element(window.clone(), (x, y + th), false);
data.state.translate_dialog_children(
&window,
x - prev_loc.x,
y + th - prev_loc.y,
);
data.state.metadata_dirty = true;
} }
"resize" => { "resize" => {
let new_w = payload_json.as_ref() let new_w = payload_json.as_ref()

View file

@ -1310,7 +1310,14 @@ fn process_electron_message(
.unwrap_or(0) as i32; .unwrap_or(0) as i32;
// comraw reports the MacWindow origin (title bar top); // comraw reports the MacWindow origin (title bar top);
// the Wayland surface sits th pixels below that. // the Wayland surface sits th pixels below that.
state.space.map_element(window, (x, y + th), false); let prev_loc = state.space.element_location(&window).unwrap_or_default();
state.space.map_element(window.clone(), (x, y + th), false);
state.translate_dialog_children(
&window,
x - prev_loc.x,
y + th - prev_loc.y,
);
state.metadata_dirty = true;
} }
"resize" => { "resize" => {
let new_w = payload_json let new_w = payload_json

View file

@ -19,7 +19,7 @@ use smithay::{
relative_pointer::RelativePointerManagerState, relative_pointer::RelativePointerManagerState,
pointer_constraints::{self, PointerConstraintsHandler, PointerConstraintsState}, pointer_constraints::{self, PointerConstraintsHandler, PointerConstraintsState},
keyboard_shortcuts_inhibit::{KeyboardShortcutsInhibitHandler, KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor}, keyboard_shortcuts_inhibit::{KeyboardShortcutsInhibitHandler, KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor},
shell::xdg::{XdgShellState, decoration::XdgDecorationState}, shm::ShmState, single_pixel_buffer::SinglePixelBufferState, tablet_manager::TabletSeatHandler, viewporter::ViewporterState, xdg_activation::{XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData}, xwayland_shell::XWaylandShellState shell::xdg::{XdgShellState, XdgToplevelSurfaceData, decoration::XdgDecorationState}, shm::ShmState, single_pixel_buffer::SinglePixelBufferState, tablet_manager::TabletSeatHandler, viewporter::ViewporterState, xdg_activation::{XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData}, xwayland_shell::XWaylandShellState
}, },
xwayland::{XWayland, xwm::X11Wm}, xwayland::{XWayland, xwm::X11Wm},
}; };
@ -558,6 +558,65 @@ impl WoState {
None None
} }
/// Return the stable parent name (Wayland or X11) for a window, if any.
pub fn parent_name_for_window(&self, window: &Window) -> Option<String> {
if let Some(x11) = window.x11_surface() {
if let Some(parent_id) = x11.is_transient_for() {
return self.x11_window_names.get(&parent_id).cloned();
}
}
let parent_surface = window.toplevel().and_then(|t| {
with_states(t.wl_surface(), |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.and_then(|d| d.lock().ok().and_then(|data| data.parent.clone()))
})
});
if let Some(parent_wl) = parent_surface {
use smithay::reexports::wayland_server::Resource;
return self.wayland_window_names.get(&parent_wl.id()).cloned();
}
None
}
/// Translate all dialog/transient children of `parent` by (dx, dy) to keep them anchored.
pub fn translate_dialog_children(&mut self, parent: &Window, dx: i32, dy: i32) {
if dx == 0 && dy == 0 {
return;
}
let parent_name = match self.wayland_window_name(parent) {
Some(name) => name.clone(),
None => return,
};
let children: Vec<_> = self
.all_windows
.iter()
.cloned()
.filter(|win| {
if let Some(child_parent) = self.parent_name_for_window(win) {
child_parent == parent_name
} else {
false
}
})
.collect();
for child in children {
if let Some(loc) = self.space.element_location(&child) {
self.space
.map_element(child.clone(), (loc.x + dx, loc.y + dy), false);
}
}
self.metadata_dirty = true;
}
/// Find a Wayland or X11 Window element by its stable name (even if minimized). /// Find a Wayland or X11 Window element by its stable name (even if minimized).
pub fn find_wayland_window(&self, name: &str) -> Option<Window> { pub fn find_wayland_window(&self, name: &str) -> Option<Window> {
self.all_windows.iter().find(|w| { self.all_windows.iter().find(|w| {

View file

@ -1,337 +1,7 @@
warning: unused import: `Buffer as BufferCoord` Compiling wo v0.1.0 (/home/hellings/Documents/wo)
--> src/cursor.rs:10:22 Compiling wo v0.1.0 (/home/hellings/Documents/wo)
| Finished `release` profile [optimized] target(s) in 2m 21s
10 | use smithay::utils::{Buffer as BufferCoord, Size}; Finished `release` profile [optimized] target(s) in 2m 21s
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
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
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::<N, Kind>::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::<N, Kind>::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::<N, Kind>::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::<N, Kind>::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<B>,
| ^^^^^ 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<B>,
| ^^^^^ 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<i32, Physical>) -> Self {
| ^^^
| impl Frame {
| ---------- associated function in this implementation
45 | pub fn new(output_rect: Rectangle<i32, Physical>) -> 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`
Running `target/release/wo` Running `target/release/wo`
Xwayland glamor: GBM Wayland interfaces not available Xwayland glamor: GBM Wayland interfaces not available
@ -374,73 +44,179 @@ The XKEYBOARD keymap compiler (xkbcomp) reports:
> Using 0, ignoring 0 > Using 0, ignoring 0
Errors from xkbcomp are not fatal to the X server Errors from xkbcomp are not fatal to the X server
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)]. Authorization required, but no authorization protocol specified
[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 Authorization required, but no authorization protocol specified
[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)]. Authorization required, but no authorization protocol specified
[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 Authorization required, but no authorization protocol specified
[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)]. Gtk-Message: 13:58:16.908: Failed to load module "appmenu-gtk-module"
[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)]. Gtk-Message: 13:58:16.908: Failed to load module "appmenu-gtk-module"
[75966:0310/190621.261808:ERROR:components/viz/service/main/viz_main_impl.cc:189] Exiting GPU process due to errors during initialization Authorization required, but no authorization protocol specified
[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. Authorization required, but no authorization protocol specified
(Use `electron --trace-deprecation ...` to show where the warning was created)
(node:75859) DeprecationWarning: getBitmap() is deprecated, use toBitmap() instead. [24415:0311/135817.250839:ERROR:ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc:250] '--ozone-platform=wayland' is not compatible with Vulkan. Consider switching to '--ozone-platform=x11' or disabling Vulkan
(Use `electron --trace-deprecation ...` to show where the warning was created) [24415:0311/135817.250839:ERROR:ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc:250] '--ozone-platform=wayland' is not compatible with Vulkan. Consider switching to '--ozone-platform=x11' or disabling Vulkan
Error sending from webFrameMain: Error: Failed to serialize arguments [24415:0311/135817.264878:ERROR:components/viz/service/display_embedder/skia_output_device_vulkan.cc:301] Failed to create vulkan surface.
[24415:0311/135817.264878:ERROR:components/viz/service/display_embedder/skia_output_device_vulkan.cc:301] Failed to create vulkan surface.
[24330:0311/135939.036070:ERROR:content/browser/network_service_instance_impl.cc:613] Network service crashed, restarting service.
[24330:0311/135939.036070:ERROR:content/browser/network_service_instance_impl.cc:613] Network service crashed, restarting service.
[24330:0311/135939.185963:ERROR:content/browser/gpu/gpu_process_host.cc:999] GPU process exited unexpectedly: exit_code=15
[24330:0311/135939.185963:ERROR:content/browser/gpu/gpu_process_host.cc:999] GPU process exited unexpectedly: exit_code=15
Error sending from webFrameMain: Error: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266) at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687) 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.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1251:30)
at Socket.emit (node:events:519:28) at Socket.emit (node:events:519:28)
at addChunk (node:internal/streams/readable:561:12) at addChunk (node:internal/streams/readable:561:12)
at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)
at Readable.push (node:internal/streams/readable:392:5) at Readable.push (node:internal/streams/readable:392:5)
at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)
Error sending from webFrameMain: Error: Failed to serialize arguments Error sending from webFrameMain: Error: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266) at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687) 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.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1251:30)
at Socket.emit (node:events:519:28) at Socket.emit (node:events:519:28)
at addChunk (node:internal/streams/readable:561:12) at addChunk (node:internal/streams/readable:561:12)
at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)
at Readable.push (node:internal/streams/readable:392:5) at Readable.push (node:internal/streams/readable:392:5)
at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)
Error sending from webFrameMain: Error: Failed to serialize arguments Error sending from webFrameMain: Error: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266) at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687) 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.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1251:30)
at Socket.emit (node:events:519:28) at Socket.emit (node:events:519:28)
at addChunk (node:internal/streams/readable:561:12) at addChunk (node:internal/streams/readable:561:12)
at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)
at Readable.push (node:internal/streams/readable:392:5) at Readable.push (node:internal/streams/readable:392:5)
at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)
Error sending from webFrameMain: Error: Failed to serialize arguments Error sending from webFrameMain: Error: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266) at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687) 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.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
at Socket.<anonymous> (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)
Authorization required, but no authorization protocol specified
Authorization required, but no authorization protocol specified
Error sending from webFrameMain: Error: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
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)
Authorization required, but no authorization protocol specified
Authorization required, but no authorization protocol specified
Error sending from webFrameMain: Error: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1213:36)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
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: Render frame was disposed before WebFrameMain could be accessed
at s.send (node:electron/js2c/browser_init:2:100266)
at b.send (node:electron/js2c/browser_init:2:84687)
at Socket.<anonymous> (file:///home/hellings/Documents/wo/electron/dist/main.js:1235:40)
at Socket.emit (node:events:519:28) at Socket.emit (node:events:519:28)
at addChunk (node:internal/streams/readable:561:12) at addChunk (node:internal/streams/readable:561:12)
at readableAddChunkPushByteMode (node:internal/streams/readable:512:3) at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)
at Readable.push (node:internal/streams/readable:392:5) at Readable.push (node:internal/streams/readable:392:5)
at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) 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
(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