From f5add2b77c3f2da48a3b38ab5bfa276374fe917c Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Tue, 15 Oct 2024 18:32:50 +0000 Subject: [PATCH] Add headless mode This patch adds a daemon mode to Alacritty which allows starting the Alacritty process without spawning an initial window. While this does not provide any significant advantage over the existing behavior of always spawning a window, it does integrate nicer with some setups and is a pretty trivial addition. --- CHANGELOG.md | 1 + alacritty/src/cli.rs | 6 +++- alacritty/src/event.rs | 50 ++++++++++++++++++++------------ alacritty/src/ipc.rs | 9 ++++-- alacritty/src/window_context.rs | 17 ++++------- extra/completions/_alacritty | 3 +- extra/completions/alacritty.bash | 2 +- extra/completions/alacritty.fish | 3 +- extra/man/alacritty.1.scd | 4 +++ 9 files changed, 58 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d14aa188..025e56a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its - Support relative path imports from config files - `alacritty migrate` support for TOML configuration changes - Support for Unicode 16 characters +- Headless mode using `alacritty --daemon` ### Changed diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index 2b4afa02..bb0a24f4 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -26,7 +26,7 @@ pub struct Options { pub print_events: bool, /// Generates ref test. - #[clap(long)] + #[clap(long, conflicts_with("daemon"))] pub ref_test: bool, /// X11 window ID to embed Alacritty within (decimal or hexadecimal with "0x" prefix). @@ -62,6 +62,10 @@ pub struct Options { #[clap(short, conflicts_with("quiet"), action = ArgAction::Count)] verbose: u8, + /// Do not spawn an initial window. + #[clap(long)] + pub daemon: bool, + /// CLI options for config overrides. #[clap(skip)] pub config_options: ParsedOptions, diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index 23575e21..f0159060 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -1,6 +1,7 @@ //! Process window events. use crate::ConfigMonitor; +use glutin::config::GetGlConfig; use std::borrow::Cow; use std::cmp::min; use std::collections::{HashMap, HashSet, VecDeque}; @@ -16,7 +17,8 @@ use std::{env, f32, mem}; use ahash::RandomState; use crossfont::Size as FontSize; -use glutin::display::{Display as GlutinDisplay, GetGlDisplay}; +use glutin::config::Config as GlutinConfig; +use glutin::display::GetGlDisplay; use log::{debug, error, info, warn}; use winit::application::ApplicationHandler; use winit::event::{ @@ -80,7 +82,7 @@ pub struct Processor { initial_window_error: Option>, windows: HashMap, proxy: EventLoopProxy, - gl_display: Option, + gl_config: Option, #[cfg(unix)] global_ipc_options: ParsedOptions, cli_options: CliOptions, @@ -121,7 +123,7 @@ impl Processor { cli_options, proxy, scheduler, - gl_display: None, + gl_config: None, config: Rc::new(config), clipboard, windows: Default::default(), @@ -138,12 +140,16 @@ impl Processor { pub fn create_initial_window( &mut self, event_loop: &ActiveEventLoop, - options: WindowOptions, ) -> Result<(), Box> { + let options = match self.initial_window_options.take() { + Some(options) => options, + None => return Ok(()), + }; + let window_context = WindowContext::initial(event_loop, self.proxy.clone(), self.config.clone(), options)?; - self.gl_display = Some(window_context.display.gl_context().display()); + self.gl_config = Some(window_context.display.gl_context().config()); self.windows.insert(window_context.id(), window_context); Ok(()) @@ -155,7 +161,7 @@ impl Processor { event_loop: &ActiveEventLoop, options: WindowOptions, ) -> Result<(), Box> { - let window = self.windows.iter().next().as_ref().unwrap().1; + let gl_config = self.gl_config.as_ref().unwrap(); // Override config with CLI/IPC options. let mut config_overrides = options.config_overrides(); @@ -164,9 +170,14 @@ impl Processor { let mut config = self.config.clone(); config = config_overrides.override_config_rc(config); - #[allow(unused_mut)] - let mut window_context = - window.additional(event_loop, self.proxy.clone(), config, options, config_overrides)?; + let window_context = WindowContext::additional( + gl_config, + event_loop, + self.proxy.clone(), + config, + options, + config_overrides, + )?; self.windows.insert(window_context.id(), window_context); Ok(()) @@ -210,16 +221,11 @@ impl ApplicationHandler for Processor { fn resumed(&mut self, _event_loop: &ActiveEventLoop) {} fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { - if cause != StartCause::Init { + if cause != StartCause::Init || self.cli_options.daemon { return; } - let initial_window_options = match self.initial_window_options.take() { - Some(initial_window_options) => initial_window_options, - None => return, - }; - - if let Err(err) = self.create_initial_window(event_loop, initial_window_options) { + if let Err(err) = self.create_initial_window(event_loop) { self.initial_window_error = Some(err); event_loop.exit(); return; @@ -338,7 +344,13 @@ impl ApplicationHandler for Processor { window_context.display.make_not_current(); } - if let Err(err) = self.create_window(event_loop, options.clone()) { + if self.gl_config.is_none() { + // Handle initial window creation in daemon mode. + if let Err(err) = self.create_initial_window(event_loop) { + self.initial_window_error = Some(err); + event_loop.exit(); + } + } else if let Err(err) = self.create_window(event_loop, options.clone()) { error!("Could not open window: {:?}", err); } }, @@ -375,7 +387,7 @@ impl ApplicationHandler for Processor { self.scheduler.unschedule_window(window_context.id()); // Shutdown if no more terminals are open. - if self.windows.is_empty() { + if self.windows.is_empty() && !self.cli_options.daemon { // Write ref tests of last window to disk. if self.config.debug.ref_test { window_context.write_ref_test_results(); @@ -439,7 +451,7 @@ impl ApplicationHandler for Processor { info!("Exiting the event loop"); } - match self.gl_display.take() { + match self.gl_config.take().map(|config| config.display()) { #[cfg(not(target_os = "macos"))] Some(glutin::display::Display::Egl(display)) => { // Ensure that all the windows are dropped, so the destructors for diff --git a/alacritty/src/ipc.rs b/alacritty/src/ipc.rs index d06d395e..3d14c4ce 100644 --- a/alacritty/src/ipc.rs +++ b/alacritty/src/ipc.rs @@ -20,13 +20,13 @@ const ALACRITTY_SOCKET_ENV: &str = "ALACRITTY_SOCKET"; /// Create an IPC socket. pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy) -> Option { - // Create the IPC socket and export its path as env variable if necessary. + // Create the IPC socket and export its path as env. + let socket_path = options.socket.clone().unwrap_or_else(|| { let mut path = socket_dir(); path.push(format!("{}-{}.sock", socket_prefix(), process::id())); path }); - env::set_var(ALACRITTY_SOCKET_ENV, socket_path.as_os_str()); let listener = match UnixListener::bind(&socket_path) { Ok(listener) => listener, @@ -36,6 +36,11 @@ pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy) - }, }; + env::set_var(ALACRITTY_SOCKET_ENV, socket_path.as_os_str()); + if options.daemon { + println!("ALACRITTY_SOCKET={}; export ALACRITTY_SOCKET", socket_path.display()); + } + // Spawn a thread to listen on the IPC socket. thread::spawn_named("socket listener", move || { let mut data = String::new(); diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs index 062f9ef0..cfc3cd96 100644 --- a/alacritty/src/window_context.rs +++ b/alacritty/src/window_context.rs @@ -9,7 +9,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::Arc; -use glutin::config::GetGlConfig; +use glutin::config::Config as GlutinConfig; use glutin::display::GetGlDisplay; #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] use glutin::platform::x11::X11GlConfigExt; @@ -119,18 +119,14 @@ impl WindowContext { /// Create additional context with the graphics platform other windows are using. pub fn additional( - &self, + gl_config: &GlutinConfig, event_loop: &ActiveEventLoop, proxy: EventLoopProxy, config: Rc, options: WindowOptions, config_overrides: ParsedOptions, ) -> Result> { - // Get any window and take its GL config and display to build a new context. - let (gl_display, gl_config) = { - let gl_context = self.display.gl_context(); - (gl_context.display(), gl_context.config()) - }; + let gl_display = gl_config.display(); let mut identity = config.window.identity.clone(); options.window_identity.override_identity_config(&mut identity); @@ -147,11 +143,8 @@ impl WindowContext { // Create context. let raw_window_handle = window.raw_window_handle(); - let gl_context = renderer::platform::create_gl_context( - &gl_display, - &gl_config, - Some(raw_window_handle), - )?; + let gl_context = + renderer::platform::create_gl_context(&gl_display, gl_config, Some(raw_window_handle))?; // Check if new window will be opened as a tab. #[cfg(target_os = "macos")] diff --git a/extra/completions/_alacritty b/extra/completions/_alacritty index a9260d5c..0ccce66f 100644 --- a/extra/completions/_alacritty +++ b/extra/completions/_alacritty @@ -27,9 +27,10 @@ _alacritty() { '*-o+[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \ '*--option=[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \ '--print-events[Print all events to STDOUT]' \ -'--ref-test[Generates ref test]' \ +'(--daemon)--ref-test[Generates ref test]' \ '(-v)*-q[Reduces the level of verbosity (the min level is -qq)]' \ '(-q)*-v[Increases the level of verbosity (the max level is -vvv)]' \ +'--daemon[Do not spawn an initial window]' \ '--hold[Remain open after child process exit]' \ '-h[Print help]' \ '--help[Print help]' \ diff --git a/extra/completions/alacritty.bash b/extra/completions/alacritty.bash index 2c10d47c..e2213b75 100644 --- a/extra/completions/alacritty.bash +++ b/extra/completions/alacritty.bash @@ -61,7 +61,7 @@ _alacritty() { case "${cmd}" in alacritty) - opts="-q -v -e -T -o -h -V --print-events --ref-test --embed --config-file --socket --working-directory --hold --command --title --class --option --help --version msg migrate help" + opts="-q -v -e -T -o -h -V --print-events --ref-test --embed --config-file --socket --daemon --working-directory --hold --command --title --class --option --help --version msg migrate help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/extra/completions/alacritty.fish b/extra/completions/alacritty.fish index b7fa8ce6..7cfc3337 100644 --- a/extra/completions/alacritty.fish +++ b/extra/completions/alacritty.fish @@ -1,6 +1,6 @@ # Print an optspec for argparse to handle cmd's options that are independent of any subcommand. function __fish_alacritty_global_optspecs - string join \n print-events ref-test embed= config-file= socket= q v working-directory= hold e/command= T/title= class= o/option= h/help V/version + string join \n print-events ref-test embed= config-file= socket= q v daemon working-directory= hold e/command= T/title= class= o/option= h/help V/version end function __fish_alacritty_needs_command @@ -36,6 +36,7 @@ complete -c alacritty -n "__fish_alacritty_needs_command" -l print-events -d 'Pr complete -c alacritty -n "__fish_alacritty_needs_command" -l ref-test -d 'Generates ref test' complete -c alacritty -n "__fish_alacritty_needs_command" -s q -d 'Reduces the level of verbosity (the min level is -qq)' complete -c alacritty -n "__fish_alacritty_needs_command" -s v -d 'Increases the level of verbosity (the max level is -vvv)' +complete -c alacritty -n "__fish_alacritty_needs_command" -l daemon -d 'Do not spawn an initial window' complete -c alacritty -n "__fish_alacritty_needs_command" -l hold -d 'Remain open after child process exit' complete -c alacritty -n "__fish_alacritty_needs_command" -s h -l help -d 'Print help' complete -c alacritty -n "__fish_alacritty_needs_command" -s V -l version -d 'Print version' diff --git a/extra/man/alacritty.1.scd b/extra/man/alacritty.1.scd index c8a67bb8..86630a65 100644 --- a/extra/man/alacritty.1.scd +++ b/extra/man/alacritty.1.scd @@ -21,6 +21,10 @@ set of features with high performance. Remain open after child process exits. +*--daemon* + + Do not spawn an initial window. + *--print-events* Print all events to STDOUT.