Skip to content

Commit

Permalink
Add title/class CLI parameters to create-window
Browse files Browse the repository at this point in the history
This adds the ability to pass title and class over IPC via the
create-window subcommand, so users can run only one instance for windows
of different spurposes in the window managers of their choice.
  • Loading branch information
kchibisov authored Jan 3, 2022
1 parent e2b5219 commit ce59fa4
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 88 deletions.
68 changes: 46 additions & 22 deletions alacritty/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use structopt::StructOpt;

use alacritty_terminal::config::{Program, PtyConfig};

use crate::config::window::{Class, DEFAULT_NAME};
use crate::config::window::{Class, Identity, DEFAULT_NAME};
use crate::config::{serde_utils, UiConfig};

/// CLI options for the main Alacritty executable.
Expand All @@ -23,14 +23,6 @@ pub struct Options {
#[structopt(long)]
pub ref_test: bool,

/// Defines the window title [default: Alacritty].
#[structopt(short, long)]
pub title: Option<String>,

/// Defines window class/app_id on X11/Wayland [default: Alacritty].
#[structopt(long, value_name = "instance> | <instance>,<general", parse(try_from_str = parse_class))]
pub class: Option<Class>,

/// Defines the X11 window ID (as a decimal integer) to embed Alacritty within.
#[structopt(long)]
pub embed: Option<String>,
Expand Down Expand Up @@ -71,9 +63,9 @@ pub struct Options {
#[structopt(skip)]
pub config_options: Value,

/// Terminal options which could be passed via IPC.
/// Options which could be passed via IPC.
#[structopt(flatten)]
pub terminal_options: TerminalOptions,
pub window_options: WindowOptions,

/// Subcommand passed to the CLI.
#[cfg(unix)]
Expand All @@ -100,19 +92,12 @@ impl Options {

/// Override configuration file with options from the CLI.
pub fn override_config(&self, config: &mut UiConfig) {
if let Some(title) = self.title.clone() {
config.window.title = title
}
if let Some(class) = &self.class {
config.window.class = class.clone();
}

#[cfg(unix)]
{
config.ipc_socket |= self.socket.is_some();
}

config.window.dynamic_title &= self.title.is_none();
config.window.dynamic_title &= self.window_options.window_identity.title.is_none();
config.window.embed = self.embed.as_ref().and_then(|embed| embed.parse().ok());
config.debug.print_events |= self.print_events;
config.debug.log_level = max(config.debug.log_level, self.log_level());
Expand Down Expand Up @@ -237,6 +222,30 @@ impl From<TerminalOptions> for PtyConfig {
}
}

/// Window specific cli options which can be passed to new windows via IPC.
#[derive(Serialize, Deserialize, StructOpt, Default, Debug, Clone, PartialEq)]
pub struct WindowIdentity {
/// Defines the window title [default: Alacritty].
#[structopt(short, long)]
pub title: Option<String>,

/// Defines window class/app_id on X11/Wayland [default: Alacritty].
#[structopt(long, value_name = "instance> | <instance>,<general", parse(try_from_str = parse_class))]
pub class: Option<Class>,
}

impl WindowIdentity {
/// Override the [`WindowIdentityConfig`]'s fields with the [`WindowOptions`].
pub fn override_identity_config(&self, identity: &mut Identity) {
if let Some(title) = &self.title {
identity.title = title.clone();
}
if let Some(class) = &self.class {
identity.class = class.clone();
}
}
}

/// Available CLI subcommands.
#[cfg(unix)]
#[derive(StructOpt, Debug)]
Expand All @@ -262,7 +271,19 @@ pub struct MessageOptions {
#[derive(StructOpt, Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum SocketMessage {
/// Create a new window in the same Alacritty process.
CreateWindow(TerminalOptions),
CreateWindow(WindowOptions),
}

/// Subset of options that we pass to a 'create-window' subcommand.
#[derive(StructOpt, Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct WindowOptions {
/// Terminal options which can be passed via IPC.
#[structopt(flatten)]
pub terminal_options: TerminalOptions,

#[structopt(flatten)]
/// Window options which could be passed via IPC.
pub window_identity: WindowIdentity,
}

#[cfg(test)]
Expand Down Expand Up @@ -292,7 +313,10 @@ mod tests {
fn dynamic_title_overridden_by_options() {
let mut config = UiConfig::default();

let options = Options { title: Some("foo".to_owned()), ..Options::default() };
let title = Some(String::from("foo"));
let window_identity = WindowIdentity { title, ..WindowIdentity::default() };
let new_window_options = WindowOptions { window_identity, ..WindowOptions::default() };
let options = Options { window_options: new_window_options, ..Options::default() };
options.override_config(&mut config);

assert!(!config.window.dynamic_title);
Expand All @@ -302,7 +326,7 @@ mod tests {
fn dynamic_title_not_overridden_by_config() {
let mut config = UiConfig::default();

config.window.title = "foo".to_owned();
config.window.identity.title = "foo".to_owned();
Options::default().override_config(&mut config);

assert!(config.window.dynamic_title);
Expand Down
30 changes: 21 additions & 9 deletions alacritty/src/config/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::os::raw::c_ulong;
use glutin::window::Fullscreen;
use log::error;
use serde::de::{self, MapAccess, Visitor};
use serde::{Deserialize, Deserializer};
use serde::{Deserialize, Deserializer, Serialize};

use alacritty_config_derive::ConfigDeserialize;
use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG};
Expand Down Expand Up @@ -39,11 +39,9 @@ pub struct WindowConfig {
/// Use dynamic title.
pub dynamic_title: bool,

/// Window title.
pub title: String,

/// Window class.
pub class: Class,
/// Information to identify a particular window.
#[config(flatten)]
pub identity: Identity,

/// Background opacity from 0.0 to 1.0.
pub opacity: Percentage,
Expand All @@ -59,14 +57,13 @@ impl Default for WindowConfig {
fn default() -> Self {
Self {
dynamic_title: true,
title: DEFAULT_NAME.into(),
position: Default::default(),
decorations: Default::default(),
startup_mode: Default::default(),
embed: Default::default(),
gtk_theme_variant: Default::default(),
dynamic_padding: Default::default(),
class: Default::default(),
identity: Identity::default(),
opacity: Default::default(),
padding: Default::default(),
dimensions: Default::default(),
Expand Down Expand Up @@ -109,6 +106,21 @@ impl WindowConfig {
}
}

#[derive(ConfigDeserialize, Debug, Clone, PartialEq)]
pub struct Identity {
/// Window title.
pub title: String,

/// Window class.
pub class: Class,
}

impl Default for Identity {
fn default() -> Self {
Self { title: DEFAULT_NAME.into(), class: Default::default() }
}
}

#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum StartupMode {
Windowed,
Expand Down Expand Up @@ -153,7 +165,7 @@ pub struct Dimensions {
}

/// Window class hint.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
pub struct Class {
pub instance: String,
pub general: String,
Expand Down
4 changes: 3 additions & 1 deletion alacritty/src/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::{SizeInfo, Term, TermMode, MIN_COLUMNS, MIN_SCREEN_LINES};

use crate::config::font::Font;
use crate::config::window::Dimensions;
#[cfg(not(windows))]
use crate::config::window::StartupMode;
use crate::config::window::{Dimensions, Identity};
use crate::config::UiConfig;
use crate::display::bell::VisualBell;
use crate::display::color::List;
Expand Down Expand Up @@ -202,6 +202,7 @@ impl Display {
pub fn new<E>(
config: &UiConfig,
event_loop: &EventLoopWindowTarget<E>,
identity: &Identity,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue: Option<&EventQueue>,
) -> Result<Display, Error> {
Expand Down Expand Up @@ -236,6 +237,7 @@ impl Display {
let mut window = Window::new(
event_loop,
config,
identity,
estimated_size,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue,
Expand Down
44 changes: 27 additions & 17 deletions alacritty/src/display/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use winapi::shared::minwindef::WORD;
use alacritty_terminal::index::Point;
use alacritty_terminal::term::SizeInfo;

use crate::config::window::{Decorations, WindowConfig};
use crate::config::window::{Decorations, Identity, WindowConfig};
use crate::config::UiConfig;
use crate::gl;

Expand Down Expand Up @@ -154,6 +154,9 @@ pub struct Window {
/// Cached DPR for quickly scaling pixel sizes.
pub dpr: f64,

/// Current window title.
title: String,

windowed_context: Replaceable<WindowedContext<PossiblyCurrent>>,
current_mouse_cursor: CursorIcon,
mouse_visible: bool,
Expand All @@ -166,12 +169,13 @@ impl Window {
pub fn new<E>(
event_loop: &EventLoopWindowTarget<E>,
config: &UiConfig,
identity: &Identity,
size: Option<PhysicalSize<u32>>,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue: Option<&EventQueue>,
) -> Result<Window> {
let window_config = &config.window;
let window_builder = Window::get_platform_window(&window_config.title, window_config);
let identity = identity.clone();
let window_builder = Window::get_platform_window(&identity, &config.window);

// Check if we're running Wayland to disable vsync.
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
Expand All @@ -195,7 +199,7 @@ impl Window {
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
if !is_wayland {
// On X11, embed the window inside another if the parent ID has been set.
if let Some(parent_window_id) = window_config.embed {
if let Some(parent_window_id) = config.window.embed {
x_embed_window(windowed_context.window(), parent_window_id);
}
}
Expand All @@ -216,6 +220,7 @@ impl Window {
current_mouse_cursor,
mouse_visible: true,
windowed_context: Replaceable::new(windowed_context),
title: identity.title,
#[cfg(not(any(target_os = "macos", windows)))]
should_draw: Arc::new(AtomicBool::new(true)),
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
Expand All @@ -241,8 +246,15 @@ impl Window {

/// Set the window title.
#[inline]
pub fn set_title(&self, title: &str) {
self.window().set_title(title);
pub fn set_title(&mut self, title: String) {
self.title = title;
self.window().set_title(&self.title);
}

/// Get the window title.
#[inline]
pub fn title(&self) -> &str {
&self.title
}

#[inline]
Expand All @@ -267,7 +279,7 @@ impl Window {
}

#[cfg(not(any(target_os = "macos", windows)))]
pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
pub fn get_platform_window(identity: &Identity, window_config: &WindowConfig) -> WindowBuilder {
#[cfg(feature = "x11")]
let icon = {
let decoder = Decoder::new(Cursor::new(WINDOW_ICON));
Expand All @@ -278,7 +290,7 @@ impl Window {
};

let builder = WindowBuilder::new()
.with_title(title)
.with_title(&identity.title)
.with_visible(false)
.with_transparent(true)
.with_decorations(window_config.decorations != Decorations::None)
Expand All @@ -289,13 +301,11 @@ impl Window {
let builder = builder.with_window_icon(icon.ok());

#[cfg(feature = "wayland")]
let builder = builder.with_app_id(window_config.class.instance.to_owned());
let builder = builder.with_app_id(identity.class.instance.to_owned());

#[cfg(feature = "x11")]
let builder = builder.with_class(
window_config.class.instance.to_owned(),
window_config.class.general.to_owned(),
);
let builder = builder
.with_class(identity.class.instance.to_owned(), identity.class.general.to_owned());

#[cfg(feature = "x11")]
let builder = match &window_config.gtk_theme_variant {
Expand All @@ -307,11 +317,11 @@ impl Window {
}

#[cfg(windows)]
pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
pub fn get_platform_window(identity: &Identity, window_config: &WindowConfig) -> WindowBuilder {
let icon = glutin::window::Icon::from_resource(IDI_ICON, None);

WindowBuilder::new()
.with_title(title)
.with_title(&identity.title)
.with_visible(false)
.with_decorations(window_config.decorations != Decorations::None)
.with_transparent(true)
Expand All @@ -321,9 +331,9 @@ impl Window {
}

#[cfg(target_os = "macos")]
pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
pub fn get_platform_window(identity: &Identity, window_config: &WindowConfig) -> WindowBuilder {
let window = WindowBuilder::new()
.with_title(title)
.with_title(&identity.title)
.with_visible(false)
.with_transparent(true)
.with_maximized(window_config.maximized())
Expand Down
Loading

0 comments on commit ce59fa4

Please sign in to comment.