mirror of
https://github.com/wez/wezterm.git
synced 2024-12-28 07:55:03 +03:00
window: add WindowState concept
WindowState is a bitfield that can represent maximized, full screen and hidden window states. WindowState is passed along with resize events, improving on the prior basic is_full_screen boolean by representing the other states. Notably, WindowState::MAXIMIZED is used to represent a state where the window's size is constrained by some window environment function; it could be due to the window being maximized in either or both the vertical or horizontal directions, or by the window being in a tiled state on any edge. When the window is MAXIMIZED, wezterm will behave as though `adjust_window_size_when_changing_font_size = false` because it knows that it cannot adjust the window size in that state. This potentially helps with #695, depending on whether the window manager propagates this state information to wezterm. Gnome/mutter does a good job at this with both X11 and Wayland, but I couldn't get sway to report these states and I don't know of any other tiling wm that I can easily install and use on fedora, so there's a question mark around that.
This commit is contained in:
parent
0158e8e49a
commit
79165617b1
@ -9,7 +9,7 @@ use mlua::{UserData, UserDataMethods};
|
||||
use mux::window::WindowId as MuxWindowId;
|
||||
use serde::*;
|
||||
use wezterm_toast_notification::ToastNotification;
|
||||
use window::{Connection, ConnectionOps, WindowOps};
|
||||
use window::{Connection, ConnectionOps, WindowOps, WindowState};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GuiWin {
|
||||
@ -53,7 +53,7 @@ impl UserData for GuiWin {
|
||||
methods.add_async_method("get_dimensions", |_, this, _: ()| async move {
|
||||
let (tx, rx) = smol::channel::bounded(1);
|
||||
this.window.notify(TermWindowNotif::GetDimensions(tx));
|
||||
let (dims, is_full_screen) = rx
|
||||
let (dims, window_state) = rx
|
||||
.recv()
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("{:#}", e))
|
||||
@ -72,7 +72,8 @@ impl UserData for GuiWin {
|
||||
pixel_width: dims.pixel_width,
|
||||
pixel_height: dims.pixel_height,
|
||||
dpi: dims.dpi,
|
||||
is_full_screen,
|
||||
is_full_screen: window_state.contains(WindowState::FULL_SCREEN),
|
||||
// FIXME: expose other states here
|
||||
};
|
||||
Ok(dims)
|
||||
});
|
||||
|
@ -81,7 +81,7 @@ pub enum TermWindowNotif {
|
||||
assignment: KeyAssignment,
|
||||
},
|
||||
SetRightStatus(String),
|
||||
GetDimensions(Sender<(Dimensions, bool)>),
|
||||
GetDimensions(Sender<(Dimensions, WindowState)>),
|
||||
GetSelectionForPane {
|
||||
pane_id: PaneId,
|
||||
tx: Sender<String>,
|
||||
@ -176,7 +176,7 @@ pub struct TermWindow {
|
||||
fonts: Rc<FontConfiguration>,
|
||||
/// Window dimensions and dpi
|
||||
pub dimensions: Dimensions,
|
||||
pub is_full_screen: bool,
|
||||
pub window_state: WindowState,
|
||||
/// Terminal dimensions
|
||||
terminal_size: PtySize,
|
||||
pub mux_window_id: MuxWindowId,
|
||||
@ -457,7 +457,7 @@ impl TermWindow {
|
||||
fonts: Rc::clone(&fontconfig),
|
||||
render_metrics,
|
||||
dimensions,
|
||||
is_full_screen: false,
|
||||
window_state: WindowState::default(),
|
||||
terminal_size,
|
||||
render_state,
|
||||
input_map: InputMap::new(&config),
|
||||
@ -568,9 +568,9 @@ impl TermWindow {
|
||||
}
|
||||
WindowEvent::Resized {
|
||||
dimensions,
|
||||
is_full_screen,
|
||||
window_state,
|
||||
} => {
|
||||
self.resize(dimensions, is_full_screen);
|
||||
self.resize(dimensions, window_state);
|
||||
Ok(true)
|
||||
}
|
||||
WindowEvent::KeyEvent(event) => {
|
||||
@ -642,7 +642,7 @@ impl TermWindow {
|
||||
}
|
||||
}
|
||||
TermWindowNotif::GetDimensions(tx) => {
|
||||
tx.try_send((self.dimensions, self.is_full_screen))
|
||||
tx.try_send((self.dimensions, self.window_state))
|
||||
.map_err(chan_err)
|
||||
.context("send GetDimensions response")?;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::utilsprites::RenderMetrics;
|
||||
use ::window::{Dimensions, WindowOps};
|
||||
use ::window::{Dimensions, WindowOps, WindowState};
|
||||
use config::ConfigHandle;
|
||||
use mux::Mux;
|
||||
use portable_pty::PtySize;
|
||||
@ -13,12 +13,12 @@ pub struct RowsAndCols {
|
||||
}
|
||||
|
||||
impl super::TermWindow {
|
||||
pub fn resize(&mut self, dimensions: Dimensions, is_full_screen: bool) {
|
||||
pub fn resize(&mut self, dimensions: Dimensions, window_state: WindowState) {
|
||||
log::trace!(
|
||||
"resize event, current cells: {:?}, new dims: {:?} is_full_screen:{}",
|
||||
"resize event, current cells: {:?}, new dims: {:?} window_state:{:?}",
|
||||
self.current_cell_dimensions(),
|
||||
dimensions,
|
||||
is_full_screen,
|
||||
window_state,
|
||||
);
|
||||
if dimensions.pixel_width == 0 || dimensions.pixel_height == 0 {
|
||||
// on windows, this can happen when minimizing the window.
|
||||
@ -26,12 +26,12 @@ impl super::TermWindow {
|
||||
log::trace!("new dimensions are zero: NOP!");
|
||||
return;
|
||||
}
|
||||
if self.dimensions == dimensions && self.is_full_screen == is_full_screen {
|
||||
if self.dimensions == dimensions && self.window_state == window_state {
|
||||
// It didn't really change
|
||||
log::trace!("dimensions didn't change NOP!");
|
||||
return;
|
||||
}
|
||||
self.is_full_screen = is_full_screen;
|
||||
self.window_state = window_state;
|
||||
self.scaling_changed(dimensions, self.fonts.get_font_scale());
|
||||
self.emit_window_event("window-resized");
|
||||
}
|
||||
@ -198,7 +198,8 @@ impl super::TermWindow {
|
||||
/// the `adjust_window_size_when_changing_font_size` configuration and
|
||||
/// revises the scaling/resize change accordingly
|
||||
pub fn adjust_font_scale(&mut self, font_scale: f64) {
|
||||
if !self.is_full_screen && self.config.adjust_window_size_when_changing_font_size {
|
||||
if self.window_state.can_resize() && self.config.adjust_window_size_when_changing_font_size
|
||||
{
|
||||
self.scaling_changed(self.dimensions, font_scale);
|
||||
} else {
|
||||
let dimensions = self.dimensions;
|
||||
|
@ -34,9 +34,9 @@ impl MyWindow {
|
||||
}
|
||||
WindowEvent::Resized {
|
||||
dimensions,
|
||||
is_full_screen,
|
||||
window_state,
|
||||
} => {
|
||||
eprintln!("resize {:?} is_full_screen={}", dimensions, is_full_screen);
|
||||
eprintln!("resize {:?} state={:?}", dimensions, window_state);
|
||||
self.dims = dimensions;
|
||||
}
|
||||
WindowEvent::MouseEvent(event) => {
|
||||
|
@ -225,7 +225,7 @@ impl MyWindow {
|
||||
}
|
||||
WindowEvent::Resized {
|
||||
dimensions,
|
||||
is_full_screen: _,
|
||||
window_state: _,
|
||||
} => {
|
||||
self.resize(dimensions);
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use bitflags::bitflags;
|
||||
use promise::Future;
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
@ -89,6 +90,30 @@ impl std::string::ToString for Appearance {
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct WindowState: u8 {
|
||||
/// Occupies the whole screen; cannot be resized while in this state.
|
||||
const FULL_SCREEN = 1<<1;
|
||||
/// Maximized along either or both of horizontal or vertical dimensions;
|
||||
/// cannot be resized while in this state.
|
||||
const MAXIMIZED = 1<<2;
|
||||
/// Minimized or in some kind of off-screen state. Cannot be repainted
|
||||
/// while in this state.
|
||||
const HIDDEN = 1<<3;
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
pub fn can_resize(self) -> bool {
|
||||
!self.intersects(Self::FULL_SCREEN | Self::MAXIMIZED)
|
||||
}
|
||||
|
||||
pub fn can_paint(self) -> bool {
|
||||
!self.contains(Self::HIDDEN)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WindowEvent {
|
||||
/// Called when the window close button is clicked.
|
||||
@ -103,7 +128,7 @@ pub enum WindowEvent {
|
||||
/// Called when the window has been resized
|
||||
Resized {
|
||||
dimensions: Dimensions,
|
||||
is_full_screen: bool,
|
||||
window_state: WindowState,
|
||||
},
|
||||
|
||||
/// Called when the window has been invalidated and needs to
|
||||
|
@ -507,7 +507,7 @@ impl Window {
|
||||
dpi: (crate::DEFAULT_DPI * (backing_frame.size.width / frame.size.width))
|
||||
as usize,
|
||||
},
|
||||
is_full_screen: false,
|
||||
window_state: WindowState::default(),
|
||||
});
|
||||
|
||||
Ok(window_handle)
|
||||
@ -1929,7 +1929,11 @@ impl WindowView {
|
||||
dpi: (crate::DEFAULT_DPI * (backing_frame.size.width / frame.size.width))
|
||||
as usize,
|
||||
},
|
||||
is_full_screen,
|
||||
window_state: if is_full_screen {
|
||||
WindowState::FULL_SCREEN
|
||||
} else {
|
||||
WindowState::default()
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::os::wayland::connection::WaylandConnection;
|
||||
use crate::os::x11::keyboard::Keyboard;
|
||||
use crate::{
|
||||
Clipboard, Connection, Dimensions, MouseCursor, Point, ScreenPoint, Window, WindowEvent,
|
||||
WindowEventSender, WindowOps,
|
||||
WindowEventSender, WindowOps, WindowState,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use async_io::Timer;
|
||||
@ -112,7 +112,7 @@ pub struct WaylandWindowInner {
|
||||
copy_and_paste: Arc<Mutex<CopyAndPaste>>,
|
||||
window: Option<toolkit::window::Window<ConceptFrame>>,
|
||||
dimensions: Dimensions,
|
||||
full_screen: bool,
|
||||
window_state: WindowState,
|
||||
last_mouse_coords: Point,
|
||||
mouse_buttons: MouseButtons,
|
||||
modifiers: Modifiers,
|
||||
@ -134,7 +134,7 @@ struct PendingEvent {
|
||||
refresh_decorations: bool,
|
||||
configure: Option<(u32, u32)>,
|
||||
dpi: Option<i32>,
|
||||
full_screen: Option<bool>,
|
||||
window_state: Option<WindowState>,
|
||||
}
|
||||
|
||||
impl PendingEvent {
|
||||
@ -165,17 +165,32 @@ impl PendingEvent {
|
||||
} else {
|
||||
changed = true;
|
||||
}
|
||||
let full_screen = states.contains(&State::Fullscreen);
|
||||
let mut state = WindowState::default();
|
||||
for s in &states {
|
||||
match s {
|
||||
State::Fullscreen => {
|
||||
state |= WindowState::FULL_SCREEN;
|
||||
}
|
||||
State::Maximized
|
||||
| State::TiledLeft
|
||||
| State::TiledRight
|
||||
| State::TiledTop
|
||||
| State::TiledBottom => {
|
||||
state |= WindowState::MAXIMIZED;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
log::debug!(
|
||||
"Config: self.full_screen={:?}, states:{:?} {:?}",
|
||||
self.full_screen,
|
||||
full_screen,
|
||||
"Config: self.window_state={:?}, states:{:?} {:?}",
|
||||
self.window_state,
|
||||
state,
|
||||
states
|
||||
);
|
||||
match (self.full_screen, full_screen) {
|
||||
(None, false) => {}
|
||||
match (self.window_state, state) {
|
||||
(None, s) if s == WindowState::default() => {}
|
||||
_ => {
|
||||
self.full_screen.replace(full_screen);
|
||||
self.window_state.replace(state);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
@ -292,7 +307,7 @@ impl WaylandWindow {
|
||||
surface: surface.detach(),
|
||||
window: Some(window),
|
||||
dimensions,
|
||||
full_screen: false,
|
||||
window_state: WindowState::default(),
|
||||
last_mouse_coords: Point::new(0, 0),
|
||||
mouse_buttons: MouseButtons::NONE,
|
||||
modifiers: Modifiers::NONE,
|
||||
@ -507,13 +522,13 @@ impl WaylandWindowInner {
|
||||
self.events.dispatch(WindowEvent::CloseRequested);
|
||||
}
|
||||
|
||||
if let Some(full_screen) = pending.full_screen.take() {
|
||||
if let Some(window_state) = pending.window_state.take() {
|
||||
log::debug!(
|
||||
"dispatch_pending_event self.full_screen={} pending:{}",
|
||||
self.full_screen,
|
||||
full_screen
|
||||
"dispatch_pending_event self.window_state={:?} pending:{:?}",
|
||||
self.window_state,
|
||||
window_state
|
||||
);
|
||||
self.full_screen = full_screen;
|
||||
self.window_state = window_state;
|
||||
}
|
||||
|
||||
if pending.configure.is_none() && pending.dpi.is_some() {
|
||||
@ -554,7 +569,7 @@ impl WaylandWindowInner {
|
||||
|
||||
self.events.dispatch(WindowEvent::Resized {
|
||||
dimensions: self.dimensions,
|
||||
is_full_screen: self.full_screen,
|
||||
window_state: self.window_state,
|
||||
});
|
||||
if let Some(wegl_surface) = self.wegl_surface.as_mut() {
|
||||
wegl_surface.resize(pixel_width, pixel_height, 0, 0);
|
||||
@ -874,7 +889,7 @@ impl WaylandWindowInner {
|
||||
|
||||
fn toggle_fullscreen(&mut self) {
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
if self.full_screen {
|
||||
if self.window_state.contains(WindowState::FULL_SCREEN) {
|
||||
window.unset_fullscreen();
|
||||
} else {
|
||||
window.set_fullscreen(None);
|
||||
|
@ -4,7 +4,7 @@ use crate::Appearance;
|
||||
use crate::{
|
||||
Clipboard, Dimensions, KeyCode, KeyEvent, Modifiers, MouseButtons, MouseCursor, MouseEvent,
|
||||
MouseEventKind, MousePress, Point, Rect, ScreenPoint, WindowDecorations, WindowEvent,
|
||||
WindowEventSender, WindowOps,
|
||||
WindowEventSender, WindowOps, WindowState,
|
||||
};
|
||||
use anyhow::{bail, Context};
|
||||
use async_trait::async_trait;
|
||||
@ -221,7 +221,11 @@ impl WindowInner {
|
||||
|
||||
self.events.dispatch(WindowEvent::Resized {
|
||||
dimensions: current_dims,
|
||||
is_full_screen: self.saved_placement.is_some(),
|
||||
window_state: if self.saved_placement.is_some() {
|
||||
WindowState::FULL_SCREEN
|
||||
} else {
|
||||
WindowState::default()
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,11 @@ pub struct XConnection {
|
||||
pub atom_xsettings_selection: xcb::Atom,
|
||||
pub atom_xsettings_settings: xcb::Atom,
|
||||
pub atom_manager: xcb::Atom,
|
||||
pub atom_state_maximized_vert: xcb::Atom,
|
||||
pub atom_state_maximized_horz: xcb::Atom,
|
||||
pub atom_state_hidden: xcb::Atom,
|
||||
pub atom_state_fullscreen: xcb::Atom,
|
||||
pub atom_net_wm_state: xcb::Atom,
|
||||
keysyms: *mut xcb_key_symbols_t,
|
||||
pub(crate) xrm: RefCell<HashMap<String, String>>,
|
||||
pub(crate) windows: RefCell<HashMap<xcb::xproto::Window, Arc<Mutex<XWindowInner>>>>,
|
||||
@ -357,6 +362,23 @@ impl XConnection {
|
||||
let atom_manager = xcb::intern_atom(&conn, false, "MANAGER")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
let atom_state_maximized_vert =
|
||||
xcb::intern_atom(&conn, false, "_NET_WM_STATE_MAXIMIZED_VERT")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
let atom_state_maximized_horz =
|
||||
xcb::intern_atom(&conn, false, "_NET_WM_STATE_MAXIMIZED_HORZ")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
let atom_state_hidden = xcb::intern_atom(&conn, false, "_NET_WM_STATE_HIDDEN")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
let atom_state_fullscreen = xcb::intern_atom(&conn, false, "_NET_WM_STATE_FULLSCREEN")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
let atom_net_wm_state = xcb::intern_atom(&conn, false, "_NET_WM_STATE")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
|
||||
let keysyms = unsafe { xcb_key_symbols_alloc((*conn).get_raw_conn()) };
|
||||
|
||||
@ -434,6 +456,11 @@ impl XConnection {
|
||||
atom_xsettings_settings,
|
||||
atom_manager,
|
||||
atom_delete,
|
||||
atom_state_maximized_vert,
|
||||
atom_state_maximized_horz,
|
||||
atom_state_hidden,
|
||||
atom_state_fullscreen,
|
||||
atom_net_wm_state,
|
||||
keysyms,
|
||||
keyboard,
|
||||
kbd_ev,
|
||||
|
@ -6,6 +6,7 @@ use crate::os::{Connection, Window};
|
||||
use crate::{
|
||||
Appearance, Clipboard, Dimensions, MouseButtons, MouseCursor, MouseEvent, MouseEventKind,
|
||||
MousePress, Point, ScreenPoint, WindowDecorations, WindowEvent, WindowEventSender, WindowOps,
|
||||
WindowState,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use async_trait::async_trait;
|
||||
@ -152,7 +153,7 @@ impl XWindowInner {
|
||||
pixel_height: self.height as usize,
|
||||
dpi: self.dpi as usize,
|
||||
},
|
||||
is_full_screen: self.is_fullscreen().unwrap_or(false),
|
||||
window_state: self.get_window_state().unwrap_or(WindowState::default()),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -189,7 +190,7 @@ impl XWindowInner {
|
||||
|
||||
self.events.dispatch(WindowEvent::Resized {
|
||||
dimensions,
|
||||
is_full_screen: self.is_fullscreen().unwrap_or(false),
|
||||
window_state: self.get_window_state().unwrap_or(WindowState::default()),
|
||||
});
|
||||
}
|
||||
xcb::KEY_PRESS | xcb::KEY_RELEASE => {
|
||||
@ -515,22 +516,14 @@ impl XWindowInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_fullscreen(&self) -> anyhow::Result<bool> {
|
||||
fn get_window_state(&self) -> anyhow::Result<WindowState> {
|
||||
let conn = self.conn();
|
||||
|
||||
let net_wm_state = xcb::intern_atom(conn.conn(), false, "_NET_WM_STATE")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
let net_wm_state_fullscreen =
|
||||
xcb::intern_atom(conn.conn(), false, "_NET_WM_STATE_FULLSCREEN")
|
||||
.get_reply()?
|
||||
.atom();
|
||||
|
||||
let reply = xcb::xproto::get_property(
|
||||
&conn,
|
||||
false,
|
||||
self.window_id,
|
||||
net_wm_state,
|
||||
conn.atom_net_wm_state,
|
||||
xcb::xproto::ATOM_ATOM,
|
||||
0,
|
||||
1024,
|
||||
@ -538,11 +531,19 @@ impl XWindowInner {
|
||||
.get_reply()?;
|
||||
|
||||
let state = reply.value::<u32>();
|
||||
let mut window_state = WindowState::default();
|
||||
|
||||
Ok(state
|
||||
.iter()
|
||||
.position(|&x| x == net_wm_state_fullscreen)
|
||||
.is_some())
|
||||
for &s in state {
|
||||
if s == conn.atom_state_fullscreen {
|
||||
window_state |= WindowState::FULL_SCREEN;
|
||||
} else if s == conn.atom_state_maximized_vert || s == conn.atom_state_maximized_horz {
|
||||
window_state |= WindowState::MAXIMIZED;
|
||||
} else if s == conn.atom_state_hidden {
|
||||
window_state |= WindowState::HIDDEN;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(window_state)
|
||||
}
|
||||
|
||||
fn set_fullscreen_hint(&mut self, enable: bool) -> anyhow::Result<()> {
|
||||
@ -804,8 +805,8 @@ impl XWindowInner {
|
||||
}
|
||||
|
||||
fn toggle_fullscreen(&mut self) {
|
||||
let fullscreen = match self.is_fullscreen() {
|
||||
Ok(f) => f,
|
||||
let fullscreen = match self.get_window_state() {
|
||||
Ok(f) => f.contains(WindowState::FULL_SCREEN),
|
||||
Err(err) => {
|
||||
log::error!("Failed to determine fullscreen state: {}", err);
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user