feat(electrobun): native C resize library (Wails pattern)
New: agor-pty/native/agor_resize.c — C shared library that: - Connects button-press-event on GtkWindow + WebView children in C - Stores mouseButton, xroot, yroot, dragTime from real GTK events - Exports agor_resize_start(edge) for Bun FFI Build: gcc -shared -fPIC -o libagor-resize.so agor_resize.c $(pkg-config --cflags --libs gtk+-3.0) New: ui-electrobun/src/bun/native-resize.ts — Bun FFI wrapper App.svelte: simplified to just edge detection + RPC call
This commit is contained in:
parent
de40bcbcac
commit
178e560068
5 changed files with 244 additions and 62 deletions
|
|
@ -109,14 +109,11 @@ const rpc = BrowserView.defineRPC<PtyRPCSchema>({
|
|||
...miscHandlers,
|
||||
|
||||
// GTK native drag/resize — delegates to window manager (zero CPU)
|
||||
"window.beginResize": ({ edge, button, rootX, rootY }: { edge: string; button: number; rootX: number; rootY: number }) => {
|
||||
"window.beginResize": ({ edge }: { edge: string }) => {
|
||||
try {
|
||||
const { beginResizeDrag, edgeStringToGdk } = require("./gtk-window.ts");
|
||||
const gdkEdge = edgeStringToGdk(edge);
|
||||
if (gdkEdge === null) return { ok: false, error: `Unknown edge: ${edge}` };
|
||||
console.log(`[resize] edge=${edge} gdkEdge=${gdkEdge} btn=${button} rootX=${rootX} rootY=${rootY} ptr=${(mainWindow as any).ptr}`);
|
||||
const ok = beginResizeDrag((mainWindow as any).ptr, gdkEdge, button, rootX, rootY);
|
||||
console.log(`[resize] result: ${ok}`);
|
||||
// Uses native C library — stored mouse state from real GTK event
|
||||
const { startNativeResize } = require("./native-resize.ts");
|
||||
const ok = startNativeResize(edge);
|
||||
return { ok };
|
||||
} catch (err) { console.error("[window.beginResize]", err); return { ok: false }; }
|
||||
},
|
||||
|
|
@ -240,11 +237,12 @@ mainWindow = new BrowserWindow({
|
|||
},
|
||||
});
|
||||
|
||||
// GTK window setup — only log resizable status, skip all FFI modifications
|
||||
// (forceSmallMinSize and wrapWebViewInScrolledWindow caused crashes)
|
||||
// Native resize via C shared library (libagor-resize.so)
|
||||
// Owns GTK signal connections in C — no JSCallback boundary crossing
|
||||
try {
|
||||
console.log("[gtk] Window ptr:", (mainWindow as any).ptr);
|
||||
} catch (e) { console.error("[gtk] ptr access failed:", e); }
|
||||
const { initNativeResize } = require("./native-resize.ts");
|
||||
initNativeResize((mainWindow as any).ptr, 8);
|
||||
} catch (e) { console.error("[native-resize] init failed:", e); }
|
||||
|
||||
// Prevent GTK's false Ctrl+click detection from closing the window on initial load.
|
||||
// WebKitGTK reports stale modifier state (0x14 = Ctrl+Alt) after SIGTERM of previous instance,
|
||||
|
|
|
|||
94
ui-electrobun/src/bun/native-resize.ts
Normal file
94
ui-electrobun/src/bun/native-resize.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* Native resize via libagor-resize.so — C shared library that owns
|
||||
* GTK signal connections and calls begin_resize_drag with real event data.
|
||||
*
|
||||
* The C library:
|
||||
* 1. Connects button-press-event on GtkWindow + WebView children (in C)
|
||||
* 2. Stores mouseButton, xroot, yroot, dragTime from real GTK events
|
||||
* 3. Exports agor_resize_start(edge) that Bun calls via FFI
|
||||
*
|
||||
* This avoids JSCallback boundary crossing (which crashes in Bun).
|
||||
*/
|
||||
|
||||
import { dlopen, FFIType } from "bun:ffi";
|
||||
import { join } from "path";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
let lib: ReturnType<typeof dlopen> | null = null;
|
||||
|
||||
function loadLib(): ReturnType<typeof dlopen> | null {
|
||||
if (lib) return lib;
|
||||
|
||||
// Search paths for the .so
|
||||
const candidates = [
|
||||
join(import.meta.dir, "../../agor-pty/native/libagor-resize.so"),
|
||||
join(import.meta.dir, "../../../agor-pty/native/libagor-resize.so"),
|
||||
"/home/hibryda/code/ai/agent-orchestrator/agor-pty/native/libagor-resize.so",
|
||||
];
|
||||
|
||||
const soPath = candidates.find(p => existsSync(p));
|
||||
if (!soPath) {
|
||||
console.error("[native-resize] libagor-resize.so not found. Searched:", candidates);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
lib = dlopen(soPath, {
|
||||
agor_resize_init: {
|
||||
args: [FFIType.ptr, FFIType.i32],
|
||||
returns: FFIType.void,
|
||||
},
|
||||
agor_resize_start: {
|
||||
args: [FFIType.i32],
|
||||
returns: FFIType.i32,
|
||||
},
|
||||
});
|
||||
console.log("[native-resize] Loaded:", soPath);
|
||||
return lib;
|
||||
} catch (err) {
|
||||
console.error("[native-resize] Failed to load:", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize native resize handler. Call once after window creation.
|
||||
*/
|
||||
export function initNativeResize(windowPtr: number | bigint, borderPx: number = 8): boolean {
|
||||
const l = loadLib();
|
||||
if (!l) return false;
|
||||
try {
|
||||
l.symbols.agor_resize_init(windowPtr as any, borderPx);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("[native-resize] init failed:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// GdkWindowEdge mapping
|
||||
const EDGE_MAP: Record<string, number> = {
|
||||
n: 1, s: 6, e: 4, w: 3,
|
||||
ne: 2, nw: 0, se: 7, sw: 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a resize drag. Call from RPC when JS detects mousedown in border zone.
|
||||
* The C library uses stored mouse state from the real GTK button-press event.
|
||||
*/
|
||||
export function startNativeResize(edge: string): boolean {
|
||||
const l = loadLib();
|
||||
if (!l) return false;
|
||||
const gdkEdge = EDGE_MAP[edge];
|
||||
if (gdkEdge === undefined) {
|
||||
console.error("[native-resize] Unknown edge:", edge);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const ok = l.symbols.agor_resize_start(gdkEdge);
|
||||
return ok === 1;
|
||||
} catch (err) {
|
||||
console.error("[native-resize] start failed:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue