Repository: wezterm/wezterm (language: rust)

Issue:
Unfocused Wezterm window content doesn't get drawn until "damage" occurs
### What Operating System(s) are you seeing this problem on?

Linux X11

### Which Wayland compositor or X11 Window manager(s) are you using?

Xfce4 4.18 / Xorg 21.1.13

### WezTerm version

20240509-223025-91a16e52

### Did you try the latest nightly build to see if the issue is better (or worse!) than your current version?

Yes, and I updated the version box above to show the version of the nightly that I tried

### Describe the bug

When switching to a virtual desktop unfocused Wezterm windows don't get drawn until some sort of "damage" (mouse over, focusing, etc.) occurs.

![wezterm-unfocused](https://github.com/wez/wezterm/assets/4973300/4f098bb2-409e-43fb-bb33-32d7f33ae429)

This happens with or without an active compositor.
Default or front_end = "OpenGL" makes no difference either


### To Reproduce

Switch away and back to a virtual desktop that has unfocused Wezterm windows on it.

### Configuration

no config

Tested with:  WEZTERM_CONFIG_FILE=/dev/null wezterm

### Expected Behavior

Windows should get drawn as soon as they become visible

### Logs

_No response_

### Anything else?

Commit 0184e88 (29. April 2024) works fine. This issue started happening after that release.


Discussion / hints:
I'm guessing this is another regression from 809bcc55.  I can reproduce with xfwm4 4.18.0, so I will investigate further.

Fix patch:
diff --git a/window/src/os/x11/connection.rs b/window/src/os/x11/connection.rs
index 0a11d75d583..3f078b82f08 100644
--- a/window/src/os/x11/connection.rs
+++ b/window/src/os/x11/connection.rs
@@ -70,6 +70,7 @@ pub struct XConnection {
     pub atom_net_active_window: Atom,
     pub(crate) xrm: RefCell<HashMap<String, String>>,
     pub(crate) windows: RefCell<HashMap<xcb::x::Window, Arc<Mutex<XWindowInner>>>>,
+    pub(crate) child_to_parent_id: RefCell<HashMap<xcb::x::Window, xcb::x::Window>>,
     should_terminate: RefCell<bool>,
     pub(crate) visual: xcb::x::Visualtype,
     pub(crate) depth: u8,
@@ -578,6 +579,10 @@ impl XConnection {
         self.windows.borrow().get(&window_id).map(Arc::clone)
     }
 
+    fn parent_id_by_child_id(&self, child_id: xcb::x::Window) -> Option<xcb::x::Window> {
+        self.child_to_parent_id.borrow().get(&child_id).copied()
+    }
+
     fn dispatch_pending_events(&self) -> anyhow::Result<()> {
         for window in self.windows.borrow().values() {
             let mut inner = window.lock().unwrap();
@@ -595,6 +600,11 @@ impl XConnection {
         if let Some(window) = self.window_by_id(window_id) {
             let mut inner = window.lock().unwrap();
             inner.dispatch_event(event)?;
+        } else if let Some(parent_id) = self.parent_id_by_child_id(window_id) {
+            if let Some(window) = self.window_by_id(parent_id) {
+                let mut inner = window.lock().unwrap();
+                inner.dispatch_event(event)?;
+            }
         }
         Ok(())
     }
@@ -804,6 +814,7 @@ impl XConnection {
             atom_xsel_data,
             atom_targets,
             windows: RefCell::new(HashMap::new()),
+            child_to_parent_id: RefCell::new(HashMap::new()),
             should_terminate: RefCell::new(false),
             depth,
             visual,
diff --git a/window/src/os/x11/window.rs b/window/src/os/x11/window.rs
index 8b434c32a89..58ec4f8936f 100644
--- a/window/src/os/x11/window.rs
+++ b/window/src/os/x11/window.rs
@@ -792,6 +792,7 @@ impl XWindowInner {
             Event::X(xcb::x::Event::DestroyNotify(_)) => {
                 self.events.dispatch(WindowEvent::Destroyed);
                 conn.windows.borrow_mut().remove(&self.window_id);
+                conn.child_to_parent_id.borrow_mut().remove(&self.child_id);
             }
             Event::X(xcb::x::Event::SelectionClear(e)) => {
                 self.selection_clear(e)?;
@@ -1414,8 +1415,7 @@ impl XWindow {
                     xcb::x::Cw::BackPixel(0), // transparent background
                     xcb::x::Cw::BorderPixel(screen.black_pixel()),
                     xcb::x::Cw::EventMask(
-                        xcb::x::EventMask::EXPOSURE
-                            | xcb::x::EventMask::FOCUS_CHANGE
+                        xcb::x::EventMask::FOCUS_CHANGE
                             | xcb::x::EventMask::KEY_PRESS
                             | xcb::x::EventMask::BUTTON_PRESS
                             | xcb::x::EventMask::BUTTON_RELEASE
@@ -1449,6 +1449,7 @@ impl XWindow {
                     xcb::x::Cw::BackPixel(0), // transparent background
                     xcb::x::Cw::BorderPixel(screen.black_pixel()),
                     xcb::x::Cw::BitGravity(xcb::x::Gravity::NorthWest),
+                    xcb::x::Cw::EventMask(xcb::x::EventMask::EXPOSURE),
                     xcb::x::Cw::Colormap(color_map_id),
                 ],
             })
@@ -1538,6 +1539,9 @@ impl XWindow {
         let window_handle = Window::X11(XWindow::from_id(window_id));
 
         conn.windows.borrow_mut().insert(window_id, window);
+        conn.child_to_parent_id
+            .borrow_mut()
+            .insert(child_id, window_id);
 
         window_handle.set_title(name);
         // Before we map the window, flush to ensure that all of the other properties
@@ -1581,6 +1585,10 @@ impl XWindowInner {
         // Drop impl, and that cannot succeed after we've
         // destroyed the window at the X11 level.
         self.conn().windows.borrow_mut().remove(&self.window_id);
+        self.conn()
+            .child_to_parent_id
+            .borrow_mut()
+            .remove(&self.child_id);
 
         // Unmap the window first: calling DestroyWindow here may race
         // with some requests made either by EGL or the IME, but I haven't


Test patch: