1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

windows: implement decoration setting, change to bitfield

Can now set `window_decorations = "TITLE|RESIZE"` or variations
on those flags.  `NONE` is a shortcut for no flags.
This commit is contained in:
Wez Furlong 2021-03-04 09:39:57 -08:00
parent 945b6b726f
commit 1178639a22
7 changed files with 150 additions and 78 deletions

View File

@ -25,7 +25,7 @@ use std::time::Duration;
use termwiz::hyperlink;
use termwiz::surface::CursorShape;
use toml;
use wezterm_input_types::{KeyCode, Modifiers};
use wezterm_input_types::{KeyCode, Modifiers, WindowDecorations};
mod color;
mod daemon;
@ -1160,18 +1160,6 @@ pub struct LoadedConfig {
lua: Option<mlua::Lua>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WindowDecorations {
Full,
None,
}
impl Default for WindowDecorations {
fn default() -> Self {
Self::Full
}
}
struct PathPossibility {
path: PathBuf,
is_required: bool,

View File

@ -50,11 +50,6 @@ impl WindowConfiguration for ConfigBridge {
}
fn decorations(&self) -> ::window::WindowDecorations {
use ::config::WindowDecorations as CWD;
use ::window::WindowDecorations as WD;
match configuration().window_decorations {
CWD::Full => WD::Full,
CWD::None => WD::None,
}
configuration().window_decorations
}
}

View File

@ -1,5 +1,6 @@
use bitflags::*;
use serde::*;
use std::convert::TryFrom;
pub struct PixelUnit;
pub struct ScreenPixelUnit;
@ -253,3 +254,39 @@ impl KeyEvent {
self
}
}
bitflags! {
#[derive(Deserialize, Serialize)]
#[serde(try_from = "String")]
pub struct WindowDecorations: u8 {
const TITLE = 1;
const RESIZE = 2;
const NONE = 0;
}
}
impl TryFrom<String> for WindowDecorations {
type Error = String;
fn try_from(s: String) -> std::result::Result<WindowDecorations, String> {
let mut flags = Self::NONE;
for ele in s.split('|') {
let ele = ele.trim();
if ele == "TITLE" {
flags |= Self::TITLE;
} else if ele == "NONE" || ele == "None" {
flags = Self::NONE;
} else if ele == "RESIZE" {
flags |= Self::RESIZE;
} else {
return Err(format!("invalid WindowDecoration name {} in {}", ele, s));
}
}
Ok(flags)
}
}
impl Default for WindowDecorations {
fn default() -> Self {
WindowDecorations::TITLE | WindowDecorations::RESIZE
}
}

View File

@ -78,18 +78,6 @@ pub enum MouseCursor {
SizeLeftRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WindowDecorations {
Full,
None,
}
impl Default for WindowDecorations {
fn default() -> Self {
Self::Full
}
}
#[allow(unused_variables)]
pub trait WindowCallbacks: Any {
/// Called when the window close button is clicked.

View File

@ -859,14 +859,24 @@ impl WindowOpsMut for WindowInner {
}
fn decoration_to_mask(decorations: WindowDecorations) -> NSWindowStyleMask {
match decorations {
WindowDecorations::Full => {
NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
| NSWindowStyleMask::NSResizableWindowMask
}
WindowDecorations::None => NSWindowStyleMask::NSResizableWindowMask,
if decorations == WindowDecorations::TITLE | WindowDecorations::RESIZE {
NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
| NSWindowStyleMask::NSResizableWindowMask
} else if decorations == WindowDecorations::RESIZE {
NSWindowStyleMask::NSResizableWindowMask
} else if decorations == WindowDecorations::NONE {
NSWindowStyleMask::NSBorderlessWindowMask
} else if decorations == WindowDecorations::TITLE {
NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
} else {
NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
| NSWindowStyleMask::NSResizableWindowMask
}
}

View File

@ -64,14 +64,14 @@ fn rect_height(r: &RECT) -> i32 {
r.bottom - r.top
}
fn adjust_client_to_window_dimensions(width: usize, height: usize) -> (i32, i32) {
fn adjust_client_to_window_dimensions(style: u32, width: usize, height: usize) -> (i32, i32) {
let mut rect = RECT {
left: 0,
top: 0,
right: width as _,
bottom: height as _,
};
unsafe { AdjustWindowRect(&mut rect, WS_POPUP | WS_SYSMENU | WS_CAPTION, 0) };
unsafe { AdjustWindowRect(&mut rect, style, 0) };
(rect_width(&rect), rect_height(&rect))
}
@ -215,34 +215,46 @@ impl WindowInner {
fn apply_decoration(&mut self) {
let hwnd = self.hwnd.0;
promise::spawn::spawn(async move {
unsafe {
let orig_style = GetWindowLongW(hwnd, GWL_STYLE);
let style = decorations_to_style(config().decorations());
SetWindowLongW(
hwnd,
GWL_STYLE,
(orig_style & !(WS_OVERLAPPEDWINDOW as i32)) | style as i32,
);
SetWindowPos(
hwnd,
std::ptr::null_mut(),
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED,
);
}
})
.detach();
schedule_apply_decoration(hwnd, config().decorations());
}
}
fn schedule_apply_decoration(hwnd: HWND, decorations: WindowDecorations) {
promise::spawn::spawn(async move {
apply_decoration_immediate(hwnd, decorations);
})
.detach();
}
fn apply_decoration_immediate(hwnd: HWND, decorations: WindowDecorations) {
unsafe {
let orig_style = GetWindowLongW(hwnd, GWL_STYLE);
let style = decorations_to_style(decorations);
let new_style = (orig_style & !(WS_OVERLAPPEDWINDOW as i32)) | style as i32;
SetWindowLongW(hwnd, GWL_STYLE, new_style);
SetWindowPos(
hwnd,
std::ptr::null_mut(),
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED,
);
}
}
fn decorations_to_style(decorations: WindowDecorations) -> u32 {
match decorations {
WindowDecorations::Full => WS_OVERLAPPEDWINDOW,
WindowDecorations::None => WS_THICKFRAME,
if decorations == WindowDecorations::RESIZE {
WS_THICKFRAME
} else if decorations == WindowDecorations::TITLE {
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
} else if decorations == WindowDecorations::NONE {
WS_POPUP
} else if decorations == WindowDecorations::TITLE | WindowDecorations::RESIZE {
WS_OVERLAPPEDWINDOW
} else {
WS_OVERLAPPEDWINDOW
}
}
@ -291,9 +303,33 @@ impl Window {
}
}
let (width, height) = adjust_client_to_window_dimensions(width, height);
let decorations = config().decorations();
let style = decorations_to_style(decorations);
let (width, height) = adjust_client_to_window_dimensions(style, width, height);
let style = decorations_to_style(config().decorations());
let (x, y) = if (style & WS_POPUP) == 0 {
(CW_USEDEFAULT, CW_USEDEFAULT)
} else {
// WS_POPUP windows need to specify the initial position.
// We pick the middle of the primary monitor
unsafe {
let mut mi: MONITORINFO = std::mem::zeroed();
mi.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
GetMonitorInfoW(
MonitorFromWindow(std::ptr::null_mut(), MONITOR_DEFAULTTOPRIMARY),
&mut mi,
);
let mon_width = mi.rcMonitor.right - mi.rcMonitor.left;
let mon_height = mi.rcMonitor.bottom - mi.rcMonitor.top;
(
mi.rcMonitor.left + (mon_width - width) / 2,
mi.rcMonitor.top + (mon_height - height) / 2,
)
}
};
let name = wide_string(name);
let hwnd = unsafe {
@ -302,8 +338,8 @@ impl Window {
class_name.as_ptr(),
name.as_ptr(),
style,
CW_USEDEFAULT,
CW_USEDEFAULT,
x,
y,
width,
height,
null_mut(),
@ -318,6 +354,10 @@ impl Window {
bail!("CreateWindowExW: {}", err);
}
// We have to re-apply the styles otherwise they don't
// completely stick
schedule_apply_decoration(hwnd, decorations);
Ok(hwnd)
}
@ -410,7 +450,11 @@ impl WindowOpsMut for WindowInner {
}
fn set_inner_size(&mut self, width: usize, height: usize) {
let (width, height) = adjust_client_to_window_dimensions(width, height);
let (width, height) = adjust_client_to_window_dimensions(
decorations_to_style(config().decorations()),
width,
height,
);
let hwnd = self.hwnd;
promise::spawn::spawn(async move {
unsafe {

View File

@ -611,13 +611,13 @@ impl XWindowInner {
xcb::ClientMessageData::from_data32(data),
),
);
self.adjust_decorations(config().decorations() == WindowDecorations::Full)?;
self.adjust_decorations(config().decorations())?;
Ok(())
}
#[allow(clippy::identity_op)]
fn adjust_decorations(&mut self, enable: bool) -> anyhow::Result<()> {
fn adjust_decorations(&mut self, decorations: WindowDecorations) -> anyhow::Result<()> {
// Set the motif hints to disable decorations.
// See https://stackoverflow.com/a/1909708
#[repr(C)]
@ -632,18 +632,28 @@ impl XWindowInner {
const HINTS_DECORATIONS: u32 = 1 << 1;
const FUNC_ALL: u32 = 1 << 0;
const FUNC_RESIZE: u32 = 1 << 1;
/*
const HINTS_FUNCTIONS: u32 = 1 << 0;
// const HINTS_FUNCTIONS: u32 = 1 << 0;
const FUNC_MOVE: u32 = 1 << 2;
const FUNC_MINIMIZE: u32 = 1 << 3;
const FUNC_MAXIMIZE: u32 = 1 << 4;
const FUNC_CLOSE: u32 = 1 << 5;
*/
let decorations = if decorations == WindowDecorations::TITLE | WindowDecorations::RESIZE {
FUNC_ALL
} else if decorations == WindowDecorations::RESIZE {
FUNC_RESIZE
} else if decorations == WindowDecorations::TITLE {
FUNC_MOVE | FUNC_MINIMIZE | FUNC_MAXIMIZE | FUNC_CLOSE
} else if decorations == WindowDecorations::NONE {
0
} else {
FUNC_ALL
};
let hints = MwmHints {
flags: HINTS_DECORATIONS,
functions: 0,
decorations: if enable { FUNC_ALL } else { FUNC_RESIZE },
decorations,
input_mode: 0,
status: 0,
};
@ -792,7 +802,7 @@ impl XWindow {
window
.lock()
.unwrap()
.adjust_decorations(config().decorations() == WindowDecorations::Full)?;
.adjust_decorations(config().decorations())?;
let window_handle = Window::X11(XWindow::from_id(window_id));
@ -840,7 +850,7 @@ impl WindowOpsMut for XWindowInner {
}
fn config_did_change(&mut self) {
let _ = self.adjust_decorations(config().decorations() == WindowDecorations::Full);
let _ = self.adjust_decorations(config().decorations());
}
fn set_inner_size(&mut self, width: usize, height: usize) {