mirror of
https://github.com/alacritty/alacritty.git
synced 2025-04-14 17:53:03 -04:00
Add parameters to msg create-window
subcommand
Alacritty's `msg create-window` subcommand would previously inherit all the CLI parameters from the original executable. However not only could this lead to unexpected behavior, it also prevents multi-window users from making use of parameters like `-e`, `--working-directory`, or `--hold`. This is solved by adding a JSON-based message format to the IPC socket messages which instructs the Alacritty server on which CLI parameters should be used to create the new window. Fixes #5562. Fixes #5561. Fixes #5560.
This commit is contained in:
parent
c89939b5d1
commit
8681f71084
28 changed files with 474 additions and 357 deletions
|
@ -2,13 +2,14 @@ use std::cmp::max;
|
|||
use std::path::PathBuf;
|
||||
|
||||
use log::{self, error, LevelFilter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::Value;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use alacritty_terminal::config::Program;
|
||||
use alacritty_terminal::config::{Program, PtyConfig};
|
||||
|
||||
use crate::config::window::{Class, DEFAULT_NAME};
|
||||
use crate::config::{serde_utils, Config};
|
||||
use crate::config::{serde_utils, UiConfig};
|
||||
|
||||
/// CLI options for the main Alacritty executable.
|
||||
#[derive(StructOpt, Debug)]
|
||||
|
@ -34,10 +35,6 @@ pub struct Options {
|
|||
#[structopt(long)]
|
||||
pub embed: Option<String>,
|
||||
|
||||
/// Start the shell in the specified working directory.
|
||||
#[structopt(long)]
|
||||
pub working_directory: Option<PathBuf>,
|
||||
|
||||
/// Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml].
|
||||
#[cfg(not(any(target_os = "macos", windows)))]
|
||||
#[structopt(long)]
|
||||
|
@ -53,10 +50,6 @@ pub struct Options {
|
|||
#[structopt(long)]
|
||||
pub config_file: Option<PathBuf>,
|
||||
|
||||
/// Remain open after child process exits.
|
||||
#[structopt(long)]
|
||||
pub hold: bool,
|
||||
|
||||
/// Path for IPC socket creation.
|
||||
#[cfg(unix)]
|
||||
#[structopt(long)]
|
||||
|
@ -70,10 +63,6 @@ pub struct Options {
|
|||
#[structopt(short, conflicts_with("quiet"), parse(from_occurrences))]
|
||||
verbose: u8,
|
||||
|
||||
/// Command and args to execute (must be last argument).
|
||||
#[structopt(short = "e", long, allow_hyphen_values = true)]
|
||||
command: Vec<String>,
|
||||
|
||||
/// Override configuration file options [example: cursor.style=Beam].
|
||||
#[structopt(short = "o", long)]
|
||||
option: Vec<String>,
|
||||
|
@ -82,6 +71,10 @@ pub struct Options {
|
|||
#[structopt(skip)]
|
||||
pub config_options: Value,
|
||||
|
||||
/// Terminal options which could be passed via IPC.
|
||||
#[structopt(flatten)]
|
||||
pub terminal_options: TerminalOptions,
|
||||
|
||||
/// Subcommand passed to the CLI.
|
||||
#[cfg(unix)]
|
||||
#[structopt(subcommand)]
|
||||
|
@ -106,42 +99,42 @@ impl Options {
|
|||
}
|
||||
|
||||
/// Override configuration file with options from the CLI.
|
||||
pub fn override_config(&self, config: &mut Config) {
|
||||
if let Some(working_directory) = &self.working_directory {
|
||||
pub fn override_config(&self, config: &mut UiConfig) {
|
||||
if let Some(working_directory) = &self.terminal_options.working_directory {
|
||||
if working_directory.is_dir() {
|
||||
config.working_directory = Some(working_directory.to_owned());
|
||||
config.terminal_config.pty_config.working_directory =
|
||||
Some(working_directory.to_owned());
|
||||
} else {
|
||||
error!("Invalid working directory: {:?}", working_directory);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(command) = self.command() {
|
||||
config.shell = Some(command);
|
||||
if let Some(command) = self.terminal_options.command() {
|
||||
config.terminal_config.pty_config.shell = Some(command);
|
||||
}
|
||||
|
||||
config.hold = self.hold;
|
||||
config.terminal_config.pty_config.hold = self.terminal_options.hold;
|
||||
|
||||
if let Some(title) = self.title.clone() {
|
||||
config.ui_config.window.title = title
|
||||
config.window.title = title
|
||||
}
|
||||
if let Some(class) = &self.class {
|
||||
config.ui_config.window.class = class.clone();
|
||||
config.window.class = class.clone();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
config.ui_config.ipc_socket |= self.socket.is_some();
|
||||
config.ipc_socket |= self.socket.is_some();
|
||||
}
|
||||
|
||||
config.ui_config.window.dynamic_title &= self.title.is_none();
|
||||
config.ui_config.window.embed = self.embed.as_ref().and_then(|embed| embed.parse().ok());
|
||||
config.ui_config.debug.print_events |= self.print_events;
|
||||
config.ui_config.debug.log_level = max(config.ui_config.debug.log_level, self.log_level());
|
||||
config.ui_config.debug.ref_test |= self.ref_test;
|
||||
config.window.dynamic_title &= self.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());
|
||||
config.debug.ref_test |= self.ref_test;
|
||||
|
||||
if config.ui_config.debug.print_events {
|
||||
config.ui_config.debug.log_level =
|
||||
max(config.ui_config.debug.log_level, LevelFilter::Info);
|
||||
if config.debug.print_events {
|
||||
config.debug.log_level = max(config.debug.log_level, LevelFilter::Info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,12 +157,6 @@ impl Options {
|
|||
(..) => LevelFilter::Off,
|
||||
}
|
||||
}
|
||||
|
||||
/// Shell override passed through the CLI.
|
||||
pub fn command(&self) -> Option<Program> {
|
||||
let (program, args) = self.command.split_first()?;
|
||||
Some(Program::WithArgs { program: program.clone(), args: args.to_vec() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Format an option in the format of `parent.field=value` to a serde Value.
|
||||
|
@ -214,6 +201,47 @@ fn parse_class(input: &str) -> Result<Class, String> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Terminal specific cli options which can be passed to new windows via IPC.
|
||||
#[derive(Serialize, Deserialize, StructOpt, Default, Debug, Clone, PartialEq)]
|
||||
pub struct TerminalOptions {
|
||||
/// Start the shell in the specified working directory.
|
||||
#[structopt(long)]
|
||||
pub working_directory: Option<PathBuf>,
|
||||
|
||||
/// Remain open after child process exit.
|
||||
#[structopt(long)]
|
||||
pub hold: bool,
|
||||
|
||||
/// Command and args to execute (must be last argument).
|
||||
#[structopt(short = "e", long, allow_hyphen_values = true)]
|
||||
command: Vec<String>,
|
||||
}
|
||||
|
||||
impl TerminalOptions {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.working_directory.is_none() && !self.hold && self.command.is_empty()
|
||||
}
|
||||
|
||||
/// Shell override passed through the CLI.
|
||||
pub fn command(&self) -> Option<Program> {
|
||||
let (program, args) = self.command.split_first()?;
|
||||
Some(Program::WithArgs { program: program.clone(), args: args.to_vec() })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TerminalOptions> for PtyConfig {
|
||||
fn from(mut options: TerminalOptions) -> Self {
|
||||
let working_directory = options.working_directory.take();
|
||||
let shell = options.command();
|
||||
let hold = options.hold;
|
||||
PtyConfig { hold, shell, working_directory }
|
||||
}
|
||||
}
|
||||
|
||||
/// Available CLI subcommands.
|
||||
#[cfg(unix)]
|
||||
#[derive(StructOpt, Debug)]
|
||||
|
@ -236,10 +264,10 @@ pub struct MessageOptions {
|
|||
|
||||
/// Available socket messages.
|
||||
#[cfg(unix)]
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[derive(StructOpt, Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub enum SocketMessage {
|
||||
/// Create a new window in the same Alacritty process.
|
||||
CreateWindow,
|
||||
CreateWindow(TerminalOptions),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -257,32 +285,32 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn dynamic_title_ignoring_options_by_default() {
|
||||
let mut config = Config::default();
|
||||
let old_dynamic_title = config.ui_config.window.dynamic_title;
|
||||
let mut config = UiConfig::default();
|
||||
let old_dynamic_title = config.window.dynamic_title;
|
||||
|
||||
Options::new().override_config(&mut config);
|
||||
|
||||
assert_eq!(old_dynamic_title, config.ui_config.window.dynamic_title);
|
||||
assert_eq!(old_dynamic_title, config.window.dynamic_title);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_title_overridden_by_options() {
|
||||
let mut config = Config::default();
|
||||
let mut config = UiConfig::default();
|
||||
|
||||
let options = Options { title: Some("foo".to_owned()), ..Options::new() };
|
||||
options.override_config(&mut config);
|
||||
|
||||
assert!(!config.ui_config.window.dynamic_title);
|
||||
assert!(!config.window.dynamic_title);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_title_not_overridden_by_config() {
|
||||
let mut config = Config::default();
|
||||
let mut config = UiConfig::default();
|
||||
|
||||
config.ui_config.window.title = "foo".to_owned();
|
||||
config.window.title = "foo".to_owned();
|
||||
Options::new().override_config(&mut config);
|
||||
|
||||
assert!(config.ui_config.window.dynamic_title);
|
||||
assert!(config.window.dynamic_title);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -7,7 +7,7 @@ use serde::Deserialize;
|
|||
use serde_yaml::mapping::Mapping;
|
||||
use serde_yaml::Value;
|
||||
|
||||
use alacritty_terminal::config::{Config as TermConfig, LOG_TARGET_CONFIG};
|
||||
use alacritty_terminal::config::LOG_TARGET_CONFIG;
|
||||
|
||||
pub mod bell;
|
||||
pub mod color;
|
||||
|
@ -27,13 +27,11 @@ pub use crate::config::bindings::{
|
|||
};
|
||||
#[cfg(test)]
|
||||
pub use crate::config::mouse::{ClickHandler, Mouse};
|
||||
use crate::config::ui_config::UiConfig;
|
||||
pub use crate::config::ui_config::UiConfig;
|
||||
|
||||
/// Maximum number of depth for the configuration file imports.
|
||||
const IMPORT_RECURSION_LIMIT: usize = 5;
|
||||
|
||||
pub type Config = TermConfig<UiConfig>;
|
||||
|
||||
/// Result from config loading.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
|
@ -100,7 +98,7 @@ impl From<serde_yaml::Error> for Error {
|
|||
}
|
||||
|
||||
/// Load the configuration file.
|
||||
pub fn load(options: &Options) -> Config {
|
||||
pub fn load(options: &Options) -> UiConfig {
|
||||
let config_options = options.config_options.clone();
|
||||
let config_path = options.config_file.clone().or_else(installed_config);
|
||||
|
||||
|
@ -112,9 +110,9 @@ pub fn load(options: &Options) -> Config {
|
|||
.as_ref()
|
||||
.and_then(|config_path| load_from(config_path, config_options.clone()).ok())
|
||||
.unwrap_or_else(|| {
|
||||
let mut config = Config::deserialize(config_options).unwrap_or_default();
|
||||
let mut config = UiConfig::deserialize(config_options).unwrap_or_default();
|
||||
match config_path {
|
||||
Some(config_path) => config.ui_config.config_paths.push(config_path),
|
||||
Some(config_path) => config.config_paths.push(config_path),
|
||||
None => info!(target: LOG_TARGET_CONFIG, "No config file found; using default"),
|
||||
}
|
||||
config
|
||||
|
@ -126,7 +124,7 @@ pub fn load(options: &Options) -> Config {
|
|||
}
|
||||
|
||||
/// Attempt to reload the configuration file.
|
||||
pub fn reload(config_path: &Path, options: &Options) -> Result<Config> {
|
||||
pub fn reload(config_path: &Path, options: &Options) -> Result<UiConfig> {
|
||||
// Load config, propagating errors.
|
||||
let config_options = options.config_options.clone();
|
||||
let mut config = load_from(config_path, config_options)?;
|
||||
|
@ -136,17 +134,17 @@ pub fn reload(config_path: &Path, options: &Options) -> Result<Config> {
|
|||
Ok(config)
|
||||
}
|
||||
|
||||
/// Modifications after the `Config` object is created.
|
||||
fn after_loading(config: &mut Config, options: &Options) {
|
||||
/// Modifications after the `UiConfig` object is created.
|
||||
fn after_loading(config: &mut UiConfig, options: &Options) {
|
||||
// Override config with CLI options.
|
||||
options.override_config(config);
|
||||
|
||||
// Create key bindings for regex hints.
|
||||
config.ui_config.generate_hint_bindings();
|
||||
config.generate_hint_bindings();
|
||||
}
|
||||
|
||||
/// Load configuration file and log errors.
|
||||
fn load_from(path: &Path, cli_config: Value) -> Result<Config> {
|
||||
fn load_from(path: &Path, cli_config: Value) -> Result<UiConfig> {
|
||||
match read_config(path, cli_config) {
|
||||
Ok(config) => Ok(config),
|
||||
Err(err) => {
|
||||
|
@ -157,7 +155,7 @@ fn load_from(path: &Path, cli_config: Value) -> Result<Config> {
|
|||
}
|
||||
|
||||
/// Deserialize configuration file from path.
|
||||
fn read_config(path: &Path, cli_config: Value) -> Result<Config> {
|
||||
fn read_config(path: &Path, cli_config: Value) -> Result<UiConfig> {
|
||||
let mut config_paths = Vec::new();
|
||||
let mut config_value = parse_config(path, &mut config_paths, IMPORT_RECURSION_LIMIT)?;
|
||||
|
||||
|
@ -165,8 +163,8 @@ fn read_config(path: &Path, cli_config: Value) -> Result<Config> {
|
|||
config_value = serde_utils::merge(config_value, cli_config);
|
||||
|
||||
// Deserialize to concrete type.
|
||||
let mut config = Config::deserialize(config_value)?;
|
||||
config.ui_config.config_paths = config_paths;
|
||||
let mut config = UiConfig::deserialize(config_value)?;
|
||||
config.config_paths = config_paths;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
@ -307,7 +305,7 @@ mod tests {
|
|||
fn config_read_eof() {
|
||||
let config_path: PathBuf = DEFAULT_ALACRITTY_CONFIG.into();
|
||||
let mut config = read_config(&config_path, Value::Null).unwrap();
|
||||
config.ui_config.config_paths = Vec::new();
|
||||
assert_eq!(config, Config::default());
|
||||
config.config_paths = Vec::new();
|
||||
assert_eq!(config, UiConfig::default());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ use serde::{self, Deserialize, Deserializer};
|
|||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use alacritty_config_derive::ConfigDeserialize;
|
||||
use alacritty_terminal::config::{Percentage, Program, LOG_TARGET_CONFIG};
|
||||
use alacritty_terminal::config::{
|
||||
Config as TerminalConfig, Percentage, Program, LOG_TARGET_CONFIG,
|
||||
};
|
||||
use alacritty_terminal::term::search::RegexSearch;
|
||||
|
||||
use crate::config::bell::BellConfig;
|
||||
|
@ -66,6 +68,10 @@ pub struct UiConfig {
|
|||
#[cfg(unix)]
|
||||
pub ipc_socket: bool,
|
||||
|
||||
/// Config for the alacritty_terminal itself.
|
||||
#[config(flatten)]
|
||||
pub terminal_config: TerminalConfig,
|
||||
|
||||
/// Keybindings.
|
||||
key_bindings: KeyBindings,
|
||||
|
||||
|
@ -91,6 +97,7 @@ impl Default for UiConfig {
|
|||
config_paths: Default::default(),
|
||||
key_bindings: Default::default(),
|
||||
mouse_bindings: Default::default(),
|
||||
terminal_config: Default::default(),
|
||||
background_opacity: Default::default(),
|
||||
bell: Default::default(),
|
||||
colors: Default::default(),
|
||||
|
|
|
@ -8,7 +8,6 @@ use std::os::windows::process::CommandExt;
|
|||
use std::process::{Command, Stdio};
|
||||
|
||||
use log::{debug, warn};
|
||||
|
||||
#[cfg(windows)]
|
||||
use winapi::um::winbase::{CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW};
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ use std::mem;
|
|||
use std::ops::{Deref, DerefMut, RangeInclusive};
|
||||
|
||||
use alacritty_terminal::ansi::{Color, CursorShape, NamedColor};
|
||||
use alacritty_terminal::config::Config;
|
||||
use alacritty_terminal::event::EventListener;
|
||||
use alacritty_terminal::grid::{Dimensions, Indexed};
|
||||
use alacritty_terminal::index::{Column, Direction, Line, Point};
|
||||
|
@ -13,7 +12,7 @@ use alacritty_terminal::term::color::{CellRgb, Rgb};
|
|||
use alacritty_terminal::term::search::{Match, RegexIter, RegexSearch};
|
||||
use alacritty_terminal::term::{RenderableContent as TerminalContent, Term, TermMode};
|
||||
|
||||
use crate::config::ui_config::UiConfig;
|
||||
use crate::config::UiConfig;
|
||||
use crate::display::color::{List, DIM_FACTOR};
|
||||
use crate::display::hint::HintState;
|
||||
use crate::display::{self, Display, MAX_SEARCH_LINES};
|
||||
|
@ -32,14 +31,14 @@ pub struct RenderableContent<'a> {
|
|||
cursor_point: Point<usize>,
|
||||
search: Option<Regex<'a>>,
|
||||
hint: Option<Hint<'a>>,
|
||||
config: &'a Config<UiConfig>,
|
||||
config: &'a UiConfig,
|
||||
colors: &'a List,
|
||||
focused_match: Option<&'a Match>,
|
||||
}
|
||||
|
||||
impl<'a> RenderableContent<'a> {
|
||||
pub fn new<T: EventListener>(
|
||||
config: &'a Config<UiConfig>,
|
||||
config: &'a UiConfig,
|
||||
display: &'a mut Display,
|
||||
term: &'a Term<T>,
|
||||
search_state: &'a SearchState,
|
||||
|
@ -54,7 +53,7 @@ impl<'a> RenderableContent<'a> {
|
|||
|| search_state.regex().is_some()
|
||||
{
|
||||
CursorShape::Hidden
|
||||
} else if !term.is_focused && config.cursor.unfocused_hollow {
|
||||
} else if !term.is_focused && config.terminal_config.cursor.unfocused_hollow {
|
||||
CursorShape::HollowBlock
|
||||
} else {
|
||||
terminal_content.cursor.shape
|
||||
|
@ -113,9 +112,9 @@ impl<'a> RenderableContent<'a> {
|
|||
|
||||
// Cursor colors.
|
||||
let color = if self.terminal_content.mode.contains(TermMode::VI) {
|
||||
self.config.ui_config.colors.vi_mode_cursor
|
||||
self.config.colors.vi_mode_cursor
|
||||
} else {
|
||||
self.config.ui_config.colors.cursor
|
||||
self.config.colors.cursor
|
||||
};
|
||||
let cursor_color =
|
||||
self.terminal_content.colors[NamedColor::Cursor].map_or(color.background, CellRgb::Rgb);
|
||||
|
@ -131,8 +130,8 @@ impl<'a> RenderableContent<'a> {
|
|||
|
||||
// Invert cursor color with insufficient contrast to prevent invisible cursors.
|
||||
if insufficient_contrast {
|
||||
cursor_color = self.config.ui_config.colors.primary.foreground;
|
||||
text_color = self.config.ui_config.colors.primary.background;
|
||||
cursor_color = self.config.colors.primary.foreground;
|
||||
text_color = self.config.colors.primary.background;
|
||||
}
|
||||
|
||||
Some(RenderableCursor {
|
||||
|
@ -204,7 +203,7 @@ impl RenderableCell {
|
|||
mem::swap(&mut fg, &mut bg);
|
||||
1.0
|
||||
} else {
|
||||
Self::compute_bg_alpha(&content.config.ui_config, cell.bg)
|
||||
Self::compute_bg_alpha(content.config, cell.bg)
|
||||
};
|
||||
|
||||
let is_selected = content.terminal_content.selection.map_or(false, |selection| {
|
||||
|
@ -217,7 +216,7 @@ impl RenderableCell {
|
|||
|
||||
let display_offset = content.terminal_content.display_offset;
|
||||
let viewport_start = Point::new(Line(-(display_offset as i32)), Column(0));
|
||||
let colors = &content.config.ui_config.colors;
|
||||
let colors = &content.config.colors;
|
||||
let mut character = cell.c;
|
||||
|
||||
if let Some((c, is_first)) =
|
||||
|
@ -293,18 +292,18 @@ impl RenderableCell {
|
|||
|
||||
/// Get the RGB color from a cell's foreground color.
|
||||
fn compute_fg_rgb(content: &mut RenderableContent<'_>, fg: Color, flags: Flags) -> Rgb {
|
||||
let ui_config = &content.config.ui_config;
|
||||
let config = &content.config;
|
||||
match fg {
|
||||
Color::Spec(rgb) => match flags & Flags::DIM {
|
||||
Flags::DIM => rgb * DIM_FACTOR,
|
||||
_ => rgb,
|
||||
},
|
||||
Color::Named(ansi) => {
|
||||
match (ui_config.draw_bold_text_with_bright_colors, flags & Flags::DIM_BOLD) {
|
||||
match (config.draw_bold_text_with_bright_colors, flags & Flags::DIM_BOLD) {
|
||||
// If no bright foreground is set, treat it like the BOLD flag doesn't exist.
|
||||
(_, Flags::DIM_BOLD)
|
||||
if ansi == NamedColor::Foreground
|
||||
&& ui_config.colors.primary.bright_foreground.is_none() =>
|
||||
&& config.colors.primary.bright_foreground.is_none() =>
|
||||
{
|
||||
content.color(NamedColor::DimForeground as usize)
|
||||
},
|
||||
|
@ -320,7 +319,7 @@ impl RenderableCell {
|
|||
},
|
||||
Color::Indexed(idx) => {
|
||||
let idx = match (
|
||||
ui_config.draw_bold_text_with_bright_colors,
|
||||
config.draw_bold_text_with_bright_colors,
|
||||
flags & Flags::DIM_BOLD,
|
||||
idx,
|
||||
) {
|
||||
|
|
|
@ -8,7 +8,7 @@ use alacritty_terminal::term::search::{Match, RegexIter, RegexSearch};
|
|||
use alacritty_terminal::term::{Term, TermMode};
|
||||
|
||||
use crate::config::ui_config::{Hint, HintAction};
|
||||
use crate::config::Config;
|
||||
use crate::config::UiConfig;
|
||||
use crate::display::content::RegexMatches;
|
||||
use crate::display::MAX_SEARCH_LINES;
|
||||
|
||||
|
@ -242,13 +242,13 @@ impl HintLabels {
|
|||
/// Check if there is a hint highlighted at the specified point.
|
||||
pub fn highlighted_at<T>(
|
||||
term: &Term<T>,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
point: Point,
|
||||
mouse_mods: ModifiersState,
|
||||
) -> Option<HintMatch> {
|
||||
let mouse_mode = term.mode().intersects(TermMode::MOUSE_MODE);
|
||||
|
||||
config.ui_config.hints.enabled.iter().find_map(|hint| {
|
||||
config.hints.enabled.iter().find_map(|hint| {
|
||||
// Check if all required modifiers are pressed.
|
||||
let highlight = hint.mouse.map_or(false, |mouse| {
|
||||
mouse.enabled
|
||||
|
|
|
@ -35,7 +35,7 @@ use crate::config::font::Font;
|
|||
use crate::config::window::Dimensions;
|
||||
#[cfg(not(windows))]
|
||||
use crate::config::window::StartupMode;
|
||||
use crate::config::Config;
|
||||
use crate::config::UiConfig;
|
||||
use crate::display::bell::VisualBell;
|
||||
use crate::display::color::List;
|
||||
use crate::display::content::RenderableContent;
|
||||
|
@ -202,7 +202,7 @@ pub struct Display {
|
|||
|
||||
impl Display {
|
||||
pub fn new<E>(
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
event_loop: &EventLoopWindowTarget<E>,
|
||||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
wayland_event_queue: Option<&EventQueue>,
|
||||
|
@ -221,11 +221,11 @@ impl Display {
|
|||
};
|
||||
|
||||
// Guess the target window dimensions.
|
||||
let metrics = GlyphCache::static_metrics(config.ui_config.font.clone(), estimated_dpr)?;
|
||||
let metrics = GlyphCache::static_metrics(config.font.clone(), estimated_dpr)?;
|
||||
let (cell_width, cell_height) = compute_cell_size(config, &metrics);
|
||||
|
||||
// Guess the target window size if the user has specified the number of lines/columns.
|
||||
let dimensions = config.ui_config.window.dimensions();
|
||||
let dimensions = config.window.dimensions();
|
||||
let estimated_size = dimensions.map(|dimensions| {
|
||||
window_size(config, dimensions, cell_width, cell_height, estimated_dpr)
|
||||
});
|
||||
|
@ -261,7 +261,7 @@ impl Display {
|
|||
}
|
||||
}
|
||||
|
||||
let padding = config.ui_config.window.padding(window.dpr);
|
||||
let padding = config.window.padding(window.dpr);
|
||||
let viewport_size = window.inner_size();
|
||||
|
||||
// Create new size with at least one column and row.
|
||||
|
@ -272,7 +272,7 @@ impl Display {
|
|||
cell_height,
|
||||
padding.0,
|
||||
padding.1,
|
||||
config.ui_config.window.dynamic_padding && dimensions.is_none(),
|
||||
config.window.dynamic_padding && dimensions.is_none(),
|
||||
);
|
||||
|
||||
info!("Cell size: {} x {}", cell_width, cell_height);
|
||||
|
@ -283,25 +283,25 @@ impl Display {
|
|||
renderer.resize(&size_info);
|
||||
|
||||
// Clear screen.
|
||||
let background_color = config.ui_config.colors.primary.background;
|
||||
renderer.with_api(&config.ui_config, &size_info, |api| {
|
||||
let background_color = config.colors.primary.background;
|
||||
renderer.with_api(config, &size_info, |api| {
|
||||
api.clear(background_color);
|
||||
});
|
||||
|
||||
// Set subpixel anti-aliasing.
|
||||
#[cfg(target_os = "macos")]
|
||||
crossfont::set_font_smoothing(config.ui_config.font.use_thin_strokes);
|
||||
crossfont::set_font_smoothing(config.font.use_thin_strokes);
|
||||
|
||||
// Disable shadows for transparent windows on macOS.
|
||||
#[cfg(target_os = "macos")]
|
||||
window.set_has_shadow(config.ui_config.window_opacity() >= 1.0);
|
||||
window.set_has_shadow(config.window_opacity() >= 1.0);
|
||||
|
||||
// On Wayland we can safely ignore this call, since the window isn't visible until you
|
||||
// actually draw something into it and commit those changes.
|
||||
#[cfg(not(any(target_os = "macos", windows)))]
|
||||
if is_x11 {
|
||||
window.swap_buffers();
|
||||
renderer.with_api(&config.ui_config, &size_info, |api| {
|
||||
renderer.with_api(config, &size_info, |api| {
|
||||
api.finish();
|
||||
});
|
||||
}
|
||||
|
@ -312,13 +312,13 @@ impl Display {
|
|||
//
|
||||
// TODO: replace `set_position` with `with_position` once available.
|
||||
// Upstream issue: https://github.com/rust-windowing/winit/issues/806.
|
||||
if let Some(position) = config.ui_config.window.position {
|
||||
if let Some(position) = config.window.position {
|
||||
window.set_outer_position(PhysicalPosition::from((position.x, position.y)));
|
||||
}
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
#[cfg(not(windows))]
|
||||
match config.ui_config.window.startup_mode {
|
||||
match config.window.startup_mode {
|
||||
#[cfg(target_os = "macos")]
|
||||
StartupMode::SimpleFullscreen => window.set_simple_fullscreen(true),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
|
@ -326,7 +326,7 @@ impl Display {
|
|||
_ => (),
|
||||
}
|
||||
|
||||
let hint_state = HintState::new(config.ui_config.hints.alphabet());
|
||||
let hint_state = HintState::new(config.hints.alphabet());
|
||||
|
||||
Ok(Self {
|
||||
window,
|
||||
|
@ -340,8 +340,8 @@ impl Display {
|
|||
#[cfg(not(any(target_os = "macos", windows)))]
|
||||
is_x11,
|
||||
cursor_hidden: false,
|
||||
visual_bell: VisualBell::from(&config.ui_config.bell),
|
||||
colors: List::from(&config.ui_config.colors),
|
||||
visual_bell: VisualBell::from(&config.bell),
|
||||
colors: List::from(&config.colors),
|
||||
pending_update: Default::default(),
|
||||
})
|
||||
}
|
||||
|
@ -349,10 +349,10 @@ impl Display {
|
|||
fn new_glyph_cache(
|
||||
dpr: f64,
|
||||
renderer: &mut QuadRenderer,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
) -> Result<(GlyphCache, f32, f32), Error> {
|
||||
let font = config.ui_config.font.clone();
|
||||
let rasterizer = Rasterizer::new(dpr as f32, config.ui_config.font.use_thin_strokes)?;
|
||||
let font = config.font.clone();
|
||||
let rasterizer = Rasterizer::new(dpr as f32, config.font.use_thin_strokes)?;
|
||||
|
||||
// Initialize glyph cache.
|
||||
let glyph_cache = {
|
||||
|
@ -380,7 +380,7 @@ impl Display {
|
|||
/// Update font size and cell dimensions.
|
||||
///
|
||||
/// This will return a tuple of the cell width and height.
|
||||
fn update_glyph_cache(&mut self, config: &Config, font: &Font) -> (f32, f32) {
|
||||
fn update_glyph_cache(&mut self, config: &UiConfig, font: &Font) -> (f32, f32) {
|
||||
let cache = &mut self.glyph_cache;
|
||||
let dpr = self.window.dpr;
|
||||
|
||||
|
@ -407,7 +407,7 @@ impl Display {
|
|||
pty_resize_handle: &mut dyn OnResize,
|
||||
message_buffer: &MessageBuffer,
|
||||
search_active: bool,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
) where
|
||||
T: EventListener,
|
||||
{
|
||||
|
@ -436,7 +436,7 @@ impl Display {
|
|||
height = dimensions.height as f32;
|
||||
}
|
||||
|
||||
let padding = config.ui_config.window.padding(self.window.dpr);
|
||||
let padding = config.window.padding(self.window.dpr);
|
||||
|
||||
self.size_info = SizeInfo::new(
|
||||
width,
|
||||
|
@ -445,7 +445,7 @@ impl Display {
|
|||
cell_height,
|
||||
padding.0,
|
||||
padding.1,
|
||||
config.ui_config.window.dynamic_padding,
|
||||
config.window.dynamic_padding,
|
||||
);
|
||||
|
||||
// Update number of column/lines in the viewport.
|
||||
|
@ -478,7 +478,7 @@ impl Display {
|
|||
&mut self,
|
||||
terminal: MutexGuard<'_, Term<T>>,
|
||||
message_buffer: &MessageBuffer,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
search_state: &SearchState,
|
||||
) {
|
||||
// Collect renderable content before the terminal is dropped.
|
||||
|
@ -505,7 +505,7 @@ impl Display {
|
|||
// Make sure this window's OpenGL context is active.
|
||||
self.window.make_current();
|
||||
|
||||
self.renderer.with_api(&config.ui_config, &size_info, |api| {
|
||||
self.renderer.with_api(config, &size_info, |api| {
|
||||
api.clear(background_color);
|
||||
});
|
||||
|
||||
|
@ -522,7 +522,7 @@ impl Display {
|
|||
let glyph_cache = &mut self.glyph_cache;
|
||||
let highlighted_hint = &self.highlighted_hint;
|
||||
let vi_highlighted_hint = &self.vi_highlighted_hint;
|
||||
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
||||
self.renderer.with_api(config, &size_info, |mut api| {
|
||||
// Iterate over all non-empty cells in the grid.
|
||||
for mut cell in grid_cells {
|
||||
// Underline hints hovered by mouse or vi mode cursor.
|
||||
|
@ -559,7 +559,7 @@ impl Display {
|
|||
|
||||
// Push the cursor rects for rendering.
|
||||
if let Some(cursor) = cursor {
|
||||
for rect in cursor.rects(&size_info, config.cursor.thickness()) {
|
||||
for rect in cursor.rects(&size_info, config.terminal_config.cursor.thickness()) {
|
||||
rects.push(rect);
|
||||
}
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ impl Display {
|
|||
0.,
|
||||
size_info.width(),
|
||||
size_info.height(),
|
||||
config.ui_config.bell.color,
|
||||
config.bell.color,
|
||||
visual_bell_intensity as f32,
|
||||
);
|
||||
rects.push(visual_bell_rect);
|
||||
|
@ -587,8 +587,8 @@ impl Display {
|
|||
let y = size_info.cell_height().mul_add(start_line as f32, size_info.padding_y());
|
||||
|
||||
let bg = match message.ty() {
|
||||
MessageType::Error => config.ui_config.colors.normal.red,
|
||||
MessageType::Warning => config.ui_config.colors.normal.yellow,
|
||||
MessageType::Error => config.colors.normal.red,
|
||||
MessageType::Warning => config.colors.normal.yellow,
|
||||
};
|
||||
|
||||
let message_bar_rect =
|
||||
|
@ -602,10 +602,10 @@ impl Display {
|
|||
|
||||
// Relay messages to the user.
|
||||
let glyph_cache = &mut self.glyph_cache;
|
||||
let fg = config.ui_config.colors.primary.background;
|
||||
let fg = config.colors.primary.background;
|
||||
for (i, message_text) in text.iter().enumerate() {
|
||||
let point = Point::new(start_line + i, Column(0));
|
||||
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
||||
self.renderer.with_api(config, &size_info, |mut api| {
|
||||
api.render_string(glyph_cache, point, fg, bg, message_text);
|
||||
});
|
||||
}
|
||||
|
@ -651,16 +651,16 @@ impl Display {
|
|||
// On X11 `swap_buffers` does not block for vsync. However the next OpenGl command
|
||||
// will block to synchronize (this is `glClear` in Alacritty), which causes a
|
||||
// permanent one frame delay.
|
||||
self.renderer.with_api(&config.ui_config, &size_info, |api| {
|
||||
self.renderer.with_api(config, &size_info, |api| {
|
||||
api.finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Update to a new configuration.
|
||||
pub fn update_config(&mut self, config: &Config) {
|
||||
self.visual_bell.update_config(&config.ui_config.bell);
|
||||
self.colors = List::from(&config.ui_config.colors);
|
||||
pub fn update_config(&mut self, config: &UiConfig) {
|
||||
self.visual_bell.update_config(&config.bell);
|
||||
self.colors = List::from(&config.colors);
|
||||
}
|
||||
|
||||
/// Update the mouse/vi mode cursor hint highlighting.
|
||||
|
@ -669,7 +669,7 @@ impl Display {
|
|||
pub fn update_highlighted_hints<T>(
|
||||
&mut self,
|
||||
term: &Term<T>,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
mouse: &Mouse,
|
||||
modifiers: ModifiersState,
|
||||
) -> bool {
|
||||
|
@ -744,7 +744,7 @@ impl Display {
|
|||
}
|
||||
|
||||
/// Draw current search regex.
|
||||
fn draw_search(&mut self, config: &Config, size_info: &SizeInfo, text: &str) {
|
||||
fn draw_search(&mut self, config: &UiConfig, size_info: &SizeInfo, text: &str) {
|
||||
let glyph_cache = &mut self.glyph_cache;
|
||||
let num_cols = size_info.columns();
|
||||
|
||||
|
@ -752,17 +752,17 @@ impl Display {
|
|||
let text = format!("{:<1$}", text, num_cols);
|
||||
|
||||
let point = Point::new(size_info.screen_lines(), Column(0));
|
||||
let fg = config.ui_config.colors.search_bar_foreground();
|
||||
let bg = config.ui_config.colors.search_bar_background();
|
||||
let fg = config.colors.search_bar_foreground();
|
||||
let bg = config.colors.search_bar_background();
|
||||
|
||||
self.renderer.with_api(&config.ui_config, size_info, |mut api| {
|
||||
self.renderer.with_api(config, size_info, |mut api| {
|
||||
api.render_string(glyph_cache, point, fg, bg, &text);
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw render timer.
|
||||
fn draw_render_timer(&mut self, config: &Config, size_info: &SizeInfo) {
|
||||
if !config.ui_config.debug.render_timer {
|
||||
fn draw_render_timer(&mut self, config: &UiConfig, size_info: &SizeInfo) {
|
||||
if !config.debug.render_timer {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -770,10 +770,10 @@ impl Display {
|
|||
|
||||
let timing = format!("{:.3} usec", self.meter.average());
|
||||
let point = Point::new(size_info.screen_lines().saturating_sub(2), Column(0));
|
||||
let fg = config.ui_config.colors.primary.background;
|
||||
let bg = config.ui_config.colors.normal.red;
|
||||
let fg = config.colors.primary.background;
|
||||
let bg = config.colors.normal.red;
|
||||
|
||||
self.renderer.with_api(&config.ui_config, size_info, |mut api| {
|
||||
self.renderer.with_api(config, size_info, |mut api| {
|
||||
api.render_string(glyph_cache, point, fg, bg, &timing);
|
||||
});
|
||||
}
|
||||
|
@ -781,7 +781,7 @@ impl Display {
|
|||
/// Draw an indicator for the position of a line in history.
|
||||
fn draw_line_indicator(
|
||||
&mut self,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
size_info: &SizeInfo,
|
||||
total_lines: usize,
|
||||
obstructed_column: Option<Column>,
|
||||
|
@ -789,14 +789,14 @@ impl Display {
|
|||
) {
|
||||
let text = format!("[{}/{}]", line, total_lines - 1);
|
||||
let column = Column(size_info.columns().saturating_sub(text.len()));
|
||||
let colors = &config.ui_config.colors;
|
||||
let colors = &config.colors;
|
||||
let fg = colors.line_indicator.foreground.unwrap_or(colors.primary.background);
|
||||
let bg = colors.line_indicator.background.unwrap_or(colors.primary.foreground);
|
||||
|
||||
// Do not render anything if it would obscure the vi mode cursor.
|
||||
if obstructed_column.map_or(true, |obstructed_column| obstructed_column < column) {
|
||||
let glyph_cache = &mut self.glyph_cache;
|
||||
self.renderer.with_api(&config.ui_config, size_info, |mut api| {
|
||||
self.renderer.with_api(config, size_info, |mut api| {
|
||||
api.render_string(glyph_cache, Point::new(0, column), fg, bg, &text);
|
||||
});
|
||||
}
|
||||
|
@ -847,9 +847,9 @@ pub fn viewport_to_point(display_offset: usize, point: Point<usize>) -> Point {
|
|||
///
|
||||
/// This will return a tuple of the cell width and height.
|
||||
#[inline]
|
||||
fn compute_cell_size(config: &Config, metrics: &crossfont::Metrics) -> (f32, f32) {
|
||||
let offset_x = f64::from(config.ui_config.font.offset.x);
|
||||
let offset_y = f64::from(config.ui_config.font.offset.y);
|
||||
fn compute_cell_size(config: &UiConfig, metrics: &crossfont::Metrics) -> (f32, f32) {
|
||||
let offset_x = f64::from(config.font.offset.x);
|
||||
let offset_y = f64::from(config.font.offset.y);
|
||||
(
|
||||
(metrics.average_advance + offset_x).floor().max(1.) as f32,
|
||||
(metrics.line_height + offset_y).floor().max(1.) as f32,
|
||||
|
@ -858,13 +858,13 @@ fn compute_cell_size(config: &Config, metrics: &crossfont::Metrics) -> (f32, f32
|
|||
|
||||
/// Calculate the size of the window given padding, terminal dimensions and cell size.
|
||||
fn window_size(
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
dimensions: Dimensions,
|
||||
cell_width: f32,
|
||||
cell_height: f32,
|
||||
dpr: f64,
|
||||
) -> PhysicalSize<u32> {
|
||||
let padding = config.ui_config.window.padding(dpr);
|
||||
let padding = config.window.padding(dpr);
|
||||
|
||||
let grid_width = cell_width * dimensions.columns.0.max(MIN_COLUMNS) as f32;
|
||||
let grid_height = cell_height * dimensions.lines.max(MIN_SCREEN_LINES) as f32;
|
||||
|
|
|
@ -54,7 +54,7 @@ use alacritty_terminal::index::Point;
|
|||
use alacritty_terminal::term::SizeInfo;
|
||||
|
||||
use crate::config::window::{Decorations, WindowConfig};
|
||||
use crate::config::Config;
|
||||
use crate::config::UiConfig;
|
||||
use crate::gl;
|
||||
|
||||
/// Window icon for `_NET_WM_ICON` property.
|
||||
|
@ -172,12 +172,12 @@ impl Window {
|
|||
/// This creates a window and fully initializes a window.
|
||||
pub fn new<E>(
|
||||
event_loop: &EventLoopWindowTarget<E>,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
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.ui_config.window;
|
||||
let window_config = &config.window;
|
||||
let window_builder = Window::get_platform_window(&window_config.title, window_config);
|
||||
|
||||
// Check if we're running Wayland to disable vsync.
|
||||
|
@ -210,7 +210,7 @@ impl Window {
|
|||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
let wayland_surface = if is_wayland {
|
||||
// Apply client side decorations theme.
|
||||
let theme = AlacrittyWaylandTheme::new(&config.ui_config.colors);
|
||||
let theme = AlacrittyWaylandTheme::new(&config.colors);
|
||||
windowed_context.window().set_wayland_theme(theme);
|
||||
|
||||
// Attach surface to Alacritty's internal wayland queue to handle frame callbacks.
|
||||
|
|
|
@ -5,8 +5,6 @@ use std::cmp::{max, min};
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
#[cfg(not(any(target_os = "macos", windows)))]
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{env, f32, mem};
|
||||
|
@ -35,10 +33,10 @@ use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
|
|||
#[cfg(not(windows))]
|
||||
use alacritty_terminal::tty;
|
||||
|
||||
use crate::cli::Options as CliOptions;
|
||||
use crate::cli::{Options as CliOptions, TerminalOptions as TerminalCliOptions};
|
||||
use crate::clipboard::Clipboard;
|
||||
use crate::config::ui_config::{HintAction, HintInternalAction};
|
||||
use crate::config::{self, Config};
|
||||
use crate::config::{self, UiConfig};
|
||||
use crate::daemon::start_daemon;
|
||||
use crate::display::hint::HintMatch;
|
||||
use crate::display::window::Window;
|
||||
|
@ -89,7 +87,7 @@ pub enum EventType {
|
|||
ConfigReload(PathBuf),
|
||||
Message(Message),
|
||||
Scroll(Scroll),
|
||||
CreateWindow,
|
||||
CreateWindow(Option<TerminalCliOptions>),
|
||||
BlinkCursor,
|
||||
SearchNext,
|
||||
}
|
||||
|
@ -180,7 +178,7 @@ pub struct ActionContext<'a, N, T> {
|
|||
pub modifiers: &'a mut ModifiersState,
|
||||
pub display: &'a mut Display,
|
||||
pub message_buffer: &'a mut MessageBuffer,
|
||||
pub config: &'a mut Config,
|
||||
pub config: &'a mut UiConfig,
|
||||
pub event_loop: &'a EventLoopWindowTarget<Event>,
|
||||
pub event_proxy: &'a EventLoopProxy<Event>,
|
||||
pub scheduler: &'a mut Scheduler,
|
||||
|
@ -241,7 +239,8 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
None => return,
|
||||
};
|
||||
|
||||
if ty == ClipboardType::Selection && self.config.selection.save_to_clipboard {
|
||||
if ty == ClipboardType::Selection && self.config.terminal_config.selection.save_to_clipboard
|
||||
{
|
||||
self.clipboard.store(ClipboardType::Clipboard, text.clone());
|
||||
}
|
||||
self.clipboard.store(ty, text);
|
||||
|
@ -318,17 +317,17 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
|
||||
#[inline]
|
||||
fn received_count(&mut self) -> &mut usize {
|
||||
&mut self.received_count
|
||||
self.received_count
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn suppress_chars(&mut self) -> &mut bool {
|
||||
&mut self.suppress_chars
|
||||
self.suppress_chars
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn modifiers(&mut self) -> &mut ModifiersState {
|
||||
&mut self.modifiers
|
||||
self.modifiers
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -338,7 +337,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
|
||||
#[inline]
|
||||
fn display(&mut self) -> &mut Display {
|
||||
&mut self.display
|
||||
self.display
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -355,26 +354,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
let mut env_args = env::args();
|
||||
let alacritty = env_args.next().unwrap();
|
||||
|
||||
// Use working directory of controlling process, or fallback to initial shell, then add it
|
||||
// as working-directory parameter.
|
||||
#[cfg(unix)]
|
||||
let mut args = {
|
||||
// Use working directory of controlling process, or fallback to initial shell.
|
||||
let mut pid = unsafe { libc::tcgetpgrp(tty::master_fd()) };
|
||||
if pid < 0 {
|
||||
pid = tty::child_pid();
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "freebsd")))]
|
||||
let link_path = format!("/proc/{}/cwd", pid);
|
||||
#[cfg(target_os = "freebsd")]
|
||||
let link_path = format!("/compat/linux/proc/{}/cwd", pid);
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let cwd = fs::read_link(link_path);
|
||||
#[cfg(target_os = "macos")]
|
||||
let cwd = macos::proc::cwd(pid);
|
||||
|
||||
// Add the current working directory as parameter.
|
||||
cwd.map(|path| vec!["--working-directory".into(), path]).unwrap_or_default()
|
||||
};
|
||||
let mut args = foreground_process_path()
|
||||
.map(|path| vec!["--working-directory".into(), path])
|
||||
.unwrap_or_default();
|
||||
|
||||
#[cfg(not(unix))]
|
||||
let mut args: Vec<PathBuf> = Vec::new();
|
||||
|
@ -395,20 +380,34 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
start_daemon(&alacritty, &args);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn create_new_window(&mut self) {
|
||||
let _ = self.event_proxy.send_event(Event::new(EventType::CreateWindow, None));
|
||||
let options = if let Ok(working_directory) = foreground_process_path() {
|
||||
let mut options = TerminalCliOptions::new();
|
||||
options.working_directory = Some(working_directory);
|
||||
Some(options)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let _ = self.event_proxy.send_event(Event::new(EventType::CreateWindow(options), None));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn create_new_window(&mut self) {
|
||||
let _ = self.event_proxy.send_event(Event::new(EventType::CreateWindow(None), None));
|
||||
}
|
||||
|
||||
fn change_font_size(&mut self, delta: f32) {
|
||||
*self.font_size = max(*self.font_size + delta, Size::new(FONT_SIZE_STEP));
|
||||
let font = self.config.ui_config.font.clone().with_size(*self.font_size);
|
||||
let font = self.config.font.clone().with_size(*self.font_size);
|
||||
self.display.pending_update.set_font(font);
|
||||
*self.dirty = true;
|
||||
}
|
||||
|
||||
fn reset_font_size(&mut self) {
|
||||
*self.font_size = self.config.ui_config.font.size();
|
||||
self.display.pending_update.set_font(self.config.ui_config.font.clone());
|
||||
*self.font_size = self.config.font.size();
|
||||
self.display.pending_update.set_font(self.config.font.clone());
|
||||
*self.dirty = true;
|
||||
}
|
||||
|
||||
|
@ -632,14 +631,15 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
// Disable cursor blinking.
|
||||
let timer_id = TimerId::new(Topic::BlinkCursor, self.display.window.id());
|
||||
if let Some(timer) = self.scheduler.unschedule(timer_id) {
|
||||
let interval = Duration::from_millis(self.config.cursor.blink_interval());
|
||||
let interval =
|
||||
Duration::from_millis(self.config.terminal_config.cursor.blink_interval());
|
||||
self.scheduler.schedule(timer.event, interval, true, timer.id);
|
||||
self.display.cursor_hidden = false;
|
||||
*self.dirty = true;
|
||||
}
|
||||
|
||||
// Hide mouse cursor.
|
||||
if self.config.ui_config.mouse.hide_when_typing {
|
||||
if self.config.mouse.hide_when_typing {
|
||||
self.display.window.set_mouse_visible(false);
|
||||
}
|
||||
}
|
||||
|
@ -769,7 +769,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
self.message_buffer.message()
|
||||
}
|
||||
|
||||
fn config(&self) -> &Config {
|
||||
fn config(&self) -> &UiConfig {
|
||||
self.config
|
||||
}
|
||||
|
||||
|
@ -794,7 +794,7 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
|
|||
};
|
||||
|
||||
// Hide cursor while typing into the search bar.
|
||||
if self.config.ui_config.mouse.hide_when_typing {
|
||||
if self.config.mouse.hide_when_typing {
|
||||
self.display.window.set_mouse_visible(false);
|
||||
}
|
||||
|
||||
|
@ -905,9 +905,9 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
|
|||
/// Update the cursor blinking state.
|
||||
fn update_cursor_blinking(&mut self) {
|
||||
// Get config cursor style.
|
||||
let mut cursor_style = self.config.cursor.style;
|
||||
let mut cursor_style = self.config.terminal_config.cursor.style;
|
||||
if self.terminal.mode().contains(TermMode::VI) {
|
||||
cursor_style = self.config.cursor.vi_mode_style.unwrap_or(cursor_style);
|
||||
cursor_style = self.config.terminal_config.cursor.vi_mode_style.unwrap_or(cursor_style);
|
||||
};
|
||||
|
||||
// Check terminal cursor style.
|
||||
|
@ -919,7 +919,8 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
|
|||
self.scheduler.unschedule(timer_id);
|
||||
if blinking && self.terminal.is_focused {
|
||||
let event = Event::new(EventType::BlinkCursor, self.display.window.id());
|
||||
let interval = Duration::from_millis(self.config.cursor.blink_interval());
|
||||
let interval =
|
||||
Duration::from_millis(self.config.terminal_config.cursor.blink_interval());
|
||||
self.scheduler.schedule(event, interval, true, timer_id);
|
||||
} else {
|
||||
self.display.cursor_hidden = false;
|
||||
|
@ -1004,7 +1005,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
|||
let display_update_pending = &mut self.ctx.display.pending_update;
|
||||
|
||||
// Push current font to update its DPR.
|
||||
let font = self.ctx.config.ui_config.font.clone();
|
||||
let font = self.ctx.config.font.clone();
|
||||
display_update_pending.set_font(font.with_size(*self.ctx.font_size));
|
||||
|
||||
// Resize to event's dimensions, since no resize event is emitted on Wayland.
|
||||
|
@ -1026,15 +1027,13 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
|||
},
|
||||
EventType::Terminal(event) => match event {
|
||||
TerminalEvent::Title(title) => {
|
||||
let ui_config = &self.ctx.config.ui_config;
|
||||
if ui_config.window.dynamic_title {
|
||||
if self.ctx.config.window.dynamic_title {
|
||||
self.ctx.window().set_title(&title);
|
||||
}
|
||||
},
|
||||
TerminalEvent::ResetTitle => {
|
||||
let ui_config = &self.ctx.config.ui_config;
|
||||
if ui_config.window.dynamic_title {
|
||||
self.ctx.display.window.set_title(&ui_config.window.title);
|
||||
if self.ctx.config.window.dynamic_title {
|
||||
self.ctx.display.window.set_title(&self.ctx.config.window.title);
|
||||
}
|
||||
},
|
||||
TerminalEvent::Wakeup => *self.ctx.dirty = true,
|
||||
|
@ -1049,7 +1048,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
|||
self.ctx.display.visual_bell.ring();
|
||||
|
||||
// Execute bell command.
|
||||
if let Some(bell_command) = &self.ctx.config.ui_config.bell.command {
|
||||
if let Some(bell_command) = &self.ctx.config.bell.command {
|
||||
start_daemon(bell_command.program(), bell_command.args());
|
||||
}
|
||||
},
|
||||
|
@ -1069,7 +1068,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
|||
TerminalEvent::Exit => (),
|
||||
TerminalEvent::CursorBlinkingChange => self.ctx.update_cursor_blinking(),
|
||||
},
|
||||
EventType::ConfigReload(_) | EventType::CreateWindow => (),
|
||||
EventType::ConfigReload(_) | EventType::CreateWindow(_) => (),
|
||||
},
|
||||
GlutinEvent::RedrawRequested(_) => *self.ctx.dirty = true,
|
||||
GlutinEvent::WindowEvent { event, .. } => {
|
||||
|
@ -1162,7 +1161,7 @@ pub struct Processor {
|
|||
wayland_event_queue: Option<EventQueue>,
|
||||
windows: HashMap<WindowId, WindowContext>,
|
||||
cli_options: CliOptions,
|
||||
config: Config,
|
||||
config: UiConfig,
|
||||
}
|
||||
|
||||
impl Processor {
|
||||
|
@ -1170,7 +1169,7 @@ impl Processor {
|
|||
///
|
||||
/// Takes a writer which is expected to be hooked up to the write end of a PTY.
|
||||
pub fn new(
|
||||
config: Config,
|
||||
config: UiConfig,
|
||||
cli_options: CliOptions,
|
||||
_event_loop: &EventLoop<Event>,
|
||||
) -> Processor {
|
||||
|
@ -1194,10 +1193,12 @@ impl Processor {
|
|||
pub fn create_window(
|
||||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<Event>,
|
||||
options: Option<TerminalCliOptions>,
|
||||
proxy: EventLoopProxy<Event>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let window_context = WindowContext::new(
|
||||
&self.config,
|
||||
options,
|
||||
event_loop,
|
||||
proxy,
|
||||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
|
@ -1219,7 +1220,7 @@ impl Processor {
|
|||
let mut clipboard = Clipboard::new();
|
||||
|
||||
event_loop.run_return(|event, event_loop, control_flow| {
|
||||
if self.config.ui_config.debug.print_events {
|
||||
if self.config.debug.print_events {
|
||||
info!("glutin event: {:?}", event);
|
||||
}
|
||||
|
||||
|
@ -1246,7 +1247,7 @@ impl Processor {
|
|||
// Shutdown if no more terminals are open.
|
||||
if self.windows.is_empty() {
|
||||
// Write ref tests of last window to disk.
|
||||
if self.config.ui_config.debug.ref_test {
|
||||
if self.config.debug.ref_test {
|
||||
window_context.write_ref_test_results();
|
||||
}
|
||||
|
||||
|
@ -1302,8 +1303,10 @@ impl Processor {
|
|||
}
|
||||
},
|
||||
// Create a new terminal window.
|
||||
GlutinEvent::UserEvent(Event { payload: EventType::CreateWindow, .. }) => {
|
||||
if let Err(err) = self.create_window(event_loop, proxy.clone()) {
|
||||
GlutinEvent::UserEvent(Event {
|
||||
payload: EventType::CreateWindow(options), ..
|
||||
}) => {
|
||||
if let Err(err) = self.create_window(event_loop, options, proxy.clone()) {
|
||||
error!("Could not open window: {:?}", err);
|
||||
}
|
||||
},
|
||||
|
@ -1386,3 +1389,24 @@ impl EventListener for EventProxy {
|
|||
let _ = self.proxy.send_event(Event::new(event.into(), self.window_id));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn foreground_process_path() -> Result<PathBuf, Box<dyn Error>> {
|
||||
let mut pid = unsafe { libc::tcgetpgrp(tty::master_fd()) };
|
||||
if pid < 0 {
|
||||
pid = tty::child_pid();
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "freebsd")))]
|
||||
let link_path = format!("/proc/{}/cwd", pid);
|
||||
#[cfg(target_os = "freebsd")]
|
||||
let link_path = format!("/compat/linux/proc/{}/cwd", pid);
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let cwd = std::fs::read_link(link_path)?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let cwd = macos::proc::cwd(pid)?;
|
||||
|
||||
Ok(cwd)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
|
|||
use alacritty_terminal::vi_mode::ViMotion;
|
||||
|
||||
use crate::clipboard::Clipboard;
|
||||
use crate::config::{Action, BindingMode, Config, Key, MouseAction, SearchAction, ViAction};
|
||||
use crate::config::{Action, BindingMode, Key, MouseAction, SearchAction, UiConfig, ViAction};
|
||||
use crate::daemon::start_daemon;
|
||||
use crate::display::hint::HintMatch;
|
||||
use crate::display::window::Window;
|
||||
|
@ -85,7 +85,7 @@ pub trait ActionContext<T: EventListener> {
|
|||
fn reset_font_size(&mut self) {}
|
||||
fn pop_message(&mut self) {}
|
||||
fn message(&self) -> Option<&Message>;
|
||||
fn config(&self) -> &Config;
|
||||
fn config(&self) -> &UiConfig;
|
||||
fn event_loop(&self) -> &EventLoopWindowTarget<Event>;
|
||||
fn mouse_mode(&self) -> bool;
|
||||
fn clipboard_mut(&mut self) -> &mut Clipboard;
|
||||
|
@ -515,7 +515,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
self.ctx.mouse_mut().last_click_timestamp = now;
|
||||
|
||||
// Update multi-click state.
|
||||
let mouse_config = &self.ctx.config().ui_config.mouse;
|
||||
let mouse_config = &self.ctx.config().mouse;
|
||||
self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state {
|
||||
// Reset click state if button has changed.
|
||||
_ if button != self.ctx.mouse().last_click_button => {
|
||||
|
@ -643,7 +643,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
.contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL)
|
||||
&& !self.ctx.modifiers().shift()
|
||||
{
|
||||
let multiplier = f64::from(self.ctx.config().scrolling.multiplier);
|
||||
let multiplier = f64::from(self.ctx.config().terminal_config.scrolling.multiplier);
|
||||
self.ctx.mouse_mut().scroll_px += new_scroll_px * multiplier;
|
||||
|
||||
let cmd = if new_scroll_px > 0. { b'A' } else { b'B' };
|
||||
|
@ -657,7 +657,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
}
|
||||
self.ctx.write_to_pty(content);
|
||||
} else {
|
||||
let multiplier = f64::from(self.ctx.config().scrolling.multiplier);
|
||||
let multiplier = f64::from(self.ctx.config().terminal_config.scrolling.multiplier);
|
||||
self.ctx.mouse_mut().scroll_px += new_scroll_px * multiplier;
|
||||
|
||||
let lines = self.ctx.mouse().scroll_px / height;
|
||||
|
@ -801,7 +801,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
c.encode_utf8(&mut bytes[..]);
|
||||
}
|
||||
|
||||
if self.ctx.config().ui_config.alt_send_esc
|
||||
if self.ctx.config().alt_send_esc
|
||||
&& *self.ctx.received_count() == 0
|
||||
&& self.ctx.modifiers().alt()
|
||||
&& utf8_len == 1
|
||||
|
@ -823,8 +823,8 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
let mods = *self.ctx.modifiers();
|
||||
let mut suppress_chars = None;
|
||||
|
||||
for i in 0..self.ctx.config().ui_config.key_bindings().len() {
|
||||
let binding = &self.ctx.config().ui_config.key_bindings()[i];
|
||||
for i in 0..self.ctx.config().key_bindings().len() {
|
||||
let binding = &self.ctx.config().key_bindings()[i];
|
||||
|
||||
let key = match (binding.trigger, input.virtual_keycode) {
|
||||
(Key::Scancode(_), _) => Key::Scancode(input.scancode),
|
||||
|
@ -854,8 +854,8 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
let mouse_mode = self.ctx.mouse_mode();
|
||||
let mods = *self.ctx.modifiers();
|
||||
|
||||
for i in 0..self.ctx.config().ui_config.mouse_bindings().len() {
|
||||
let mut binding = self.ctx.config().ui_config.mouse_bindings()[i].clone();
|
||||
for i in 0..self.ctx.config().mouse_bindings().len() {
|
||||
let mut binding = self.ctx.config().mouse_bindings()[i].clone();
|
||||
|
||||
// Require shift for all modifiers when mouse mode is active.
|
||||
if mouse_mode {
|
||||
|
@ -974,7 +974,7 @@ mod tests {
|
|||
pub received_count: usize,
|
||||
pub suppress_chars: bool,
|
||||
pub modifiers: ModifiersState,
|
||||
config: &'a Config,
|
||||
config: &'a UiConfig,
|
||||
}
|
||||
|
||||
impl<'a, T: EventListener> super::ActionContext<T> for ActionContext<'a, T> {
|
||||
|
@ -1057,7 +1057,7 @@ mod tests {
|
|||
self.message_buffer.message()
|
||||
}
|
||||
|
||||
fn config(&self) -> &Config {
|
||||
fn config(&self) -> &UiConfig {
|
||||
self.config
|
||||
}
|
||||
|
||||
|
@ -1085,7 +1085,7 @@ mod tests {
|
|||
#[test]
|
||||
fn $name() {
|
||||
let mut clipboard = Clipboard::new_nop();
|
||||
let cfg = Config::default();
|
||||
let cfg = UiConfig::default();
|
||||
let size = SizeInfo::new(
|
||||
21.0,
|
||||
51.0,
|
||||
|
@ -1096,7 +1096,7 @@ mod tests {
|
|||
false,
|
||||
);
|
||||
|
||||
let mut terminal = Term::new(&cfg, size, MockEventProxy);
|
||||
let mut terminal = Term::new(&cfg.terminal_config, size, MockEventProxy);
|
||||
|
||||
let mut mouse = Mouse {
|
||||
click_state: $initial_state,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Alacritty socket IPC.
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::io::{Error as IoError, ErrorKind, Result as IoResult};
|
||||
use std::os::unix::net::UnixDatagram;
|
||||
use std::io::{BufRead, BufReader, Error as IoError, ErrorKind, Result as IoResult, Write};
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs, process};
|
||||
|
||||
|
@ -11,12 +11,9 @@ use log::warn;
|
|||
|
||||
use alacritty_terminal::thread;
|
||||
|
||||
use crate::cli::Options;
|
||||
use crate::cli::{Options, SocketMessage};
|
||||
use crate::event::{Event, EventType};
|
||||
|
||||
/// IPC socket message for creating a new window.
|
||||
pub const SOCKET_MESSAGE_CREATE_WINDOW: [u8; 1] = [1];
|
||||
|
||||
/// Environment variable name for the IPC socket path.
|
||||
const ALACRITTY_SOCKET_ENV: &str = "ALACRITTY_SOCKET";
|
||||
|
||||
|
@ -30,8 +27,8 @@ pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy<Event>) -
|
|||
});
|
||||
env::set_var(ALACRITTY_SOCKET_ENV, socket_path.as_os_str());
|
||||
|
||||
let socket = match UnixDatagram::bind(&socket_path) {
|
||||
Ok(socket) => socket,
|
||||
let listener = match UnixListener::bind(&socket_path) {
|
||||
Ok(listener) => listener,
|
||||
Err(err) => {
|
||||
warn!("Unable to create socket: {:?}", err);
|
||||
return None;
|
||||
|
@ -40,13 +37,31 @@ pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy<Event>) -
|
|||
|
||||
// Spawn a thread to listen on the IPC socket.
|
||||
thread::spawn_named("socket listener", move || {
|
||||
// Accept up to 2 bytes to ensure only one byte is received.
|
||||
// This ensures forward-compatibility.
|
||||
let mut buf = [0; 2];
|
||||
let mut data = String::new();
|
||||
for stream in listener.incoming().filter_map(Result::ok) {
|
||||
data.clear();
|
||||
let mut stream = BufReader::new(stream);
|
||||
|
||||
while let Ok(received) = socket.recv(&mut buf) {
|
||||
if buf[..received] == SOCKET_MESSAGE_CREATE_WINDOW {
|
||||
let _ = event_proxy.send_event(Event::new(EventType::CreateWindow, None));
|
||||
match stream.read_line(&mut data) {
|
||||
Ok(0) | Err(_) => continue,
|
||||
Ok(_) => (),
|
||||
};
|
||||
|
||||
// Read pending events on socket.
|
||||
let message: SocketMessage = match serde_json::from_str(&data) {
|
||||
Ok(message) => message,
|
||||
Err(err) => {
|
||||
warn!("Failed to convert data from socket: {}", err);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
// Handle IPC events.
|
||||
match message {
|
||||
SocketMessage::CreateWindow(terminal_options) => {
|
||||
let event = Event::new(EventType::CreateWindow(Some(terminal_options)), None);
|
||||
let _ = event_proxy.send_event(event);
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -55,9 +70,13 @@ pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy<Event>) -
|
|||
}
|
||||
|
||||
/// Send a message to the active Alacritty socket.
|
||||
pub fn send_message(socket: Option<PathBuf>, message: &[u8]) -> IoResult<()> {
|
||||
let socket = find_socket(socket)?;
|
||||
socket.send(message)?;
|
||||
pub fn send_message(socket: Option<PathBuf>, message: SocketMessage) -> IoResult<()> {
|
||||
let mut socket = find_socket(socket)?;
|
||||
|
||||
let message = serde_json::to_string(&message)?;
|
||||
socket.write_all(message[..].as_bytes())?;
|
||||
let _ = socket.flush();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -78,22 +97,20 @@ fn socket_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
/// Find the IPC socket path.
|
||||
fn find_socket(socket_path: Option<PathBuf>) -> IoResult<UnixDatagram> {
|
||||
let socket = UnixDatagram::unbound()?;
|
||||
|
||||
fn find_socket(socket_path: Option<PathBuf>) -> IoResult<UnixStream> {
|
||||
// Handle --socket CLI override.
|
||||
if let Some(socket_path) = socket_path {
|
||||
// Ensure we inform the user about an invalid path.
|
||||
socket.connect(&socket_path).map_err(|err| {
|
||||
return UnixStream::connect(&socket_path).map_err(|err| {
|
||||
let message = format!("invalid socket path {:?}", socket_path);
|
||||
IoError::new(err.kind(), message)
|
||||
})?;
|
||||
});
|
||||
}
|
||||
|
||||
// Handle environment variable.
|
||||
if let Ok(path) = env::var(ALACRITTY_SOCKET_ENV) {
|
||||
let socket_path = PathBuf::from(path);
|
||||
if socket.connect(&socket_path).is_ok() {
|
||||
if let Ok(socket) = UnixStream::connect(&socket_path) {
|
||||
return Ok(socket);
|
||||
}
|
||||
}
|
||||
|
@ -114,8 +131,8 @@ fn find_socket(socket_path: Option<PathBuf>) -> IoResult<UnixDatagram> {
|
|||
}
|
||||
|
||||
// Attempt to connect to the socket.
|
||||
match socket.connect(&path) {
|
||||
Ok(_) => return Ok(socket),
|
||||
match UnixStream::connect(&path) {
|
||||
Ok(socket) => return Ok(socket),
|
||||
// Delete orphan sockets.
|
||||
Err(error) if error.kind() == ErrorKind::ConnectionRefused => {
|
||||
let _ = fs::remove_file(&path);
|
||||
|
|
|
@ -53,10 +53,8 @@ mod gl {
|
|||
use crate::cli::Options;
|
||||
#[cfg(unix)]
|
||||
use crate::cli::{MessageOptions, Subcommands};
|
||||
use crate::config::{monitor, Config};
|
||||
use crate::config::{monitor, UiConfig};
|
||||
use crate::event::{Event, Processor};
|
||||
#[cfg(unix)]
|
||||
use crate::ipc::SOCKET_MESSAGE_CREATE_WINDOW;
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::macos::locale;
|
||||
|
||||
|
@ -94,7 +92,7 @@ fn main() {
|
|||
/// `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())
|
||||
ipc::send_message(options.socket, options.message).map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
/// Temporary files stored for Alacritty.
|
||||
|
@ -142,10 +140,10 @@ fn alacritty(options: Options) -> Result<(), String> {
|
|||
log_config_path(&config);
|
||||
|
||||
// Update the log level from config.
|
||||
log::set_max_level(config.ui_config.debug.log_level);
|
||||
log::set_max_level(config.debug.log_level);
|
||||
|
||||
// Set environment variables.
|
||||
tty::setup_env(&config);
|
||||
tty::setup_env(&config.terminal_config);
|
||||
|
||||
// Switch to home directory.
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -159,20 +157,20 @@ fn alacritty(options: Options) -> Result<(), String> {
|
|||
//
|
||||
// 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(), window_event_loop.create_proxy());
|
||||
if config.live_config_reload {
|
||||
monitor::watch(config.config_paths.clone(), window_event_loop.create_proxy());
|
||||
}
|
||||
|
||||
// Create the IPC socket listener.
|
||||
#[cfg(unix)]
|
||||
let socket_path = if config.ui_config.ipc_socket {
|
||||
let socket_path = if 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 log_cleanup = log_file.filter(|_| !config.debug.persistent_logging);
|
||||
let _files = TemporaryFiles {
|
||||
#[cfg(unix)]
|
||||
socket_path,
|
||||
|
@ -184,7 +182,7 @@ fn alacritty(options: Options) -> Result<(), String> {
|
|||
|
||||
// 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())?;
|
||||
processor.create_window(&window_event_loop, None, proxy).map_err(|err| err.to_string())?;
|
||||
|
||||
info!("Initialisation complete");
|
||||
|
||||
|
@ -217,13 +215,13 @@ fn alacritty(options: Options) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn log_config_path(config: &Config) {
|
||||
if config.ui_config.config_paths.is_empty() {
|
||||
fn log_config_path(config: &UiConfig) {
|
||||
if config.config_paths.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut msg = String::from("Configuration files loaded from:");
|
||||
for path in &config.ui_config.config_paths {
|
||||
for path in &config.config_paths {
|
||||
msg.push_str(&format!("\n {:?}", path.display()));
|
||||
}
|
||||
|
||||
|
|
|
@ -673,7 +673,7 @@ impl QuadRenderer {
|
|||
gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
|
||||
|
||||
// Restore viewport with padding.
|
||||
self.set_viewport(&size_info);
|
||||
self.set_viewport(size_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Terminal window context.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
@ -25,8 +26,9 @@ use alacritty_terminal::sync::FairMutex;
|
|||
use alacritty_terminal::term::{Term, TermMode};
|
||||
use alacritty_terminal::tty;
|
||||
|
||||
use crate::cli::TerminalOptions as TerminalCliOptions;
|
||||
use crate::clipboard::Clipboard;
|
||||
use crate::config::Config;
|
||||
use crate::config::UiConfig;
|
||||
use crate::display::Display;
|
||||
use crate::event::{ActionContext, Event, EventProxy, EventType, Mouse, SearchState};
|
||||
use crate::input;
|
||||
|
@ -52,7 +54,8 @@ pub struct WindowContext {
|
|||
impl WindowContext {
|
||||
/// Create a new terminal window context.
|
||||
pub fn new(
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
options: Option<TerminalCliOptions>,
|
||||
window_event_loop: &EventLoopWindowTarget<Event>,
|
||||
proxy: EventLoopProxy<Event>,
|
||||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
|
@ -81,7 +84,7 @@ impl WindowContext {
|
|||
// 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 = Term::new(&config.terminal_config, display.size_info, event_proxy.clone());
|
||||
let terminal = Arc::new(FairMutex::new(terminal));
|
||||
|
||||
// Create the PTY.
|
||||
|
@ -89,7 +92,10 @@ impl WindowContext {
|
|||
// 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());
|
||||
let pty_config = options
|
||||
.map(|o| Cow::Owned(o.into()))
|
||||
.unwrap_or(Cow::Borrowed(&config.terminal_config.pty_config));
|
||||
let pty = tty::new(&pty_config, &display.size_info, display.window.x11_window_id())?;
|
||||
|
||||
// Create the pseudoterminal I/O loop.
|
||||
//
|
||||
|
@ -101,8 +107,8 @@ impl WindowContext {
|
|||
Arc::clone(&terminal),
|
||||
event_proxy.clone(),
|
||||
pty,
|
||||
config.hold,
|
||||
config.ui_config.debug.ref_test,
|
||||
pty_config.hold,
|
||||
config.debug.ref_test,
|
||||
);
|
||||
|
||||
// The event loop channel allows write requests from the event processor
|
||||
|
@ -113,13 +119,13 @@ impl WindowContext {
|
|||
let _io_thread = event_loop.spawn();
|
||||
|
||||
// Start cursor blinking, in case `Focused` isn't sent on startup.
|
||||
if config.cursor.style().blinking {
|
||||
if config.terminal_config.cursor.style().blinking {
|
||||
event_proxy.send_event(TerminalEvent::CursorBlinkingChange.into());
|
||||
}
|
||||
|
||||
// Create context for the Alacritty window.
|
||||
Ok(WindowContext {
|
||||
font_size: config.ui_config.font.size(),
|
||||
font_size: config.font.size(),
|
||||
notifier: Notifier(loop_tx),
|
||||
terminal,
|
||||
display,
|
||||
|
@ -135,53 +141,55 @@ impl WindowContext {
|
|||
}
|
||||
|
||||
/// Update the terminal window to the latest config.
|
||||
pub fn update_config(&mut self, old_config: &Config, config: &Config) {
|
||||
pub fn update_config(&mut self, old_config: &UiConfig, config: &UiConfig) {
|
||||
self.display.update_config(config);
|
||||
self.terminal.lock().update_config(config);
|
||||
self.terminal.lock().update_config(&config.terminal_config);
|
||||
|
||||
// Reload cursor if its thickness has changed.
|
||||
if (old_config.cursor.thickness() - config.cursor.thickness()).abs() > f32::EPSILON {
|
||||
if (old_config.terminal_config.cursor.thickness()
|
||||
- config.terminal_config.cursor.thickness())
|
||||
.abs()
|
||||
> f32::EPSILON
|
||||
{
|
||||
self.display.pending_update.set_cursor_dirty();
|
||||
}
|
||||
|
||||
if old_config.ui_config.font != config.ui_config.font {
|
||||
if old_config.font != config.font {
|
||||
// Do not update font size if it has been changed at runtime.
|
||||
if self.font_size == old_config.ui_config.font.size() {
|
||||
self.font_size = config.ui_config.font.size();
|
||||
if self.font_size == old_config.font.size() {
|
||||
self.font_size = config.font.size();
|
||||
}
|
||||
|
||||
let font = config.ui_config.font.clone().with_size(self.font_size);
|
||||
let font = config.font.clone().with_size(self.font_size);
|
||||
self.display.pending_update.set_font(font);
|
||||
}
|
||||
|
||||
// Update display if padding options were changed.
|
||||
let window_config = &old_config.ui_config.window;
|
||||
if window_config.padding(1.) != config.ui_config.window.padding(1.)
|
||||
|| window_config.dynamic_padding != config.ui_config.window.dynamic_padding
|
||||
let window_config = &old_config.window;
|
||||
if window_config.padding(1.) != config.window.padding(1.)
|
||||
|| window_config.dynamic_padding != config.window.dynamic_padding
|
||||
{
|
||||
self.display.pending_update.dirty = true;
|
||||
}
|
||||
|
||||
// Live title reload.
|
||||
if !config.ui_config.window.dynamic_title
|
||||
|| old_config.ui_config.window.title != config.ui_config.window.title
|
||||
{
|
||||
self.display.window.set_title(&config.ui_config.window.title);
|
||||
if !config.window.dynamic_title || old_config.window.title != config.window.title {
|
||||
self.display.window.set_title(&config.window.title);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
self.display.window.set_wayland_theme(&config.ui_config.colors);
|
||||
self.display.window.set_wayland_theme(&config.colors);
|
||||
|
||||
// Set subpixel anti-aliasing.
|
||||
#[cfg(target_os = "macos")]
|
||||
crossfont::set_font_smoothing(config.ui_config.font.use_thin_strokes);
|
||||
crossfont::set_font_smoothing(config.font.use_thin_strokes);
|
||||
|
||||
// Disable shadows for transparent windows on macOS.
|
||||
#[cfg(target_os = "macos")]
|
||||
self.display.window.set_has_shadow(config.ui_config.window_opacity() >= 1.0);
|
||||
self.display.window.set_has_shadow(config.window_opacity() >= 1.0);
|
||||
|
||||
// Update hint keys.
|
||||
self.display.hint_state.update_alphabet(config.ui_config.hints.alphabet());
|
||||
self.display.hint_state.update_alphabet(config.hints.alphabet());
|
||||
|
||||
// Update cursor blinking.
|
||||
let event = Event::new(TerminalEvent::CursorBlinkingChange.into(), None);
|
||||
|
@ -195,7 +203,7 @@ impl WindowContext {
|
|||
&mut self,
|
||||
event_loop: &EventLoopWindowTarget<Event>,
|
||||
event_proxy: &EventLoopProxy<Event>,
|
||||
config: &mut Config,
|
||||
config: &mut UiConfig,
|
||||
clipboard: &mut Clipboard,
|
||||
scheduler: &mut Scheduler,
|
||||
event: GlutinEvent<'_, Event>,
|
||||
|
@ -334,7 +342,7 @@ impl WindowContext {
|
|||
message_buffer: &MessageBuffer,
|
||||
search_state: &SearchState,
|
||||
old_is_searching: bool,
|
||||
config: &Config,
|
||||
config: &UiConfig,
|
||||
) {
|
||||
// Compute cursor positions before resize.
|
||||
let num_lines = terminal.screen_lines();
|
||||
|
|
|
@ -15,37 +15,43 @@ pub use crate::config::scrolling::Scrolling;
|
|||
pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
|
||||
const MIN_BLINK_INTERVAL: u64 = 10;
|
||||
|
||||
pub type MockConfig = Config<HashMap<String, serde_yaml::Value>>;
|
||||
|
||||
/// Top-level config type.
|
||||
#[derive(ConfigDeserialize, Debug, PartialEq, Default)]
|
||||
pub struct Config<T> {
|
||||
pub struct Config {
|
||||
/// TERM env variable.
|
||||
pub env: HashMap<String, String>,
|
||||
|
||||
pub selection: Selection,
|
||||
|
||||
/// Path to a shell program to run on startup.
|
||||
pub shell: Option<Program>,
|
||||
|
||||
/// How much scrolling history to keep.
|
||||
pub scrolling: Scrolling,
|
||||
|
||||
/// Cursor configuration.
|
||||
pub cursor: Cursor,
|
||||
|
||||
#[config(flatten)]
|
||||
pub pty_config: PtyConfig,
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Default)]
|
||||
pub struct PtyConfig {
|
||||
/// Path to a shell program to run on startup.
|
||||
pub shell: Option<Program>,
|
||||
|
||||
/// Shell startup directory.
|
||||
pub working_directory: Option<PathBuf>,
|
||||
|
||||
/// Additional configuration options not directly required by the terminal.
|
||||
#[config(flatten)]
|
||||
pub ui_config: T,
|
||||
|
||||
/// Remain open after child process exits.
|
||||
#[config(skip)]
|
||||
pub hold: bool,
|
||||
}
|
||||
|
||||
impl PtyConfig {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Selection {
|
||||
pub semantic_escape_chars: String,
|
||||
|
|
|
@ -394,13 +394,13 @@ impl Selection {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::config::MockConfig;
|
||||
use crate::config::Config;
|
||||
use crate::index::{Column, Point, Side};
|
||||
use crate::term::{SizeInfo, Term};
|
||||
|
||||
fn term(height: usize, width: usize) -> Term<()> {
|
||||
let size = SizeInfo::new(width as f32, height as f32, 1.0, 1.0, 0.0, 0.0, false);
|
||||
Term::new(&MockConfig::default(), size, ())
|
||||
Term::new(&Config::default(), size, ())
|
||||
}
|
||||
|
||||
/// Test case of single cell selection.
|
||||
|
|
|
@ -288,7 +288,7 @@ impl<T> Term<T> {
|
|||
self.vi_mode_recompute_selection();
|
||||
}
|
||||
|
||||
pub fn new<C>(config: &Config<C>, size: SizeInfo, event_proxy: T) -> Term<T> {
|
||||
pub fn new(config: &Config, size: SizeInfo, event_proxy: T) -> Term<T> {
|
||||
let num_cols = size.columns;
|
||||
let num_lines = size.screen_lines;
|
||||
|
||||
|
@ -323,7 +323,7 @@ impl<T> Term<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_config<C>(&mut self, config: &Config<C>)
|
||||
pub fn update_config(&mut self, config: &Config)
|
||||
where
|
||||
T: EventListener,
|
||||
{
|
||||
|
@ -1903,7 +1903,7 @@ pub mod test {
|
|||
|
||||
// Create terminal with the appropriate dimensions.
|
||||
let size = SizeInfo::new(num_cols as f32, lines.len() as f32, 1., 1., 0., 0., false);
|
||||
let mut term = Term::new(&Config::<()>::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Fill terminal with content.
|
||||
for (line, text) in lines.iter().enumerate() {
|
||||
|
@ -1938,7 +1938,7 @@ mod tests {
|
|||
use std::mem;
|
||||
|
||||
use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
|
||||
use crate::config::MockConfig;
|
||||
use crate::config::Config;
|
||||
use crate::grid::{Grid, Scroll};
|
||||
use crate::index::{Column, Point, Side};
|
||||
use crate::selection::{Selection, SelectionType};
|
||||
|
@ -1947,7 +1947,7 @@ mod tests {
|
|||
#[test]
|
||||
fn scroll_display_page_up() {
|
||||
let size = SizeInfo::new(5., 10., 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Create 11 lines of scrollback.
|
||||
for _ in 0..20 {
|
||||
|
@ -1973,7 +1973,7 @@ mod tests {
|
|||
#[test]
|
||||
fn scroll_display_page_down() {
|
||||
let size = SizeInfo::new(5., 10., 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Create 11 lines of scrollback.
|
||||
for _ in 0..20 {
|
||||
|
@ -2003,7 +2003,7 @@ mod tests {
|
|||
#[test]
|
||||
fn semantic_selection_works() {
|
||||
let size = SizeInfo::new(5., 3., 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
let mut grid: Grid<Cell> = Grid::new(3, 5, 0);
|
||||
for i in 0..5 {
|
||||
for j in 0..2 {
|
||||
|
@ -2051,7 +2051,7 @@ mod tests {
|
|||
#[test]
|
||||
fn line_selection_works() {
|
||||
let size = SizeInfo::new(5., 1., 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
let mut grid: Grid<Cell> = Grid::new(1, 5, 0);
|
||||
for i in 0..5 {
|
||||
grid[Line(0)][Column(i)].c = 'a';
|
||||
|
@ -2072,7 +2072,7 @@ mod tests {
|
|||
#[test]
|
||||
fn selecting_empty_line() {
|
||||
let size = SizeInfo::new(3.0, 3.0, 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
let mut grid: Grid<Cell> = Grid::new(3, 3, 0);
|
||||
for l in 0..3 {
|
||||
if l != 1 {
|
||||
|
@ -2110,7 +2110,7 @@ mod tests {
|
|||
#[test]
|
||||
fn input_line_drawing_character() {
|
||||
let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
let cursor = Point::new(Line(0), Column(0));
|
||||
term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
|
||||
term.input('a');
|
||||
|
@ -2121,7 +2121,7 @@ mod tests {
|
|||
#[test]
|
||||
fn clear_saved_lines() {
|
||||
let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Add one line of scrollback.
|
||||
term.grid.scroll_up(&(Line(0)..Line(1)), 1);
|
||||
|
@ -2143,7 +2143,7 @@ mod tests {
|
|||
#[test]
|
||||
fn grow_lines_updates_active_cursor_pos() {
|
||||
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2163,7 +2163,7 @@ mod tests {
|
|||
#[test]
|
||||
fn grow_lines_updates_inactive_cursor_pos() {
|
||||
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2189,7 +2189,7 @@ mod tests {
|
|||
#[test]
|
||||
fn shrink_lines_updates_active_cursor_pos() {
|
||||
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2209,7 +2209,7 @@ mod tests {
|
|||
#[test]
|
||||
fn shrink_lines_updates_inactive_cursor_pos() {
|
||||
let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Create 10 lines of scrollback.
|
||||
for _ in 0..19 {
|
||||
|
@ -2235,7 +2235,7 @@ mod tests {
|
|||
#[test]
|
||||
fn window_title() {
|
||||
let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false);
|
||||
let mut term = Term::new(&MockConfig::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
|
||||
// Title None by default.
|
||||
assert_eq!(term.title, None);
|
||||
|
|
|
@ -785,7 +785,7 @@ mod tests {
|
|||
#[test]
|
||||
fn wide_without_spacer() {
|
||||
let size = SizeInfo::new(2., 2., 1., 1., 0., 0., false);
|
||||
let mut term = Term::new(&Config::<()>::default(), size, ());
|
||||
let mut term = Term::new(&Config::default(), size, ());
|
||||
term.grid[Line(0)][Column(0)].c = 'x';
|
||||
term.grid[Line(0)][Column(1)].c = '字';
|
||||
term.grid[Line(0)][Column(1)].flags = Flags::WIDE_CHAR;
|
||||
|
|
|
@ -60,7 +60,7 @@ pub trait EventedPty: EventedReadWrite {
|
|||
}
|
||||
|
||||
/// Setup environment variables.
|
||||
pub fn setup_env<C>(config: &Config<C>) {
|
||||
pub fn setup_env(config: &Config) {
|
||||
// Default to 'alacritty' terminfo if it is available, otherwise
|
||||
// default to 'xterm-256color'. May be overridden by user's config
|
||||
// below.
|
||||
|
|
|
@ -5,12 +5,13 @@ use std::borrow::Cow;
|
|||
use std::env;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
|
||||
use std::{io, ptr};
|
||||
|
||||
use libc::{self, c_int, pid_t, winsize, TIOCSCTTY};
|
||||
use log::error;
|
||||
|
@ -21,7 +22,7 @@ use nix::sys::termios::{self, InputFlags, SetArg};
|
|||
use signal_hook::consts as sigconsts;
|
||||
use signal_hook_mio::v0_6::Signals;
|
||||
|
||||
use crate::config::{Config, Program};
|
||||
use crate::config::{Program, PtyConfig};
|
||||
use crate::event::OnResize;
|
||||
use crate::grid::Dimensions;
|
||||
use crate::term::SizeInfo;
|
||||
|
@ -73,7 +74,7 @@ fn set_controlling_terminal(fd: c_int) {
|
|||
};
|
||||
|
||||
if res < 0 {
|
||||
die!("ioctl TIOCSCTTY failed: {}", io::Error::last_os_error());
|
||||
die!("ioctl TIOCSCTTY failed: {}", Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +144,7 @@ fn default_shell(pw: &Passwd<'_>) -> Program {
|
|||
}
|
||||
|
||||
/// Create a new TTY and return a handle to interact with it.
|
||||
pub fn new<C>(config: &Config<C>, size: &SizeInfo, window_id: Option<usize>) -> Pty {
|
||||
pub fn new(config: &PtyConfig, size: &SizeInfo, window_id: Option<usize>) -> Result<Pty> {
|
||||
let (master, slave) = make_pty(size.to_winsize());
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
|
@ -192,7 +193,7 @@ pub fn new<C>(config: &Config<C>, size: &SizeInfo, window_id: Option<usize>) ->
|
|||
// Create a new process group.
|
||||
let err = libc::setsid();
|
||||
if err == -1 {
|
||||
die!("Failed to set session id: {}", io::Error::last_os_error());
|
||||
return Err(Error::new(ErrorKind::Other, "Failed to set session id"));
|
||||
}
|
||||
|
||||
set_controlling_terminal(slave);
|
||||
|
@ -240,9 +241,12 @@ pub fn new<C>(config: &Config<C>, size: &SizeInfo, window_id: Option<usize>) ->
|
|||
signals_token: mio::Token::from(0),
|
||||
};
|
||||
pty.on_resize(size);
|
||||
pty
|
||||
Ok(pty)
|
||||
},
|
||||
Err(err) => die!("Failed to spawn command '{}': {}", shell.program(), err),
|
||||
Err(err) => Err(Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!("Failed to spawn command '{}': {}", shell.program(), err),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +271,7 @@ impl EventedReadWrite for Pty {
|
|||
token: &mut dyn Iterator<Item = mio::Token>,
|
||||
interest: mio::Ready,
|
||||
poll_opts: mio::PollOpt,
|
||||
) -> io::Result<()> {
|
||||
) -> Result<()> {
|
||||
self.token = token.next().unwrap();
|
||||
poll.register(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
|
||||
|
||||
|
@ -286,7 +290,7 @@ impl EventedReadWrite for Pty {
|
|||
poll: &mio::Poll,
|
||||
interest: mio::Ready,
|
||||
poll_opts: mio::PollOpt,
|
||||
) -> io::Result<()> {
|
||||
) -> Result<()> {
|
||||
poll.reregister(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
|
||||
|
||||
poll.reregister(
|
||||
|
@ -298,7 +302,7 @@ impl EventedReadWrite for Pty {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn deregister(&mut self, poll: &mio::Poll) -> io::Result<()> {
|
||||
fn deregister(&mut self, poll: &mio::Poll) -> Result<()> {
|
||||
poll.deregister(&EventedFd(&self.fd.as_raw_fd()))?;
|
||||
poll.deregister(&self.signals)
|
||||
}
|
||||
|
@ -360,7 +364,7 @@ impl OnResize for Pty {
|
|||
let res = unsafe { libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _) };
|
||||
|
||||
if res < 0 {
|
||||
die!("ioctl TIOCSWINSZ failed: {}", io::Error::last_os_error());
|
||||
die!("ioctl TIOCSWINSZ failed: {}", Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use winapi::um::processthreadsapi::{
|
|||
use winapi::um::winbase::{EXTENDED_STARTUPINFO_PRESENT, STARTF_USESTDHANDLES, STARTUPINFOEXW};
|
||||
use winapi::um::wincontypes::{COORD, HPCON};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::config::PtyConfig;
|
||||
use crate::event::OnResize;
|
||||
use crate::grid::Dimensions;
|
||||
use crate::term::SizeInfo;
|
||||
|
@ -40,7 +40,7 @@ impl Drop for Conpty {
|
|||
// The ConPTY handle can be sent between threads.
|
||||
unsafe impl Send for Conpty {}
|
||||
|
||||
pub fn new<C>(config: &Config<C>, size: &SizeInfo) -> Option<Pty> {
|
||||
pub fn new(config: &PtyConfig, size: &SizeInfo) -> Option<Pty> {
|
||||
let mut pty_handle = 0 as HPCON;
|
||||
|
||||
// Passing 0 as the size parameter allows the "system default" buffer
|
||||
|
@ -136,7 +136,7 @@ pub fn new<C>(config: &Config<C>, size: &SizeInfo) -> Option<Pty> {
|
|||
}
|
||||
}
|
||||
|
||||
let cmdline = win32_string(&cmdline(&config));
|
||||
let cmdline = win32_string(&cmdline(config));
|
||||
let cwd = config.working_directory.as_ref().map(win32_string);
|
||||
|
||||
let mut proc_info: PROCESS_INFORMATION = Default::default();
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::io::{self, Error, ErrorKind, Result};
|
||||
use std::iter::once;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::sync::mpsc::TryRecvError;
|
||||
|
||||
use crate::config::{Config, Program};
|
||||
use crate::config::{Program, PtyConfig};
|
||||
use crate::event::OnResize;
|
||||
use crate::term::SizeInfo;
|
||||
use crate::tty::windows::child::ChildExitWatcher;
|
||||
|
@ -28,8 +28,8 @@ pub struct Pty {
|
|||
child_watcher: ChildExitWatcher,
|
||||
}
|
||||
|
||||
pub fn new<C>(config: &Config<C>, size: &SizeInfo, _window_id: Option<usize>) -> Pty {
|
||||
conpty::new(config, size).expect("Failed to create ConPTY backend")
|
||||
pub fn new(config: &PtyConfig, size: &SizeInfo, _window_id: Option<usize>) -> Result<Pty> {
|
||||
conpty::new(config, size).ok_or_else(|| Error::new(ErrorKind::Other, "failed to spawn conpty"))
|
||||
}
|
||||
|
||||
impl Pty {
|
||||
|
@ -165,11 +165,11 @@ impl OnResize for Pty {
|
|||
}
|
||||
}
|
||||
|
||||
fn cmdline<C>(config: &Config<C>) -> String {
|
||||
fn cmdline(config: &PtyConfig) -> String {
|
||||
let default_shell = Program::Just("powershell".to_owned());
|
||||
let shell = config.shell.as_ref().unwrap_or(&default_shell);
|
||||
|
||||
once(shell.program().as_ref())
|
||||
once(shell.program())
|
||||
.chain(shell.args().iter().map(|a| a.as_ref()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
|
|
|
@ -379,13 +379,13 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
use crate::ansi::Handler;
|
||||
use crate::config::MockConfig;
|
||||
use crate::config::Config;
|
||||
use crate::index::{Column, Line};
|
||||
use crate::term::{SizeInfo, Term};
|
||||
|
||||
fn term() -> Term<()> {
|
||||
let size = SizeInfo::new(20., 20., 1.0, 1.0, 0.0, 0.0, false);
|
||||
Term::new(&MockConfig::default(), size, ())
|
||||
Term::new(&Config::default(), size, ())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::io::Read;
|
|||
use std::path::Path;
|
||||
|
||||
use alacritty_terminal::ansi;
|
||||
use alacritty_terminal::config::MockConfig;
|
||||
use alacritty_terminal::config::Config;
|
||||
use alacritty_terminal::event::{Event, EventListener};
|
||||
use alacritty_terminal::grid::{Dimensions, Grid};
|
||||
use alacritty_terminal::index::{Column, Line};
|
||||
|
@ -101,7 +101,7 @@ fn ref_test(dir: &Path) {
|
|||
let grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
|
||||
let ref_config: RefConfig = json::from_str(&serialized_cfg).unwrap();
|
||||
|
||||
let mut config = MockConfig::default();
|
||||
let mut config = Config::default();
|
||||
config.scrolling.set_history(ref_config.history_size);
|
||||
|
||||
let mut terminal = Term::new(&config, size, Mock);
|
||||
|
|
|
@ -2,18 +2,28 @@
|
|||
.SH NAME
|
||||
alacritty-msg \- Send messages to Alacritty
|
||||
.SH "SYNOPSIS"
|
||||
alacritty msg [OPTIONS] [MESSAGES]
|
||||
alacritty msg [OPTIONS] <MESSAGE> [MESSAGE_OPTIONS]
|
||||
.SH DESCRIPTION
|
||||
This command communicates with running Alacritty instances through a socket,
|
||||
making it possible to control Alacritty without directly accessing it.
|
||||
.SH "OPTIONS"
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-socket\fR <socket>
|
||||
Path for IPC socket creation
|
||||
.SH "MESSAGES"
|
||||
.TP
|
||||
\fBcreate-window\fR
|
||||
Create a new window in the same Alacritty process
|
||||
.TP
|
||||
.SH "\tOPTIONS"
|
||||
.RS 12
|
||||
\fB\-\-hold\fR
|
||||
Remain open after child process exits
|
||||
|
||||
\fB\-\-working\-directory\fR <working\-directory>
|
||||
Start the shell in the specified working directory
|
||||
|
||||
\fB\-e\fR, \fB\-\-command\fR <command>...
|
||||
Command and args to execute (must be last argument)
|
||||
.RE
|
||||
.SH "SEE ALSO"
|
||||
See the alacritty github repository at https://github.com/alacritty/alacritty for the full documentation.
|
||||
.SH "BUGS"
|
||||
|
|
|
@ -19,18 +19,18 @@ _alacritty() {
|
|||
'--title=[Defines the window title \[default: Alacritty\]]' \
|
||||
'--class=[Defines window class/app_id on X11/Wayland \[default: Alacritty\]]' \
|
||||
'--embed=[Defines the X11 window ID (as a decimal integer) to embed Alacritty within]' \
|
||||
'--working-directory=[Start the shell in the specified working directory]' \
|
||||
'--config-file=[Specify alternative configuration file \[default: $XDG_CONFIG_HOME/alacritty/alacritty.yml\]]' \
|
||||
'--socket=[Path for IPC socket creation]' \
|
||||
'*-e+[Command and args to execute (must be last argument)]' \
|
||||
'*--command=[Command and args to execute (must be last argument)]' \
|
||||
'*-o+[Override configuration file options \[example: cursor.style=Beam\]]' \
|
||||
'*--option=[Override configuration file options \[example: cursor.style=Beam\]]' \
|
||||
'--working-directory=[Start the shell in the specified working directory]' \
|
||||
'*-e+[Command and args to execute (must be last argument)]' \
|
||||
'*--command=[Command and args to execute (must be last argument)]' \
|
||||
'--print-events[Print all events to stdout]' \
|
||||
'--ref-test[Generates ref test]' \
|
||||
'--hold[Remain open after child process exits]' \
|
||||
'(-v)*-q[Reduces the level of verbosity (the min level is -qq)]' \
|
||||
'(-q)*-v[Increases the level of verbosity (the max level is -vvv)]' \
|
||||
'--hold[Remain open after child process exit]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
|
@ -63,6 +63,10 @@ case $state in
|
|||
case $line[1] in
|
||||
(create-window)
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--working-directory=[Start the shell in the specified working directory]' \
|
||||
'*-e+[Command and args to execute (must be last argument)]' \
|
||||
'*--command=[Command and args to execute (must be last argument)]' \
|
||||
'--hold[Remain open after child process exit]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
|
|
|
@ -29,7 +29,7 @@ _alacritty() {
|
|||
|
||||
case "${cmd}" in
|
||||
alacritty)
|
||||
opts=" -q -v -h -V -t -e -o --print-events --ref-test --hold --help --version --title --class --embed --working-directory --config-file --socket --command --option msg help"
|
||||
opts=" -q -v -h -V -t -o -e --print-events --ref-test --hold --help --version --title --class --embed --config-file --socket --option --working-directory --command msg help"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
@ -52,10 +52,6 @@ _alacritty() {
|
|||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--working-directory)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--config-file)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
|
@ -64,14 +60,6 @@ _alacritty() {
|
|||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--command)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
-e)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--option)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
|
@ -80,6 +68,18 @@ _alacritty() {
|
|||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--working-directory)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--command)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
-e)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
|
@ -127,13 +127,25 @@ _alacritty() {
|
|||
return 0
|
||||
;;
|
||||
alacritty__msg__create__window)
|
||||
opts=" -h -V --help --version "
|
||||
opts=" -h -V -e --hold --help --version --working-directory --command "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
--working-directory)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--command)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
-e)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
complete -c alacritty -n "__fish_use_subcommand" -s t -l title -d 'Defines the window title [default: Alacritty]'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l class -d 'Defines window class/app_id on X11/Wayland [default: Alacritty]'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l embed -d 'Defines the X11 window ID (as a decimal integer) to embed Alacritty within'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l working-directory -d 'Start the shell in the specified working directory'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l config-file -d 'Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l socket -d 'Path for IPC socket creation'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -s e -l command -d 'Command and args to execute (must be last argument)'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -s o -l option -d 'Override configuration file options [example: cursor.style=Beam]'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l working-directory -d 'Start the shell in the specified working directory'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -s e -l command -d 'Command and args to execute (must be last argument)'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l print-events -d 'Print all events to stdout'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l ref-test -d 'Generates ref test'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l hold -d 'Remain open after child process exits'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -s q -d 'Reduces the level of verbosity (the min level is -qq)'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -s v -d 'Increases the level of verbosity (the max level is -vvv)'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -l hold -d 'Remain open after child process exit'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
|
||||
complete -c alacritty -n "__fish_use_subcommand" -f -a "msg" -d 'Available socket messages'
|
||||
|
@ -20,6 +20,9 @@ complete -c alacritty -n "__fish_seen_subcommand_from msg" -s h -l help -d 'Prin
|
|||
complete -c alacritty -n "__fish_seen_subcommand_from msg" -s V -l version -d 'Prints version information'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from msg" -f -a "create-window" -d 'Create a new window in the same Alacritty process'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from msg" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from create-window" -l working-directory -d 'Start the shell in the specified working directory'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from create-window" -s e -l command -d 'Command and args to execute (must be last argument)'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from create-window" -l hold -d 'Remain open after child process exit'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from create-window" -s h -l help -d 'Prints help information'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from create-window" -s V -l version -d 'Prints version information'
|
||||
complete -c alacritty -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information'
|
||||
|
|
Loading…
Add table
Reference in a new issue