This commit is contained in:
Haapy
2026-05-14 23:50:35 +00:00
parent f9c39953f0
commit 8bc19de599

View File

@@ -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(())
}