aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/main.rs')
-rw-r--r--alacritty/src/main.rs210
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(())
}