diff options
Diffstat (limited to 'alacritty/src/main.rs')
-rw-r--r-- | alacritty/src/main.rs | 210 |
1 files changed, 97 insertions, 113 deletions
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index 488a67bc..74e27b84 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -14,20 +14,16 @@ compile_error!(r#"at least one of the "x11"/"wayland" features must be enabled"# #[cfg(target_os = "macos")] use std::env; -use std::error::Error; -use std::fs; use std::io::{self, Write}; -use std::sync::Arc; +use std::path::PathBuf; +use std::string::ToString; +use std::{fs, process}; use glutin::event_loop::EventLoop as GlutinEventLoop; -use log::{error, info}; +use log::info; #[cfg(windows)] use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS}; -use alacritty_terminal::event_loop::{self, EventLoop, Msg}; -use alacritty_terminal::grid::Dimensions; -use alacritty_terminal::sync::FairMutex; -use alacritty_terminal::term::Term; use alacritty_terminal::tty; mod cli; @@ -37,6 +33,8 @@ mod daemon; mod display; mod event; mod input; +#[cfg(unix)] +mod ipc; mod logging; #[cfg(target_os = "macos")] mod macos; @@ -45,6 +43,7 @@ mod message_bar; mod panic; mod renderer; mod scheduler; +mod window_context; mod gl { #![allow(clippy::all)] @@ -52,12 +51,14 @@ mod gl { } use crate::cli::Options; +#[cfg(unix)] +use crate::cli::{MessageOptions, Subcommands}; use crate::config::{monitor, Config}; -use crate::display::Display; -use crate::event::{Event, EventProxy, Processor}; +use crate::event::{Event, Processor}; +#[cfg(unix)] +use crate::ipc::SOCKET_MESSAGE_CREATE_WINDOW; #[cfg(target_os = "macos")] use crate::macos::locale; -use crate::message_bar::MessageBuffer; fn main() { #[cfg(windows)] @@ -74,151 +75,135 @@ fn main() { // Load command line options. let options = Options::new(); - // Setup glutin event loop. - let window_event_loop = GlutinEventLoop::<Event>::with_user_event(); + #[cfg(unix)] + let result = match options.subcommands { + Some(Subcommands::Msg(options)) => msg(options), + None => alacritty(options), + }; - // Initialize the logger as soon as possible as to capture output from other subsystems. - let log_file = logging::initialize(&options, window_event_loop.create_proxy()) - .expect("Unable to initialize logger"); + #[cfg(not(unix))] + let result = alacritty(options); - // Load configuration file. - let config = config::load(&options); - - // Update the log level from config. - log::set_max_level(config.ui_config.debug.log_level); + // Handle command failure. + if let Err(err) = result { + eprintln!("Error: {}", err); + process::exit(1); + } +} - // Switch to home directory. - #[cfg(target_os = "macos")] - env::set_current_dir(dirs::home_dir().unwrap()).unwrap(); - // Set locale. - #[cfg(target_os = "macos")] - locale::set_locale_environment(); +/// `msg` subcommand entrypoint. +#[cfg(unix)] +fn msg(options: MessageOptions) -> Result<(), String> { + ipc::send_message(options.socket, &SOCKET_MESSAGE_CREATE_WINDOW).map_err(|err| err.to_string()) +} - // Store if log file should be deleted before moving config. - let persistent_logging = config.ui_config.debug.persistent_logging; +/// Temporary files stored for Alacritty. +/// +/// This stores temporary files to automate their destruction through its `Drop` implementation. +struct TemporaryFiles { + #[cfg(unix)] + socket_path: Option<PathBuf>, + log_file: Option<PathBuf>, +} - // Run Alacritty. - if let Err(err) = run(window_event_loop, config, options) { - error!("Alacritty encountered an unrecoverable error:\n\n\t{}\n", err); - std::process::exit(1); - } +impl Drop for TemporaryFiles { + fn drop(&mut self) { + // Clean up the IPC socket file. + #[cfg(unix)] + if let Some(socket_path) = &self.socket_path { + let _ = fs::remove_file(socket_path); + } - // Clean up logfile. - if let Some(log_file) = log_file { - if !persistent_logging && fs::remove_file(&log_file).is_ok() { - let _ = writeln!(io::stdout(), "Deleted log file at \"{}\"", log_file.display()); + // Clean up logfile. + if let Some(log_file) = &self.log_file { + if fs::remove_file(log_file).is_ok() { + let _ = writeln!(io::stdout(), "Deleted log file at \"{}\"", log_file.display()); + } } } } -/// Run Alacritty. +/// Run main Alacritty entrypoint. /// /// Creates a window, the terminal state, PTY, I/O event loop, input processor, /// config change monitor, and runs the main display loop. -fn run( - window_event_loop: GlutinEventLoop<Event>, - config: Config, - options: Options, -) -> Result<(), Box<dyn Error>> { +fn alacritty(options: Options) -> Result<(), String> { info!("Welcome to Alacritty"); - // Log the configuration paths. - log_config_path(&config); - - // Set environment variables. - tty::setup_env(&config); + // Setup glutin event loop. + let window_event_loop = GlutinEventLoop::<Event>::with_user_event(); - let event_proxy = EventProxy::new(window_event_loop.create_proxy()); + // Initialize the logger as soon as possible as to capture output from other subsystems. + let log_file = logging::initialize(&options, window_event_loop.create_proxy()) + .expect("Unable to initialize logger"); - // Create a display. - // - // The display manages a window and can draw the terminal. - let display = Display::new(&config, &window_event_loop)?; + // Load configuration file. + let config = config::load(&options); + log_config_path(&config); - info!( - "PTY dimensions: {:?} x {:?}", - display.size_info.screen_lines(), - display.size_info.columns() - ); + // Update the log level from config. + log::set_max_level(config.ui_config.debug.log_level); - // Create the terminal. - // - // This object contains all of the state about what's being displayed. It's - // wrapped in a clonable mutex since both the I/O loop and display need to - // access it. - let terminal = Term::new(&config, display.size_info, event_proxy.clone()); - let terminal = Arc::new(FairMutex::new(terminal)); + // Set environment variables. + tty::setup_env(&config); - // Create the PTY. - // - // The PTY forks a process to run the shell on the slave side of the - // pseudoterminal. A file descriptor for the master side is retained for - // reading/writing to the shell. - let pty = tty::new(&config, &display.size_info, display.window.x11_window_id()); + // Switch to home directory. + #[cfg(target_os = "macos")] + env::set_current_dir(dirs::home_dir().unwrap()).unwrap(); - // Create the pseudoterminal I/O loop. - // - // PTY I/O is ran on another thread as to not occupy cycles used by the - // renderer and input processing. Note that access to the terminal state is - // synchronized since the I/O loop updates the state, and the display - // consumes it periodically. - let event_loop = EventLoop::new( - Arc::clone(&terminal), - event_proxy.clone(), - pty, - config.hold, - config.ui_config.debug.ref_test, - ); - - // The event loop channel allows write requests from the event processor - // to be sent to the pty loop and ultimately written to the pty. - let loop_tx = event_loop.channel(); + // Set macOS locale. + #[cfg(target_os = "macos")] + locale::set_locale_environment(); // Create a config monitor when config was loaded from path. // // The monitor watches the config file for changes and reloads it. Pending // config changes are processed in the main loop. if config.ui_config.live_config_reload { - monitor::watch(config.ui_config.config_paths.clone(), event_proxy); + monitor::watch(config.ui_config.config_paths.clone(), window_event_loop.create_proxy()); } - // Setup storage for message UI. - let message_buffer = MessageBuffer::new(); + // Create the IPC socket listener. + #[cfg(unix)] + let socket_path = if config.ui_config.ipc_socket { + ipc::spawn_ipc_socket(&options, window_event_loop.create_proxy()) + } else { + None + }; + + // Setup automatic RAII cleanup for our files. + let log_cleanup = log_file.filter(|_| !config.ui_config.debug.persistent_logging); + let _files = TemporaryFiles { + #[cfg(unix)] + socket_path, + log_file: log_cleanup, + }; // Event processor. - let mut processor = Processor::new( - event_loop::Notifier(loop_tx.clone()), - message_buffer, - config, - display, - options, - ); + let mut processor = Processor::new(config, options, &window_event_loop); - // Kick off the I/O thread. - let io_thread = event_loop.spawn(); + // Create the first Alacritty window. + let proxy = window_event_loop.create_proxy(); + processor.create_window(&window_event_loop, proxy).map_err(|err| err.to_string())?; info!("Initialisation complete"); // Start event loop and block until shutdown. - processor.run(terminal, window_event_loop); + processor.run(window_event_loop); // This explicit drop is needed for Windows, ConPTY backend. Otherwise a deadlock can occur. // The cause: - // - Drop for ConPTY will deadlock if the conout pipe has already been dropped. - // - The conout pipe is dropped when the io_thread is joined below (io_thread owns PTY). - // - ConPTY is dropped when the last of processor and io_thread are dropped, because both of - // them own an Arc<ConPTY>. + // - Drop for ConPTY will deadlock if the conout pipe has already been dropped + // - ConPTY is dropped when the last of processor and window context are dropped, because both + // of them own an Arc<ConPTY> // - // The fix is to ensure that processor is dropped first. That way, when io_thread (i.e. PTY) - // is dropped, it can ensure ConPTY is dropped before the conout pipe in the PTY drop order. + // The fix is to ensure that processor is dropped first. That way, when window context (i.e. + // PTY) is dropped, it can ensure ConPTY is dropped before the conout pipe in the PTY drop + // order. // // FIXME: Change PTY API to enforce the correct drop order with the typesystem. drop(processor); - // Shutdown PTY parser event loop. - loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to PTY event loop"); - io_thread.join().expect("join io thread"); - // FIXME patch notify library to have a shutdown method. // config_reloader.join().ok(); @@ -229,7 +214,6 @@ fn run( } info!("Goodbye"); - Ok(()) } |