1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-26 14:54:16 +03:00

Implement SetInnerSizeCompleted events

These are used to signal, after a set_inner_size() call, at what point
we can expect any and all of its related Resized events to have already
been dispatched.  SetInnerSizeCompleted events are currently used to fix
a race condition in rescaling the terminal in which the number of cells
of the terminal (e.g., 80x24) can change when quickly rescaling the
terminal.
This commit is contained in:
Jeffrey Knockel 2024-01-31 21:07:50 -05:00 committed by Wez Furlong
parent 2c5fa35986
commit a0974a2537
9 changed files with 100 additions and 27 deletions

View File

@ -56,7 +56,8 @@ impl UserData for GuiWin {
methods.add_method(
"set_inner_size",
|_, this, (width, height): (usize, usize)| {
this.window.set_inner_size(width, height);
this.window
.notify(TermWindowNotif::SetInnerSize { width, height });
Ok(())
},
);

View File

@ -54,7 +54,7 @@ use mux_lua::MuxPane;
use smol::channel::Sender;
use smol::Timer;
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::collections::{HashMap, LinkedList};
use std::ops::Add;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
@ -145,6 +145,10 @@ pub enum TermWindowNotif {
EmitStatusUpdate,
Apply(Box<dyn FnOnce(&mut TermWindow) + Send + Sync>),
SwitchToMuxWindow(MuxWindowId),
SetInnerSize {
width: usize,
height: usize,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -365,6 +369,8 @@ pub struct TermWindow {
/// Window dimensions and dpi
pub dimensions: Dimensions,
pub window_state: WindowState,
pub resizes_pending: usize,
pending_scale_changes: LinkedList<resize::ScaleChange>,
/// Terminal dimensions
terminal_size: TerminalSize,
pub mux_window_id: MuxWindowId,
@ -689,6 +695,8 @@ impl TermWindow {
render_metrics,
dimensions,
window_state: WindowState::default(),
resizes_pending: 0,
pending_scale_changes: LinkedList::new(),
terminal_size,
render_state,
input_map: InputMap::new(&config),
@ -944,6 +952,11 @@ impl TermWindow {
self.resize(dimensions, window_state, window, live_resizing);
Ok(true)
}
WindowEvent::SetInnerSizeCompleted => {
self.resizes_pending -= 1;
self.apply_pending_scale_changes();
Ok(true)
}
WindowEvent::AdviseModifiersLedStatus(modifiers, leds) => {
self.current_modifier_and_leds = (modifiers, leds);
self.update_title();
@ -1287,11 +1300,21 @@ impl TermWindow {
self.update_title();
window.invalidate();
}
TermWindowNotif::SetInnerSize { width, height } => {
self.set_inner_size(width, height);
}
}
Ok(())
}
fn set_inner_size(&mut self, width: usize, height: usize) {
if let Some(window) = &self.window {
self.resizes_pending += 1;
window.set_inner_size(width, height);
}
}
/// Take care to remove our panes from the mux, otherwise
/// we can leave the mux with no windows but some panes
/// and it won't believe that we are empty.
@ -2579,21 +2602,9 @@ impl TermWindow {
self.activate_tab_relative(*n, false)?;
}
ActivateLastTab => self.activate_last_tab()?,
DecreaseFontSize => {
if let Some(w) = window.as_ref() {
self.decrease_font_size(w)
}
}
IncreaseFontSize => {
if let Some(w) = window.as_ref() {
self.increase_font_size(w)
}
}
ResetFontSize => {
if let Some(w) = window.as_ref() {
self.reset_font_size(w)
}
}
DecreaseFontSize => self.decrease_font_size(),
IncreaseFontSize => self.increase_font_size(),
ResetFontSize => self.reset_font_size(),
ResetFontAndWindowSize => {
if let Some(w) = window.as_ref() {
self.reset_font_and_window_size(&w)?

View File

@ -13,6 +13,12 @@ pub struct RowsAndCols {
pub cols: usize,
}
#[derive(Debug)]
pub enum ScaleChange {
Absolute(f64),
Relative(f64),
}
impl super::TermWindow {
pub fn resize(
&mut self,
@ -64,6 +70,24 @@ impl super::TermWindow {
self.emit_window_event("window-resized", None);
}
pub fn apply_pending_scale_changes(&mut self) {
while self.resizes_pending == 0 {
match self.pending_scale_changes.pop_front() {
Some(ScaleChange::Relative(change)) => {
if let Some(window) = self.window.as_ref().map(|w| w.clone()) {
self.adjust_font_scale(self.fonts.get_font_scale() * change, &window);
}
}
Some(ScaleChange::Absolute(change)) => {
if let Some(window) = self.window.as_ref().map(|w| w.clone()) {
self.adjust_font_scale(change, &window);
}
}
None => break,
}
}
}
pub fn apply_scale_change(&mut self, dimensions: &Dimensions, font_scale: f64) {
let config = &self.config;
let font_size = config.font_size * font_scale;
@ -313,7 +337,7 @@ impl super::TermWindow {
// pixel geometry which is considered to be a user-driven resize.
// Stashing the dimensions here avoids that misconception.
self.dimensions = dims;
window.set_inner_size(dims.pixel_width, dims.pixel_height);
self.set_inner_size(dims.pixel_width, dims.pixel_height);
}
}
}
@ -430,16 +454,22 @@ impl super::TermWindow {
}
}
pub fn decrease_font_size(&mut self, window: &Window) {
self.adjust_font_scale(self.fonts.get_font_scale() / 1.1, window);
pub fn decrease_font_size(&mut self) {
self.pending_scale_changes
.push_back(ScaleChange::Relative(1.0 / 1.1));
self.apply_pending_scale_changes();
}
pub fn increase_font_size(&mut self, window: &Window) {
self.adjust_font_scale(self.fonts.get_font_scale() * 1.1, window);
pub fn increase_font_size(&mut self) {
self.pending_scale_changes
.push_back(ScaleChange::Relative(1.1));
self.apply_pending_scale_changes();
}
pub fn reset_font_size(&mut self, window: &Window) {
self.adjust_font_scale(1.0, window);
pub fn reset_font_size(&mut self) {
self.pending_scale_changes
.push_back(ScaleChange::Absolute(1.0));
self.apply_pending_scale_changes();
}
pub fn set_window_size(&mut self, size: TerminalSize, window: &Window) -> anyhow::Result<()> {

View File

@ -86,7 +86,8 @@ impl MyWindow {
| WindowEvent::DraggedFile(_)
| WindowEvent::DroppedFile(_)
| WindowEvent::PerformKeyAssignment(_)
| WindowEvent::MouseLeave => {}
| WindowEvent::MouseLeave
| WindowEvent::SetInnerSizeCompleted => {}
}
}
}

View File

@ -171,6 +171,9 @@ pub enum WindowEvent {
live_resizing: bool,
},
/// Called when a program-requested set_inner_size() has finished
SetInnerSizeCompleted,
/// Called when the window has been invalidated and needs to
/// be repainted
NeedRepaint,

View File

@ -776,6 +776,13 @@ impl WindowOps for Window {
fn set_inner_size(&self, width: usize, height: usize) {
Connection::with_window_inner(self.id, move |inner| {
inner.set_inner_size(width, height);
if let Some(window_view) = WindowView::get_this(unsafe { &**inner.view }) {
window_view
.inner
.borrow_mut()
.events
.dispatch(WindowEvent::SetInnerSizeCompleted);
}
Ok(())
});
}

View File

@ -976,7 +976,7 @@ impl WaylandWindowInner {
Ok(())
}
fn set_inner_size(&mut self, width: usize, height: usize) -> Dimensions {
fn set_inner_size(&mut self, width: usize, height: usize) {
let pixel_width = width as i32;
let pixel_height = height as i32;
let surface_width = self.pixels_to_surface(pixel_width) as u32;
@ -993,7 +993,7 @@ impl WaylandWindowInner {
// apply the synthetic configure event to the inner surfaces
self.dispatch_pending_event();
self.dimensions.clone()
self.events.dispatch(WindowEvent::SetInnerSizeCompleted);
}
fn do_paint(&mut self) -> anyhow::Result<()> {

View File

@ -898,6 +898,10 @@ impl WindowOps for Window {
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER,
);
wm_paint(hwnd.0, 0, 0, 0);
if let Some(inner) = rc_from_hwnd(hwnd.0) {
let mut inner = inner.borrow_mut();
inner.events.dispatch(WindowEvent::SetInnerSizeCompleted);
}
}
} else {
log::trace!(

View File

@ -80,6 +80,8 @@ pub(crate) struct XWindowInner {
current_mouse_event: Option<MouseEvent>,
window_drag_position: Option<ScreenPoint>,
dragging: bool,
outstanding_configure_requests: usize,
pending_finished_resizes: usize,
}
/// <https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm46409506331616>
@ -267,6 +269,13 @@ impl XWindowInner {
self.events.dispatch(resize);
}
// These SetInnerSizeCompleted events need to be dispatched after the
// above Resized events because a resize cannot finish before it occurs.
while self.pending_finished_resizes > 0 {
self.events.dispatch(WindowEvent::SetInnerSizeCompleted);
self.pending_finished_resizes -= 1;
}
if need_paint {
if self.paint_throttled {
self.invalidated = true;
@ -524,6 +533,10 @@ impl XWindowInner {
}
Event::X(xcb::x::Event::ConfigureNotify(cfg)) => {
self.configure_notify("X::ConfigureNotify", cfg.width(), cfg.height())?;
if self.outstanding_configure_requests > 0 {
self.outstanding_configure_requests -= 1;
self.pending_finished_resizes += 1;
}
}
Event::X(xcb::x::Event::KeyPress(key_press)) => {
self.copy_and_paste.time = key_press.time();
@ -1204,6 +1217,8 @@ impl XWindow {
current_mouse_event: None,
window_drag_position: None,
dragging: false,
outstanding_configure_requests: 0,
pending_finished_resizes: 0,
}))
};
@ -1748,6 +1763,7 @@ impl WindowOps for XWindow {
xcb::x::ConfigWindow::Height(height as u32),
],
});
inner.outstanding_configure_requests += 1;
Ok(())
});
}