mirror of
https://github.com/wez/wezterm.git
synced 2024-11-22 13:16:39 +03:00
x11: fix window contents jumping on async resize
We can't hope to keep the renderer's idea of the window size consistent with the actual window size when the user can asynchronously change its size at any moment. Therefore, we create a child window which we render to and which we synchronously resize in response to async resizes. Since by default the contents of X11 windows are undefined on resize, we take the additional steps of setting our windows' X11 background color and bit gravity as appropriate.
This commit is contained in:
parent
d70624b169
commit
809bcc55a5
@ -83,6 +83,7 @@ impl Default for DragAndDrop {
|
||||
|
||||
pub(crate) struct XWindowInner {
|
||||
pub window_id: xcb::x::Window,
|
||||
pub child_id: xcb::x::Window,
|
||||
conn: Weak<XConnection>,
|
||||
pub events: WindowEventSender,
|
||||
width: u16,
|
||||
@ -122,6 +123,9 @@ impl Drop for XWindowInner {
|
||||
.flush()
|
||||
.context("flush pending requests prior to issuing DestroyWindow")
|
||||
.ok();
|
||||
conn.send_request_no_reply_log(&xcb::x::DestroyWindow {
|
||||
window: self.child_id,
|
||||
});
|
||||
conn.send_request_no_reply_log(&xcb::x::DestroyWindow {
|
||||
window: self.window_id,
|
||||
});
|
||||
@ -145,7 +149,7 @@ unsafe impl HasRawDisplayHandle for XWindowInner {
|
||||
unsafe impl HasRawWindowHandle for XWindowInner {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let mut handle = XcbWindowHandle::empty();
|
||||
handle.window = self.window_id.resource_id();
|
||||
handle.window = self.child_id.resource_id();
|
||||
handle.visual_id = self.conn.upgrade().unwrap().visual.visual_id();
|
||||
RawWindowHandle::Xcb(handle)
|
||||
}
|
||||
@ -158,11 +162,11 @@ impl XWindowInner {
|
||||
let gl_state = match conn.gl_connection.borrow().as_ref() {
|
||||
None => crate::egl::GlState::create(
|
||||
Some(conn.conn.get_raw_dpy() as *const _),
|
||||
self.window_id.resource_id() as *mut _,
|
||||
self.child_id.resource_id() as *mut _,
|
||||
),
|
||||
Some(glconn) => crate::egl::GlState::create_with_existing_connection(
|
||||
glconn,
|
||||
self.window_id.resource_id() as *mut _,
|
||||
self.child_id.resource_id() as *mut _,
|
||||
),
|
||||
};
|
||||
|
||||
@ -261,6 +265,18 @@ impl XWindowInner {
|
||||
self.pending.push(event);
|
||||
}
|
||||
|
||||
fn resize_child(&self, width: u32, height: u32) {
|
||||
self.conn()
|
||||
.send_request_no_reply_log(&xcb::x::ConfigureWindow {
|
||||
window: self.child_id,
|
||||
value_list: &[
|
||||
xcb::x::ConfigWindow::Width(width as u32),
|
||||
xcb::x::ConfigWindow::Height(height as u32),
|
||||
],
|
||||
});
|
||||
// send_request_no_reply_log() is synchronous, so no further synchronization required
|
||||
}
|
||||
|
||||
pub fn dispatch_pending_events(&mut self) -> anyhow::Result<()> {
|
||||
if self.pending.is_empty() {
|
||||
return Ok(());
|
||||
@ -355,6 +371,8 @@ impl XWindowInner {
|
||||
|| self.height != geom.height()
|
||||
|| self.last_wm_state != window_state
|
||||
{
|
||||
self.resize_child(geom.width() as u32, geom.height() as u32);
|
||||
|
||||
self.width = geom.width();
|
||||
self.height = geom.height();
|
||||
self.last_wm_state = window_state;
|
||||
@ -460,6 +478,7 @@ impl XWindowInner {
|
||||
|
||||
fn configure_notify(&mut self, source: &str, width: u16, height: u16) -> anyhow::Result<()> {
|
||||
let conn = self.conn();
|
||||
|
||||
self.update_ime_position();
|
||||
|
||||
let mut dpi = conn.default_dpi();
|
||||
@ -510,6 +529,8 @@ impl XWindowInner {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.resize_child(width as u32, height as u32);
|
||||
|
||||
log::trace!(
|
||||
"{source}: width {} -> {}, height {} -> {}, dpi {} -> {}",
|
||||
self.width,
|
||||
@ -1355,6 +1376,7 @@ impl XWindow {
|
||||
let mut events = WindowEventSender::new(event_handler);
|
||||
|
||||
let window_id;
|
||||
let child_id;
|
||||
let window = {
|
||||
let setup = conn.conn().get_setup();
|
||||
let screen = setup
|
||||
@ -1363,6 +1385,7 @@ impl XWindow {
|
||||
.ok_or_else(|| anyhow!("no screen?"))?;
|
||||
|
||||
window_id = conn.conn().generate_id();
|
||||
child_id = conn.conn().generate_id();
|
||||
|
||||
let color_map_id = conn.conn().generate_id();
|
||||
conn.send_request_no_reply(&xcb::x::CreateColormap {
|
||||
@ -1388,7 +1411,8 @@ impl XWindow {
|
||||
// We have to specify both a border pixel color and a colormap
|
||||
// when specifying a depth that doesn't match the root window in
|
||||
// order to avoid a BadMatch
|
||||
xcb::x::Cw::BorderPixel(0),
|
||||
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
|
||||
@ -1407,6 +1431,32 @@ impl XWindow {
|
||||
})
|
||||
.context("xcb::create_window_checked")?;
|
||||
|
||||
conn.send_request_no_reply(&xcb::x::CreateWindow {
|
||||
depth: conn.depth,
|
||||
wid: child_id,
|
||||
parent: window_id,
|
||||
x: x.unwrap_or(0).try_into()?,
|
||||
y: y.unwrap_or(0).try_into()?,
|
||||
width: width.try_into()?,
|
||||
height: height.try_into()?,
|
||||
border_width: 0,
|
||||
class: xcb::x::WindowClass::InputOutput,
|
||||
visual: conn.visual.visual_id(),
|
||||
value_list: &[
|
||||
// We have to specify both a border pixel color and a colormap
|
||||
// when specifying a depth that doesn't match the root window in
|
||||
// order to avoid a BadMatch
|
||||
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::Colormap(color_map_id),
|
||||
],
|
||||
})
|
||||
.context("xcb::create_window_checked")?;
|
||||
|
||||
conn.send_request_no_reply(&xcb::x::MapWindow { window: child_id })
|
||||
.context("xcb::map_window")?;
|
||||
|
||||
events.assign_window(Window::X11(XWindow::from_id(window_id)));
|
||||
|
||||
let appearance = conn.get_appearance();
|
||||
@ -1415,6 +1465,7 @@ impl XWindow {
|
||||
title: String::new(),
|
||||
appearance,
|
||||
window_id,
|
||||
child_id,
|
||||
conn: Rc::downgrade(&conn),
|
||||
events,
|
||||
width: width.try_into()?,
|
||||
@ -1539,6 +1590,10 @@ impl XWindowInner {
|
||||
window: self.window_id,
|
||||
});
|
||||
|
||||
conn.send_request_no_reply_log(&xcb::x::DestroyWindow {
|
||||
window: self.child_id,
|
||||
});
|
||||
|
||||
// Arrange to destroy the window after a couple of seconds; that
|
||||
// should give whatever stuff is still referencing the window
|
||||
// to finish and avoid triggering a protocol error.
|
||||
@ -1989,6 +2044,7 @@ impl WindowOps for XWindow {
|
||||
xcb::x::ConfigWindow::Height(height as u32),
|
||||
],
|
||||
});
|
||||
inner.resize_child(width as u32, height as u32);
|
||||
inner.outstanding_configure_requests += 1;
|
||||
Ok(())
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user