From 8bc19de599ad778a352ceb4477c4aeabde371d1d Mon Sep 17 00:00:00 2001 From: Haapy Date: Thu, 14 May 2026 23:50:35 +0000 Subject: [PATCH] aaaa --- src-tauri/src/x11mod.rs | 59 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/x11mod.rs b/src-tauri/src/x11mod.rs index 646cb99..9e6c17c 100644 --- a/src-tauri/src/x11mod.rs +++ b/src-tauri/src/x11mod.rs @@ -7,10 +7,12 @@ use x11rb::atom_manager; use x11rb::connection::Connection; use x11rb::protocol::xproto::{ AtomEnum, ChangeWindowAttributesAux, ClientMessageEvent, ConfigureWindowAux, - ConnectionExt as XprotoConnectionExt, EventMask, StackMode, + ConnectionExt as XprotoConnectionExt, EventMask, StackMode, UnmapNotifyEvent, + UNMAP_NOTIFY_EVENT, }; use x11rb::protocol::Event; use x11rb::rust_connection::RustConnection; +use x11rb::wrapper::ConnectionExt as _; atom_manager! { pub Atoms: AtomsCookie { @@ -172,9 +174,16 @@ pub async fn app_launch( // Detach: we don't wait() — Tauri exiting will SIGHUP it via the X11 parent. std::mem::forget(child); + eprintln!( + "[infinite] app_launch: command={:?} pid={} tauri_xid={:#x}", + opts.command, root_pid, parent_xid + ); + let client_xid = wait_for_window_by_pid(&c, root_pid, Duration::from_secs(20)) .ok_or_else(|| "could not find window for launched process".to_string())?; + eprintln!("[infinite] found client window {:#x} for pid {}", client_xid, root_pid); + embed_window(&c, client_xid, parent_xid)?; let title = read_window_title(&c, client_xid).unwrap_or_else(|| opts.command.clone()); @@ -279,6 +288,30 @@ pub fn app_close(app: AppHandle, state: State<'_, X11State>, xid: u32) -> Result } fn embed_window(c: &X11Conn, client_xid: u32, parent_xid: u32) -> Result<(), String> { + eprintln!( + "[infinite] embed: client={:#x} -> parent={:#x}", + client_xid, parent_xid + ); + + // XWithdrawWindow protocol: unmap then send synthetic UnmapNotify to root so + // the WM unmanages this window. Without this step Mutter et al. keep treating + // it as a top-level even after reparenting. + let _ = c.conn.unmap_window(client_xid); + let unmap_event = UnmapNotifyEvent { + response_type: UNMAP_NOTIFY_EVENT, + sequence: 0, + event: c.root, + window: client_xid, + from_configure: false, + }; + let _ = c.conn.send_event( + false, + c.root, + EventMask::SUBSTRUCTURE_NOTIFY | EventMask::SUBSTRUCTURE_REDIRECT, + unmap_event, + ); + let _ = c.conn.sync(); + // Listen for destroy / property changes on the embedded window. c.conn .change_window_attributes( @@ -292,8 +325,7 @@ fn embed_window(c: &X11Conn, client_xid: u32, parent_xid: u32) -> Result<(), Str c.conn .reparent_window(client_xid, parent_xid, 0, 0) .map_err(|e| e.to_string())?; - - c.conn.map_window(client_xid).map_err(|e| e.to_string())?; + let _ = c.conn.sync(); // Stack above the webview's GDK X11 surface (siblings under Tauri toplevel). c.conn @@ -303,6 +335,7 @@ fn embed_window(c: &X11Conn, client_xid: u32, parent_xid: u32) -> Result<(), Str ) .map_err(|e| e.to_string())?; + c.conn.map_window(client_xid).map_err(|e| e.to_string())?; c.conn.flush().map_err(|e| e.to_string())?; // Inform the client it is now embedded (XEmbed protocol). @@ -315,6 +348,26 @@ fn embed_window(c: &X11Conn, client_xid: u32, parent_xid: u32) -> Result<(), Str let _ = c.conn.send_event(false, client_xid, EventMask::NO_EVENT, event); c.conn.flush().map_err(|e| e.to_string())?; + // Verify the reparent took effect at the X11 level. If it didn't, the call + // succeeded but something (WM, compositor) reverted it. + let tree = c + .conn + .query_tree(client_xid) + .map_err(|e| e.to_string())? + .reply() + .map_err(|e| e.to_string())?; + eprintln!( + "[infinite] after reparent: parent={:#x} (wanted {:#x}), root={:#x}", + tree.parent, parent_xid, tree.root + ); + if tree.parent != parent_xid { + return Err(format!( + "reparent did not stick: window {:#x} parent is {:#x}, not {:#x}. \ + Likely a Wayland session — try logging in to 'Ubuntu on Xorg'.", + client_xid, tree.parent, parent_xid + )); + } + Ok(()) }