fix(electrobun): disable WebView sensitivity during resize to prevent grab loss

Codex review #3 identified: the issue is NOT min-size but pointer grab
conflict. WebKitGTK steals the WM's X11 pointer grab when cursor enters
the WebView during inward resize.

Fix: gtk_widget_set_sensitive(webview, false) before begin_resize_drag,
re-enable after 5s. Also added findWebView() to cache the deepest
GtkBin child pointer for fast access.
This commit is contained in:
Hibryda 2026-03-25 13:47:05 +01:00
parent 300bd30ca3
commit 7bb08697d6

View file

@ -104,6 +104,15 @@ function getGtk() {
args: [FFIType.ptr],
returns: FFIType.void,
},
// Sensitivity (disable input processing on widget)
gtk_widget_set_sensitive: {
args: [FFIType.ptr, FFIType.bool],
returns: FFIType.void,
},
gtk_widget_get_sensitive: {
args: [FFIType.ptr],
returns: FFIType.bool,
},
// Expand flags
gtk_widget_set_hexpand: {
args: [FFIType.ptr, FFIType.bool],
@ -210,9 +219,41 @@ function forceSmallMinSize(lib: NonNullable<typeof gtk3>, windowPtr: any) {
} catch { /* ignore */ }
}
/** Cache the WebView widget pointer for sensitivity toggling during resize */
let cachedWebView: any = null;
function findWebView(lib: NonNullable<typeof gtk3>, windowPtr: any): any {
if (cachedWebView) return cachedWebView;
try {
// Window → container → scrolledwindow/webview
let widget = lib.symbols.gtk_bin_get_child(windowPtr);
// Walk down the bin chain to find the deepest child (the WebView)
for (let i = 0; i < 5; i++) {
const child = lib.symbols.gtk_bin_get_child(widget);
if (!child) break;
widget = child;
}
cachedWebView = widget;
return widget;
} catch { return null; }
}
/**
* Temporarily disable WebView input during resize to prevent grab interference.
* Re-enable after resize completes (WM sends configure-event).
*/
export function setWebViewSensitive(windowPtr: number | bigint, sensitive: boolean) {
const lib = getGtk();
if (!lib) return;
const wv = findWebView(lib, windowPtr as any);
if (wv) {
lib.symbols.gtk_widget_set_sensitive(wv, sensitive);
}
}
/**
* Delegate resize to the window manager.
* Clears min-size constraints first so resize-in (shrink) works.
* Disables WebView input first to prevent grab interference.
*/
export function beginResizeDrag(
windowPtr: number | bigint,
@ -224,8 +265,12 @@ export function beginResizeDrag(
const lib = getGtk();
if (!lib) return false;
try {
// Clear min-size RIGHT BEFORE resize so shrinking is allowed
// 1. Force small min-size
forceSmallMinSize(lib, windowPtr as any);
// 2. Disable WebView input to prevent grab interference
const wv = findWebView(lib, windowPtr as any);
if (wv) lib.symbols.gtk_widget_set_sensitive(wv, false);
// 3. Start WM resize
lib.symbols.gtk_window_begin_resize_drag(
windowPtr as any,
edge,
@ -234,6 +279,12 @@ export function beginResizeDrag(
Math.round(rootY),
0, // GDK_CURRENT_TIME
);
// 4. Re-enable WebView after resize drag likely ends
// The WM holds the grab until mouse-up. 5s covers long drags.
// Shorter would risk re-enabling mid-drag.
setTimeout(() => {
if (wv) lib.symbols.gtk_widget_set_sensitive(wv, true);
}, 5000);
return true;
} catch (err) {
console.error("[gtk-window] begin_resize_drag failed:", err);