From 3b46859eceea39afb8bbc760235cc15de78d3ff3 Mon Sep 17 00:00:00 2001 From: Joe Moon Date: Thu, 20 Sep 2018 08:24:26 -0700 Subject: [PATCH] Improve window.decorations options: (#1241) The decorations config was changed from a bool to an enum. `full` has taken the place of `true`, and `none`, has replaced `false`. On macOS, there are now options for `transparent` and `buttonless`. These options are explained in both the CHANGELOG and in the configuration files. --- CHANGELOG.md | 9 +++ alacritty.yml | 6 +- alacritty_macos.yml | 10 ++- src/config.rs | 90 ++++++++++++++++++++- src/window.rs | 185 ++++++++++++++++++++++++++++++++------------ 5 files changed, 243 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c42ccec..adcc849c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implement the `hidden` escape sequence (`echo -e "\e[8mTEST"`) - Add support for macOS systemwide dark mode - Set the environment variable `COLORTERM="truecolor"` to advertise 24-bit color support +- On macOS, there are two new values for the config option `window.decorations`: + - `transparent` - This makes the title bar transparent and allows the + viewport to extend to the top of the window. + - `buttonless` - Similar to transparent but also removed the buttons. ### Changed @@ -23,6 +27,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Windows started as unfocused now show the hollow cursor if the setting is enabled - Bracketed paste mode now filters escape sequences beginning with \x1b +### Deprecated + +- The config option `window.decorations` should now use `full` or `none` instead + of `true` or `false`, respectively. + ## Version 0.2.0 ### Added diff --git a/alacritty.yml b/alacritty.yml index a0225f56..49a33737 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -30,8 +30,10 @@ window: y: 2 # Window decorations - # Setting this to false will result in window without borders and title bar. - decorations: true + # Available values: + # - `full`: Window with borders and title bar. + # - `none`: Window without borders or title bar. + decorations: full scrolling: # How many lines of scrollback to keep, diff --git a/alacritty_macos.yml b/alacritty_macos.yml index 7834cc78..9c911e26 100644 --- a/alacritty_macos.yml +++ b/alacritty_macos.yml @@ -28,8 +28,14 @@ window: y: 2 # Window decorations - # Setting this to false will result in window without borders and title bar. - decorations: true + # Available values: + # - `full`: Window with title bar and title bar buttons + # - `none`: Window without title bar, rounded corners, or drop shadow + # - `transparent`: Window with title bar with transparent background and title + # bar buttons + # - `buttonless`: Window with title bar with transparent background and no + # title bar buttons + decorations: full scrolling: # How many lines of scrollback to keep, diff --git a/src/config.rs b/src/config.rs index 6fad09f9..58e282d2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -248,6 +248,89 @@ impl Default for Alpha { } } +#[derive(Debug, Copy, Clone)] +pub enum Decorations { + Full, + Transparent, + Buttonless, + None, +} + +impl Default for Decorations { + fn default() -> Decorations { + Decorations::Full + } +} + +impl<'de> Deserialize<'de> for Decorations { + fn deserialize(deserializer: D) -> ::std::result::Result + where D: de::Deserializer<'de> + { + + struct DecorationsVisitor; + + impl<'de> Visitor<'de> for DecorationsVisitor { + type Value = Decorations; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("Some subset of full|transparent|buttonless|none") + } + + fn visit_bool(self, value: bool) -> ::std::result::Result + where E: de::Error + { + if value { + eprintln!("deprecated decorations boolean value, use one of default|transparent|buttonless|none instead; Falling back to \"full\""); + Ok(Decorations::Full) + } else { + eprintln!("deprecated decorations boolean value, use one of default|transparent|buttonless|none instead; Falling back to \"none\""); + Ok(Decorations::None) + } + } + + #[cfg(target_os = "macos")] + fn visit_str(self, value: &str) -> ::std::result::Result + where E: de::Error + { + match value { + "transparent" => Ok(Decorations::Transparent), + "buttonless" => Ok(Decorations::Buttonless), + "none" => Ok(Decorations::None), + "full" => Ok(Decorations::Full), + _ => { + eprintln!("invalid decorations value: {}; Using default value", value); + Ok(Decorations::Full) + } + } + } + + #[cfg(not(target_os = "macos"))] + fn visit_str(self, value: &str) -> ::std::result::Result + where E: de::Error + { + match value { + "none" => Ok(Decorations::None), + "full" => Ok(Decorations::Full), + "transparent" => { + eprintln!("macos-only decorations value: {}; Using default value", value); + Ok(Decorations::Full) + }, + "buttonless" => { + eprintln!("macos-only decorations value: {}; Using default value", value); + Ok(Decorations::Full) + } + _ => { + eprintln!("invalid decorations value: {}; Using default value", value); + Ok(Decorations::Full) + } + } + } + } + + deserializer.deserialize_str(DecorationsVisitor) + } +} + #[derive(Debug, Copy, Clone, Deserialize)] pub struct WindowConfig { /// Initial dimensions @@ -259,8 +342,7 @@ pub struct WindowConfig { padding: Delta, /// Draw the window with title bar / borders - #[serde(default, deserialize_with = "failure_default")] - decorations: bool, + decorations: Decorations, } fn default_padding() -> Delta { @@ -280,7 +362,7 @@ fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result bool { + pub fn decorations(&self) -> Decorations { self.decorations } } @@ -290,7 +372,7 @@ impl Default for WindowConfig { WindowConfig{ dimensions: Default::default(), padding: default_padding(), - decorations: true, + decorations: Default::default(), } } } diff --git a/src/window.rs b/src/window.rs index ede29716..eac4c014 100644 --- a/src/window.rs +++ b/src/window.rs @@ -16,14 +16,16 @@ use std::fmt::{self, Display}; use std::ops::Deref; use gl; -use glutin::{self, ContextBuilder, ControlFlow, CursorState, Event, EventsLoop, - MouseCursor as GlutinMouseCursor, WindowBuilder}; use glutin::GlContext; +use glutin::{ + self, ContextBuilder, ControlFlow, CursorState, Event, EventsLoop, + MouseCursor as GlutinMouseCursor, WindowBuilder, +}; use MouseCursor; use cli::Options; -use config::WindowConfig; +use config::{Decorations, WindowConfig}; /// Default text for the window's title bar, if not overriden. /// @@ -119,7 +121,7 @@ impl ToPoints for Size> { Size { width: Points(width_pts), - height: Points(height_pts) + height: Points(height_pts), } } } @@ -147,7 +149,6 @@ macro_rules! deref_newtype { deref_newtype! { Points, Pixels } - impl Display for Pixels { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}px", self.0) @@ -179,12 +180,8 @@ impl ::std::error::Error for Error { impl Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { - Error::ContextCreation(ref err) => { - write!(f, "Error creating GL context; {}", err) - }, - Error::Context(ref err) => { - write!(f, "Error operating on render context; {}", err) - }, + Error::ContextCreation(ref err) => write!(f, "Error creating GL context; {}", err), + Error::Context(ref err) => write!(f, "Error operating on render context; {}", err), } } } @@ -206,9 +203,7 @@ fn create_gl_window( event_loop: &EventsLoop, srgb: bool, ) -> ::std::result::Result { - let context = ContextBuilder::new() - .with_srgb(srgb) - .with_vsync(true); + let context = ContextBuilder::new().with_srgb(srgb).with_vsync(true); ::glutin::GlWindow::new(window, context, event_loop) } @@ -216,19 +211,12 @@ impl Window { /// Create a new window /// /// This creates a window and fully initializes a window. - pub fn new( - options: &Options, - window_config: &WindowConfig, - ) -> Result { + pub fn new(options: &Options, window_config: &WindowConfig) -> Result { let event_loop = EventsLoop::new(); let title = options.title.as_ref().map_or(DEFAULT_TITLE, |t| t); let class = options.class.as_ref().map_or(DEFAULT_CLASS, |c| c); - let window_builder = WindowBuilder::new() - .with_title(title) - .with_visibility(false) - .with_transparency(true) - .with_decorations(window_config.decorations()); + let window_builder = Window::get_platform_window(title, window_config); let window_builder = Window::platform_builder_ext(window_builder, &class); let window = create_gl_window(window_builder.clone(), &event_loop, false) .or_else(|_| create_gl_window(window_builder, &event_loop, true))?; @@ -268,9 +256,10 @@ impl Window { } pub fn inner_size_pixels(&self) -> Option>> { - self.window - .get_inner_size() - .map(|(w, h)| Size { width: Pixels(w), height: Pixels(h) }) + self.window.get_inner_size().map(|(w, h)| Size { + width: Pixels(w), + height: Pixels(h), + }) } #[inline] @@ -287,15 +276,14 @@ impl Window { #[inline] pub fn swap_buffers(&self) -> Result<()> { - self.window - .swap_buffers() - .map_err(From::from) + self.window.swap_buffers().map_err(From::from) } /// Poll for any available events #[inline] pub fn poll_events(&mut self, func: F) - where F: FnMut(Event) + where + F: FnMut(Event), { self.event_loop.poll_events(func); } @@ -308,7 +296,8 @@ impl Window { /// Block waiting for events #[inline] pub fn wait_events(&mut self, func: F) - where F: FnMut(Event) -> ControlFlow + where + F: FnMut(Event) -> ControlFlow, { self.event_loop.run_forever(func); } @@ -341,24 +330,95 @@ impl Window { } } - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] + #[cfg( + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd" + ) + )] fn platform_builder_ext(window_builder: WindowBuilder, wm_class: &str) -> WindowBuilder { use glutin::os::unix::WindowBuilderExt; window_builder.with_class(wm_class.to_owned(), "Alacritty".to_owned()) } - #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd")))] + #[cfg( + not( + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd" + ) + ) + )] fn platform_builder_ext(window_builder: WindowBuilder, _: &str) -> WindowBuilder { window_builder } - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] + #[cfg(not(target_os = "macos"))] + pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder { + let decorations = match window_config.decorations() { + Decorations::None => false, + _ => true, + }; + + WindowBuilder::new() + .with_title(title) + .with_visibility(false) + .with_transparency(true) + .with_decorations(decorations) + } + + #[cfg(target_os = "macos")] + pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder { + use glutin::os::macos::WindowBuilderExt; + + let window = WindowBuilder::new() + .with_title(title) + .with_visibility(false) + .with_transparency(true); + + match window_config.decorations() { + Decorations::Full => window, + Decorations::Transparent => window + .with_title_hidden(true) + .with_titlebar_transparent(true) + .with_fullsize_content_view(true), + Decorations::Buttonless => window + .with_title_hidden(true) + .with_titlebar_buttons_hidden(true) + .with_titlebar_transparent(true) + .with_fullsize_content_view(true), + Decorations::None => window + .with_titlebar_hidden(true), + } + } + + #[cfg( + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd" + ) + )] pub fn set_urgent(&self, is_urgent: bool) { use glutin::os::unix::WindowExt; self.window.set_urgent(is_urgent); } - #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd")))] + #[cfg( + not( + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd" + ) + ) + )] pub fn set_urgent(&self, _is_urgent: bool) {} pub fn set_ime_spot(&self, x: i32, y: i32) { @@ -371,7 +431,7 @@ impl Window { match self.window.get_xlib_window() { Some(xlib_window) => Some(xlib_window as usize), - None => None + None => None, } } @@ -390,17 +450,28 @@ pub trait OsExtensions { fn run_os_extensions(&self) {} } -#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="openbsd")))] -impl OsExtensions for Window { } +#[cfg( + not( + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd" + ) + ) +)] +impl OsExtensions for Window {} -#[cfg(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="openbsd"))] +#[cfg( + any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd") +)] impl OsExtensions for Window { fn run_os_extensions(&self) { use glutin::os::unix::WindowExt; - use x11_dl::xlib::{self, XA_CARDINAL, PropModeReplace}; - use std::ffi::{CStr}; - use std::ptr; use libc::getpid; + use std::ffi::CStr; + use std::ptr; + use x11_dl::xlib::{self, PropModeReplace, XA_CARDINAL}; let xlib_display = self.window.get_xlib_display(); let xlib_window = self.window.get_xlib_window(); @@ -414,17 +485,32 @@ impl OsExtensions for Window { let atom = (xlib.XInternAtom)(xlib_display as *mut _, _net_wm_pid.as_ptr(), 0); let pid = getpid(); - (xlib.XChangeProperty)(xlib_display as _, xlib_window as _, atom, - XA_CARDINAL, 32, PropModeReplace, &pid as *const i32 as *const u8, 1); - + (xlib.XChangeProperty)( + xlib_display as _, + xlib_window as _, + atom, + XA_CARDINAL, + 32, + PropModeReplace, + &pid as *const i32 as *const u8, + 1, + ); } // Although this call doesn't actually pass any data, it does cause // WM_CLIENT_MACHINE to be set. WM_CLIENT_MACHINE MUST be set if _NET_WM_PID is set // (which we do above). unsafe { - (xlib.XSetWMProperties)(xlib_display as _, xlib_window as _, ptr::null_mut(), - ptr::null_mut(), ptr::null_mut(), 0, ptr::null_mut(), ptr::null_mut(), - ptr::null_mut()); + (xlib.XSetWMProperties)( + xlib_display as _, + xlib_window as _, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ); } } } @@ -447,6 +533,7 @@ pub trait SetInnerSize { impl SetInnerSize> for Window { fn set_inner_size(&mut self, size: &T) { let size = size.to_points(self.hidpi_factor()); - self.window.set_inner_size(*size.width as _, *size.height as _); + self.window + .set_inner_size(*size.width as _, *size.height as _); } }