Allow setting gtk variant and general class on X11

This commit is contained in:
Brian Koropoff 2019-07-06 12:34:56 -07:00 committed by Christian Duerr
parent e398eb8406
commit 228c641769
7 changed files with 107 additions and 53 deletions

View file

@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Block selection mode when Control is held while starting a selection
- Allow setting general window class on X11 using CLI or config (`window.class.general`)
- Config option `window.gtk_theme_variant` to set GTK theme variant
### Fixed

View file

@ -66,7 +66,17 @@ window:
#title: Alacritty
# Window class (Linux only):
#class: Alacritty
class:
# Application instance name
instance: Alacritty
# General application class
general: Alacritty
# GTK theme variant (Linux only)
#
# Override the variant of the GTK theme. Commonly supported values are `dark` and `light`.
# Set this to `None` to use the default theme variant.
gtk_theme_variant: None
scrolling:
# Maximum number of lines in the scrollback buffer.

View file

@ -250,7 +250,14 @@ impl Options {
config.window.dimensions = self.dimensions.unwrap_or(config.window.dimensions);
config.window.position = self.position.or(config.window.position);
config.window.title = self.title.or(config.window.title);
config.window.class = self.class.or(config.window.class);
if let Some(class) = self.class {
let parts : Vec<_> = class.split(',').collect();
config.window.class.instance = parts[0].into();
if let Some(&general) = parts.get(1) {
config.window.class.general = general.into();
}
}
config.set_dynamic_title(config.dynamic_title() && config.window.title.is_none());

View file

@ -15,8 +15,10 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::path::PathBuf;
use std::fmt::Display;
use serde::{Deserialize, Deserializer};
use serde_yaml::Value;
mod bindings;
mod colors;
@ -92,7 +94,7 @@ pub struct Config {
pub mouse: Mouse,
/// Path to a shell program to run on startup
#[serde(default, deserialize_with = "failure_default")]
#[serde(default, deserialize_with = "from_string_or_deserialize")]
pub shell: Option<Shell<'static>>,
/// Path where config was loaded from
@ -134,8 +136,8 @@ pub struct Config {
alt_send_esc: DefaultTrueBool,
/// Shell startup directory
#[serde(default, deserialize_with = "failure_default")]
working_directory: WorkingDirectory,
#[serde(default, deserialize_with = "option_explicit_none")]
working_directory: Option<PathBuf>,
/// Debug options
#[serde(default, deserialize_with = "failure_default")]
@ -212,37 +214,12 @@ impl Config {
#[inline]
pub fn working_directory(&self) -> &Option<PathBuf> {
&self.working_directory.0
&self.working_directory
}
#[inline]
pub fn set_working_directory(&mut self, working_directory: Option<PathBuf>) {
self.working_directory.0 = working_directory;
}
}
#[derive(Default, Debug, PartialEq, Eq)]
struct WorkingDirectory(Option<PathBuf>);
impl<'de> Deserialize<'de> for WorkingDirectory {
fn deserialize<D>(deserializer: D) -> Result<WorkingDirectory, D::Error>
where
D: Deserializer<'de>,
{
let value = serde_yaml::Value::deserialize(deserializer)?;
// Accept `None` to use the default path
if value.as_str().filter(|v| v.to_lowercase() == "none").is_some() {
return Ok(WorkingDirectory(None));
}
Ok(match PathBuf::deserialize(value) {
Ok(path) => WorkingDirectory(Some(path)),
Err(err) => {
error!("Problem with config: {}; using None", err);
WorkingDirectory(None)
},
})
self.working_directory = working_directory;
}
}
@ -357,6 +334,12 @@ impl<'a> Shell<'a> {
}
}
impl FromString for Option<Shell<'_>> {
fn from(input: String) -> Self {
Some(Shell::new(input))
}
}
/// A delta for a point in a 2 dimensional plane
#[serde(default, bound(deserialize = "T: Deserialize<'de> + Default"))]
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
@ -418,17 +401,40 @@ impl Default for DefaultTrueBool {
}
}
fn fallback_default<T, E>(err: E) -> T
where T: Default, E: Display
{
error!("Problem with config: {}; using default value", err);
T::default()
}
pub fn failure_default<'a, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'a>,
T: Deserialize<'a> + Default,
{
let value = serde_yaml::Value::deserialize(deserializer)?;
match T::deserialize(value) {
Ok(value) => Ok(value),
Err(err) => {
error!("Problem with config: {}; using default value", err);
Ok(T::default())
},
}
Ok(T::deserialize(Value::deserialize(deserializer)?).unwrap_or_else(fallback_default))
}
pub fn option_explicit_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where D: Deserializer<'de>, T: Deserialize<'de> + Default
{
Ok(match Value::deserialize(deserializer)? {
Value::String(ref value) if value.to_lowercase() == "none" => None,
value => Some(T::deserialize(value).unwrap_or_else(fallback_default))
})
}
pub fn from_string_or_deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where D: Deserializer<'de>, T: Deserialize<'de> + FromString + Default
{
Ok(match Value::deserialize(deserializer)? {
Value::String(value) => T::from(value),
value => T::deserialize(value).unwrap_or_else(fallback_default)
})
}
// Used over From<String>, to allow implementation for foreign types
pub trait FromString {
fn from(input: String) -> Self;
}

View file

@ -1,5 +1,6 @@
use crate::config::{failure_default, Delta};
use crate::config::{failure_default, option_explicit_none, from_string_or_deserialize, Delta, FromString};
use crate::index::{Column, Line};
use crate::window::DEFAULT_NAME;
#[serde(default)]
#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
@ -33,8 +34,12 @@ pub struct WindowConfig {
pub title: Option<String>,
/// Window class
#[serde(deserialize_with = "failure_default")]
pub class: Option<String>,
#[serde(deserialize_with = "from_string_or_deserialize")]
pub class: Class,
/// GTK theme variant
#[serde(deserialize_with = "option_explicit_none")]
pub gtk_theme_variant: Option<String>,
/// TODO: DEPRECATED
#[serde(deserialize_with = "failure_default")]
@ -117,3 +122,23 @@ impl Dimensions {
self.columns.0 as u32
}
}
/// Window class hint
#[serde(default)]
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Class {
pub instance: String,
pub general: String
}
impl Default for Class {
fn default() -> Self {
Class { instance: DEFAULT_NAME.into(), general: DEFAULT_NAME.into() }
}
}
impl FromString for Class {
fn from(value: String) -> Self {
Class { instance: value, general: DEFAULT_NAME.into() }
}
}

View file

@ -151,9 +151,8 @@ impl Window {
dimensions: Option<LogicalSize>,
) -> Result<Window> {
let title = config.window.title.as_ref().map_or(DEFAULT_NAME, |t| t);
let class = config.window.class.as_ref().map_or(DEFAULT_NAME, |c| c);
let window_builder = Window::get_platform_window(title, class, &config.window);
let window_builder = Window::get_platform_window(title, &config.window);
let windowed_context =
create_gl_window(window_builder.clone(), &event_loop, false, dimensions)
.or_else(|_| create_gl_window(window_builder, &event_loop, true, dimensions))?;
@ -255,7 +254,6 @@ impl Window {
#[cfg(not(any(target_os = "macos", windows)))]
pub fn get_platform_window(
title: &str,
class: &str,
window_config: &WindowConfig,
) -> WindowBuilder {
use glutin::os::unix::WindowBuilderExt;
@ -267,7 +265,9 @@ impl Window {
let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO);
WindowBuilder::new()
let class = &window_config.class;
let mut builder = WindowBuilder::new()
.with_title(title)
.with_visibility(false)
.with_transparency(true)
@ -275,15 +275,20 @@ impl Window {
.with_maximized(window_config.startup_mode() == StartupMode::Maximized)
.with_window_icon(icon.ok())
// X11
.with_class(class.into(), DEFAULT_NAME.into())
.with_class(class.instance.clone(), class.general.clone())
// Wayland
.with_app_id(class.into())
.with_app_id(class.instance.clone());
if let Some(ref val) = window_config.gtk_theme_variant {
builder = builder.with_gtk_theme_variant(val.clone())
}
builder
}
#[cfg(windows)]
pub fn get_platform_window(
title: &str,
_class: &str,
window_config: &WindowConfig,
) -> WindowBuilder {
let decorations = match window_config.decorations {
@ -305,7 +310,6 @@ impl Window {
#[cfg(target_os = "macos")]
pub fn get_platform_window(
title: &str,
_class: &str,
window_config: &WindowConfig,
) -> WindowBuilder {
use glutin::os::macos::WindowBuilderExt;

View file

@ -39,8 +39,8 @@ Increases the level of verbosity (the max level is \fB\-vvv\fR)
Prints version information
.SH "OPTIONS"
.TP
\fB\-\-class\fR <class>
Defines the window class on Linux [default: Alacritty]
\fB\-\-class\fR [ <instance> | <instance>,<general> ]
Defines the window class hint on Linux [default: Alacritty,Alacritty ]
.TP
\fB\-e\fR, \fB\-\-command\fR <command>...
Command and args to execute (must be last argument)