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.
This commit is contained in:
Joe Moon 2018-09-20 08:24:26 -07:00 committed by Joe Wilm
parent 641f3291eb
commit 3b46859ece
5 changed files with 243 additions and 57 deletions

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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<D>(deserializer: D) -> ::std::result::Result<Decorations, D::Error>
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<E>(self, value: bool) -> ::std::result::Result<Decorations, E>
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<E>(self, value: &str) -> ::std::result::Result<Decorations, E>
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<E>(self, value: &str) -> ::std::result::Result<Decorations, E>
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<u8>,
/// Draw the window with title bar / borders
#[serde(default, deserialize_with = "failure_default")]
decorations: bool,
decorations: Decorations,
}
fn default_padding() -> Delta<u8> {
@ -280,7 +362,7 @@ fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result<Delta<u8
}
impl WindowConfig {
pub fn decorations(&self) -> 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(),
}
}
}

View File

@ -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<Pixels<u32>> {
Size {
width: Points(width_pts),
height: Points(height_pts)
height: Points(height_pts),
}
}
}
@ -147,7 +149,6 @@ macro_rules! deref_newtype {
deref_newtype! { Points<T>, Pixels<T> }
impl<T: Display> Display for Pixels<T> {
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<glutin::GlWindow, glutin::CreationError> {
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<Window> {
pub fn new(options: &Options, window_config: &WindowConfig) -> Result<Window> {
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<Size<Pixels<u32>>> {
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<F>(&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<F>(&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<T> {
impl SetInnerSize<Pixels<u32>> for Window {
fn set_inner_size<T: ToPoints>(&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 _);
}
}