mirror of
https://github.com/alacritty/alacritty.git
synced 2025-04-21 18:02:37 -04:00
Compare commits
21 commits
alacritty_
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a0c4dfe962 | ||
![]() |
d716fe4e83 | ||
![]() |
15f1278d69 | ||
![]() |
5a68e98db0 | ||
![]() |
03c2907b44 | ||
![]() |
be911fead8 | ||
![]() |
6fefa78eaf | ||
![]() |
3c7a323ef5 | ||
![]() |
441c9c6eb3 | ||
![]() |
463a867984 | ||
![]() |
5b189bca68 | ||
![]() |
5e78d20c70 | ||
![]() |
c9c41e637a | ||
![]() |
bc3b7a2c1f | ||
![]() |
05368ea6a7 | ||
![]() |
2290afff02 | ||
![]() |
cd884c984b | ||
![]() |
28910e3adc | ||
![]() |
8ab406d3fd | ||
![]() |
8833551b0d | ||
![]() |
a26174890e |
28 changed files with 347 additions and 205 deletions
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -8,7 +8,32 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
Notable changes to the `alacritty_terminal` crate are documented in its
|
||||
[CHANGELOG](./alacritty_terminal/CHANGELOG.md).
|
||||
|
||||
## 0.15.0-dev
|
||||
## 0.16.0-dev
|
||||
|
||||
### Changed
|
||||
|
||||
- Hide login message if `~/.hushlogin` is present
|
||||
|
||||
### Fixed
|
||||
|
||||
- Crash when OpenGL context resets
|
||||
- Modifier keys clearing selection with kitty keyboard protocol enabled
|
||||
- `glyph_offset.y` not applied to strikeout
|
||||
|
||||
## 0.15.1
|
||||
|
||||
### Changed
|
||||
|
||||
- Error out when socket fails to create with `--daemon`
|
||||
- Default URL hints now stop before backslashes
|
||||
|
||||
### Fixed
|
||||
|
||||
- Modifiers being out of sync for fast/synthetic input on X11
|
||||
- Child process creation failing while inside a deleted directory
|
||||
- Shifted key reported without a shift when using kitty keyboard protocol
|
||||
|
||||
## 0.15.0
|
||||
|
||||
### Added
|
||||
|
||||
|
@ -32,6 +57,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its
|
|||
- First daemon mode window ignoring window options passed through CLI
|
||||
- Report of Enter/Tab/Backspace in kitty keyboard's report event types mode
|
||||
- Crash when pressing certain modifier keys on macOS 15+
|
||||
- Cut off wide characters in preedit string
|
||||
|
||||
## 0.14.0
|
||||
|
||||
|
|
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -32,7 +32,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "alacritty"
|
||||
version = "0.15.0-dev"
|
||||
version = "0.16.0-dev"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"alacritty_config",
|
||||
|
@ -93,7 +93,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "alacritty_terminal"
|
||||
version = "0.24.2-dev"
|
||||
version = "0.25.1-dev"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags 2.6.0",
|
||||
|
@ -529,9 +529,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.13"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
@ -814,9 +814,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glutin"
|
||||
version = "0.32.1"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec69412a0bf07ea7607e638b415447857a808846c2b685a43c8aa18bc6d5e499"
|
||||
checksum = "03642b8b0cce622392deb0ee3e88511f75df2daac806102597905c3ea1974848"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg_aliases",
|
||||
|
@ -839,9 +839,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glutin_egl_sys"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827"
|
||||
checksum = "4c4680ba6195f424febdc3ba46e7a42a0e58743f2edb115297b86d7f8ecc02d2"
|
||||
dependencies = [
|
||||
"gl_generator",
|
||||
"windows-sys 0.52.0",
|
||||
|
@ -849,9 +849,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glutin_glx_sys"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63"
|
||||
checksum = "8a7bb2938045a88b612499fbcba375a77198e01306f52272e692f8c1f3751185"
|
||||
dependencies = [
|
||||
"gl_generator",
|
||||
"x11-dl",
|
||||
|
@ -859,9 +859,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glutin_wgl_sys"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c"
|
||||
checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e"
|
||||
dependencies = [
|
||||
"gl_generator",
|
||||
]
|
||||
|
@ -2045,26 +2045,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.13.1"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a0b683b20ef64071ff03745b14391751f6beab06a54347885459b77a3f2caa5"
|
||||
checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 2.6.0",
|
||||
"cursor-icon",
|
||||
"log",
|
||||
"memchr",
|
||||
"serde",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2526,9 +2516,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.30.8"
|
||||
version = "0.30.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d74280aabb958072864bff6cfbcf9025cf8bfacdde5e32b5e12920ef703b0f"
|
||||
checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"android-activity",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "alacritty"
|
||||
version = "0.15.0-dev"
|
||||
version = "0.16.0-dev"
|
||||
authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"]
|
||||
license = "Apache-2.0"
|
||||
description = "A fast, cross-platform, OpenGL terminal emulator"
|
||||
|
@ -12,7 +12,7 @@ rust-version = "1.74.0"
|
|||
|
||||
[dependencies.alacritty_terminal]
|
||||
path = "../alacritty_terminal"
|
||||
version = "0.24.2-dev"
|
||||
version = "0.25.1-dev"
|
||||
|
||||
[dependencies.alacritty_config_derive]
|
||||
path = "../alacritty_config_derive"
|
||||
|
@ -28,7 +28,7 @@ bitflags = "2.2.1"
|
|||
clap = { version = "4.2.7", features = ["derive", "env"] }
|
||||
copypasta = { version = "0.10.1", default-features = false }
|
||||
crossfont = "0.8.0"
|
||||
glutin = { version = "0.32.0", default-features = false, features = ["egl", "wgl"] }
|
||||
glutin = { version = "0.32.2", default-features = false, features = ["egl", "wgl"] }
|
||||
home = "0.5.5"
|
||||
libc = "0.2"
|
||||
log = { version = "0.4", features = ["std", "serde"] }
|
||||
|
@ -41,7 +41,7 @@ tempfile = "3.12.0"
|
|||
toml = "0.8.2"
|
||||
toml_edit = "0.22.21"
|
||||
unicode-width = "0.1"
|
||||
winit = { version = "0.30.8", default-features = false, features = ["rwh_06", "serde"] }
|
||||
winit = { version = "0.30.9", default-features = false, features = ["rwh_06", "serde"] }
|
||||
|
||||
[build-dependencies]
|
||||
gl_generator = "0.14.0"
|
||||
|
|
|
@ -17,6 +17,7 @@ fn main() {
|
|||
|
||||
Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, [
|
||||
"GL_ARB_blend_func_extended",
|
||||
"GL_KHR_robustness",
|
||||
"GL_KHR_debug",
|
||||
])
|
||||
.write_bindings(GlobalGenerator, &mut file)
|
||||
|
|
|
@ -189,7 +189,7 @@ impl TerminalOptions {
|
|||
pty_config.shell = Some(command.into());
|
||||
}
|
||||
|
||||
pty_config.hold |= self.hold;
|
||||
pty_config.drain_on_exit |= self.hold;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ impl From<TerminalOptions> for PtyOptions {
|
|||
PtyOptions {
|
||||
working_directory: options.working_directory.take(),
|
||||
shell: options.command().map(Into::into),
|
||||
hold: options.hold,
|
||||
drain_on_exit: options.hold,
|
||||
env: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ use crate::config::LOG_TARGET_CONFIG;
|
|||
/// Regex used for the default URL hint.
|
||||
#[rustfmt::skip]
|
||||
const URL_REGEX: &str = "(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file:|git://|ssh:|ftp://)\
|
||||
[^\u{0000}-\u{001F}\u{007F}-\u{009F}<>\"\\s{-}\\^⟨⟩`]+";
|
||||
[^\u{0000}-\u{001F}\u{007F}-\u{009F}<>\"\\s{-}\\^⟨⟩`\\\\]+";
|
||||
|
||||
#[derive(ConfigDeserialize, Default, Clone, Debug, PartialEq)]
|
||||
pub struct UiConfig {
|
||||
|
@ -130,7 +130,7 @@ impl UiConfig {
|
|||
let shell = self.terminal.shell.clone().or_else(|| self.shell.clone()).map(Into::into);
|
||||
let working_directory =
|
||||
self.working_directory.clone().or_else(|| self.general.working_directory.clone());
|
||||
PtyOptions { working_directory, shell, hold: false, env: HashMap::new() }
|
||||
PtyOptions { working_directory, shell, drain_on_exit: false, env: HashMap::new() }
|
||||
}
|
||||
|
||||
/// Generate key bindings for all keyboard hints.
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::process::{Command, Stdio};
|
|||
#[rustfmt::skip]
|
||||
#[cfg(not(windows))]
|
||||
use {
|
||||
std::env,
|
||||
std::error::Error,
|
||||
std::os::unix::process::CommandExt,
|
||||
std::os::unix::io::RawFd,
|
||||
|
@ -58,18 +59,22 @@ where
|
|||
{
|
||||
let mut command = Command::new(program);
|
||||
command.args(args).stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
|
||||
if let Ok(cwd) = foreground_process_path(master_fd, shell_pid) {
|
||||
command.current_dir(cwd);
|
||||
}
|
||||
|
||||
let working_directory = foreground_process_path(master_fd, shell_pid).ok();
|
||||
unsafe {
|
||||
command
|
||||
.pre_exec(|| {
|
||||
.pre_exec(move || {
|
||||
match libc::fork() {
|
||||
-1 => return Err(io::Error::last_os_error()),
|
||||
0 => (),
|
||||
_ => libc::_exit(0),
|
||||
}
|
||||
|
||||
// Copy foreground process' working directory, ignoring invalid paths.
|
||||
if let Some(working_directory) = working_directory.as_ref() {
|
||||
let _ = env::set_current_dir(working_directory);
|
||||
}
|
||||
|
||||
if libc::setsid() == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
|
|
@ -238,12 +238,12 @@ impl<'a> RenderDamageIterator<'a> {
|
|||
fn overdamage(size_info: &SizeInfo<u32>, mut rect: Rect) -> Rect {
|
||||
rect.x = (rect.x - size_info.cell_width() as i32).max(0);
|
||||
rect.width = cmp::min(
|
||||
size_info.width() as i32 - rect.x,
|
||||
(size_info.width() as i32 - rect.x).max(0),
|
||||
rect.width + 2 * size_info.cell_width() as i32,
|
||||
);
|
||||
rect.y = (rect.y - size_info.cell_height() as i32 / 2).max(0);
|
||||
rect.height = cmp::min(
|
||||
size_info.height() as i32 - rect.y,
|
||||
(size_info.height() as i32 - rect.y).max(0),
|
||||
rect.height + size_info.cell_height() as i32,
|
||||
);
|
||||
|
||||
|
@ -344,6 +344,11 @@ mod tests {
|
|||
),
|
||||
rect
|
||||
);
|
||||
|
||||
// Test out of bounds coord clamping.
|
||||
let rect = Rect::new(bound * 2, bound * 2, rect_side, rect_side);
|
||||
let rect = RenderDamageIterator::overdamage(&size_info, rect);
|
||||
assert_eq!(Rect::new(bound * 2 - cell_size, bound * 2 - cell_size / 2, 0, 0), rect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5,10 +5,13 @@ use std::cmp;
|
|||
use std::fmt::{self, Formatter};
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ops::Deref;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use glutin::config::GetGlConfig;
|
||||
use glutin::context::{NotCurrentContext, PossiblyCurrentContext};
|
||||
use glutin::display::GetGlDisplay;
|
||||
use glutin::error::ErrorKind;
|
||||
use glutin::prelude::*;
|
||||
use glutin::surface::{Surface, SwapInterval, WindowSurface};
|
||||
|
||||
|
@ -33,6 +36,7 @@ use alacritty_terminal::term::{
|
|||
};
|
||||
use alacritty_terminal::vte::ansi::{CursorShape, NamedColor};
|
||||
|
||||
use crate::config::debug::RendererPreference;
|
||||
use crate::config::font::Font;
|
||||
use crate::config::window::Dimensions;
|
||||
#[cfg(not(windows))]
|
||||
|
@ -49,7 +53,7 @@ use crate::display::window::Window;
|
|||
use crate::event::{Event, EventType, Mouse, SearchState};
|
||||
use crate::message_bar::{MessageBuffer, MessageType};
|
||||
use crate::renderer::rects::{RenderLine, RenderLines, RenderRect};
|
||||
use crate::renderer::{self, GlyphCache, Renderer};
|
||||
use crate::renderer::{self, platform, GlyphCache, Renderer};
|
||||
use crate::scheduler::{Scheduler, TimerId, Topic};
|
||||
use crate::string::{ShortenDirection, StrShortener};
|
||||
|
||||
|
@ -385,10 +389,11 @@ pub struct Display {
|
|||
hint_mouse_point: Option<Point>,
|
||||
|
||||
renderer: ManuallyDrop<Renderer>,
|
||||
renderer_preference: Option<RendererPreference>,
|
||||
|
||||
surface: ManuallyDrop<Surface<WindowSurface>>,
|
||||
|
||||
context: ManuallyDrop<Replaceable<PossiblyCurrentContext>>,
|
||||
context: ManuallyDrop<PossiblyCurrentContext>,
|
||||
|
||||
glyph_cache: GlyphCache,
|
||||
meter: Meter,
|
||||
|
@ -421,7 +426,7 @@ impl Display {
|
|||
}
|
||||
|
||||
// Create the GL surface to draw into.
|
||||
let surface = renderer::platform::create_gl_surface(
|
||||
let surface = platform::create_gl_surface(
|
||||
&gl_context,
|
||||
window.inner_size(),
|
||||
window.raw_window_handle(),
|
||||
|
@ -510,9 +515,10 @@ impl Display {
|
|||
}
|
||||
|
||||
Ok(Self {
|
||||
context: ManuallyDrop::new(Replaceable::new(context)),
|
||||
context: ManuallyDrop::new(context),
|
||||
visual_bell: VisualBell::from(&config.bell),
|
||||
renderer: ManuallyDrop::new(renderer),
|
||||
renderer_preference: config.debug.renderer,
|
||||
surface: ManuallyDrop::new(surface),
|
||||
colors: List::from(&config.colors),
|
||||
frame_timer: FrameTimer::new(),
|
||||
|
@ -538,29 +544,69 @@ impl Display {
|
|||
|
||||
#[inline]
|
||||
pub fn gl_context(&self) -> &PossiblyCurrentContext {
|
||||
self.context.get()
|
||||
&self.context
|
||||
}
|
||||
|
||||
pub fn make_not_current(&mut self) {
|
||||
if self.context.get().is_current() {
|
||||
self.context.replace_with(|context| {
|
||||
context
|
||||
.make_not_current()
|
||||
.expect("failed to disable context")
|
||||
.treat_as_possibly_current()
|
||||
});
|
||||
if self.context.is_current() {
|
||||
self.context.make_not_current_in_place().expect("failed to disable context");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_current(&self) {
|
||||
if !self.context.get().is_current() {
|
||||
self.context.make_current(&self.surface).expect("failed to make context current")
|
||||
pub fn make_current(&mut self) {
|
||||
let is_current = self.context.is_current();
|
||||
|
||||
// Attempt to make the context current if it's not.
|
||||
let context_loss = if is_current {
|
||||
self.renderer.was_context_reset()
|
||||
} else {
|
||||
match self.context.make_current(&self.surface) {
|
||||
Err(err) if err.error_kind() == ErrorKind::ContextLost => {
|
||||
info!("Context lost for window {:?}", self.window.id());
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
|
||||
if !context_loss {
|
||||
return;
|
||||
}
|
||||
|
||||
let gl_display = self.context.display();
|
||||
let gl_config = self.context.config();
|
||||
let raw_window_handle = Some(self.window.raw_window_handle());
|
||||
let context = platform::create_gl_context(&gl_display, &gl_config, raw_window_handle)
|
||||
.expect("failed to recreate context.");
|
||||
|
||||
// Drop the old context and renderer.
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.renderer);
|
||||
ManuallyDrop::drop(&mut self.context);
|
||||
}
|
||||
|
||||
// Activate new context.
|
||||
let context = context.treat_as_possibly_current();
|
||||
self.context = ManuallyDrop::new(context);
|
||||
self.context.make_current(&self.surface).expect("failed to reativate context after reset.");
|
||||
|
||||
// Recreate renderer.
|
||||
let renderer = Renderer::new(&self.context, self.renderer_preference)
|
||||
.expect("failed to recreate renderer after reset");
|
||||
self.renderer = ManuallyDrop::new(renderer);
|
||||
|
||||
// Resize the renderer.
|
||||
self.renderer.resize(&self.size_info);
|
||||
|
||||
self.reset_glyph_cache();
|
||||
self.damage_tracker.frame().mark_fully_damaged();
|
||||
|
||||
debug!("Recovered window {:?} from gpu reset", self.window.id());
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) {
|
||||
#[allow(clippy::single_match)]
|
||||
let res = match (self.surface.deref(), &self.context.get()) {
|
||||
let res = match (self.surface.deref(), &self.context.deref()) {
|
||||
#[cfg(not(any(target_os = "macos", windows)))]
|
||||
(Surface::Egl(surface), PossiblyCurrentContext::Egl(context))
|
||||
if matches!(self.raw_window_handle, RawWindowHandle::Wayland(_))
|
||||
|
@ -1349,6 +1395,8 @@ impl Display {
|
|||
(&mut self.highlighted_hint, &mut self.highlighted_hint_age, true),
|
||||
(&mut self.vi_highlighted_hint, &mut self.vi_highlighted_hint_age, false),
|
||||
];
|
||||
|
||||
let num_lines = self.size_info.screen_lines();
|
||||
for (hint, hint_age, reset_mouse) in hints {
|
||||
let (start, end) = match hint {
|
||||
Some(hint) => (*hint.bounds().start(), *hint.bounds().end()),
|
||||
|
@ -1362,10 +1410,12 @@ impl Display {
|
|||
}
|
||||
|
||||
// Convert hint bounds to viewport coordinates.
|
||||
let start = term::point_to_viewport(display_offset, start).unwrap_or_default();
|
||||
let end = term::point_to_viewport(display_offset, end).unwrap_or_else(|| {
|
||||
Point::new(self.size_info.screen_lines() - 1, self.size_info.last_column())
|
||||
});
|
||||
let start = term::point_to_viewport(display_offset, start)
|
||||
.filter(|point| point.line < num_lines)
|
||||
.unwrap_or_default();
|
||||
let end = term::point_to_viewport(display_offset, end)
|
||||
.filter(|point| point.line < num_lines)
|
||||
.unwrap_or_else(|| Point::new(num_lines - 1, self.size_info.last_column()));
|
||||
|
||||
// Clear invalidated hints.
|
||||
if frame.intersects(start, end) {
|
||||
|
@ -1500,47 +1550,6 @@ pub struct RendererUpdate {
|
|||
clear_font_cache: bool,
|
||||
}
|
||||
|
||||
/// Struct for safe in-place replacement.
|
||||
///
|
||||
/// This struct allows easily replacing struct fields that provide `self -> Self` methods in-place,
|
||||
/// without having to deal with constantly unwrapping the underlying [`Option`].
|
||||
struct Replaceable<T>(Option<T>);
|
||||
|
||||
impl<T> Replaceable<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self(Some(inner))
|
||||
}
|
||||
|
||||
/// Replace the contents of the container.
|
||||
pub fn replace_with<F: FnMut(T) -> T>(&mut self, f: F) {
|
||||
self.0 = self.0.take().map(f);
|
||||
}
|
||||
|
||||
/// Get immutable access to the wrapped value.
|
||||
pub fn get(&self) -> &T {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Get mutable access to the wrapped value.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.0.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Replaceable<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Replaceable<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// The frame timer state.
|
||||
pub struct FrameTimer {
|
||||
/// Base timestamp used to compute sync points.
|
||||
|
|
|
@ -109,6 +109,9 @@ pub struct Window {
|
|||
/// Flag indicating whether redraw was requested.
|
||||
pub requested_redraw: bool,
|
||||
|
||||
/// Hold the window when terminal exits.
|
||||
pub hold: bool,
|
||||
|
||||
window: WinitWindow,
|
||||
|
||||
/// Current window title.
|
||||
|
@ -127,7 +130,7 @@ impl Window {
|
|||
event_loop: &ActiveEventLoop,
|
||||
config: &UiConfig,
|
||||
identity: &Identity,
|
||||
_options: &mut WindowOptions,
|
||||
options: &mut WindowOptions,
|
||||
#[rustfmt::skip]
|
||||
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
|
||||
x11_visual: Option<X11VisualInfo>,
|
||||
|
@ -139,7 +142,7 @@ impl Window {
|
|||
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
|
||||
x11_visual,
|
||||
#[cfg(target_os = "macos")]
|
||||
&_options.window_tabbing_id.take(),
|
||||
&options.window_tabbing_id.take(),
|
||||
);
|
||||
|
||||
if let Some(position) = config.window.position {
|
||||
|
@ -148,7 +151,7 @@ impl Window {
|
|||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", windows)))]
|
||||
if let Some(token) = _options
|
||||
if let Some(token) = options
|
||||
.activation_token
|
||||
.take()
|
||||
.map(ActivationToken::from_raw)
|
||||
|
@ -199,6 +202,7 @@ impl Window {
|
|||
let is_x11 = matches!(window.window_handle().unwrap().as_raw(), RawWindowHandle::Xlib(_));
|
||||
|
||||
Ok(Self {
|
||||
hold: options.terminal_options.hold,
|
||||
requested_redraw: false,
|
||||
title: identity.title,
|
||||
current_mouse_cursor,
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::ConfigMonitor;
|
|||
use glutin::config::GetGlConfig;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::min;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
|
@ -380,9 +381,14 @@ impl ApplicationHandler<Event> for Processor {
|
|||
},
|
||||
(EventType::Terminal(TerminalEvent::Exit), Some(window_id)) => {
|
||||
// Remove the closed terminal.
|
||||
let window_context = match self.windows.remove(window_id) {
|
||||
Some(window_context) => window_context,
|
||||
None => return,
|
||||
let window_context = match self.windows.entry(*window_id) {
|
||||
// Don't exit when terminal exits if user asked to hold the window.
|
||||
Entry::Occupied(window_context)
|
||||
if !window_context.get().display.window.hold =>
|
||||
{
|
||||
window_context.remove()
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Unschedule pending events.
|
||||
|
@ -838,9 +844,8 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
#[cfg(not(windows))]
|
||||
fn create_new_window(&mut self, #[cfg(target_os = "macos")] tabbing_id: Option<String>) {
|
||||
let mut options = WindowOptions::default();
|
||||
if let Ok(working_directory) = foreground_process_path(self.master_fd, self.shell_pid) {
|
||||
options.terminal_options.working_directory = Some(working_directory);
|
||||
}
|
||||
options.terminal_options.working_directory =
|
||||
foreground_process_path(self.master_fd, self.shell_pid).ok();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
|
@ -869,7 +874,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
|
||||
match result {
|
||||
Ok(_) => debug!("Launched {} with args {:?}", program, args),
|
||||
Err(_) => warn!("Unable to launch {} with args {:?}", program, args),
|
||||
Err(err) => warn!("Unable to launch {program} with args {args:?}: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1793,7 +1798,11 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
|||
},
|
||||
WinitEvent::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => self.ctx.terminal.exit(),
|
||||
WindowEvent::CloseRequested => {
|
||||
// User asked to close the window, so no need to hold it.
|
||||
self.ctx.window().hold = false;
|
||||
self.ctx.terminal.exit();
|
||||
},
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
let old_scale_factor =
|
||||
mem::replace(&mut self.ctx.window().scale_factor, scale_factor);
|
||||
|
|
|
@ -77,6 +77,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
let mods = if self.alt_send_esc(&key, text) { mods } else { mods & !ModifiersState::ALT };
|
||||
|
||||
let build_key_sequence = Self::should_build_sequence(&key, text, mode, mods);
|
||||
let is_modifier_key = Self::is_modifier_key(&key);
|
||||
|
||||
let bytes = if build_key_sequence {
|
||||
build_sequence(key, mods, mode)
|
||||
|
@ -92,7 +93,10 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
|
||||
// Write only if we have something to write.
|
||||
if !bytes.is_empty() {
|
||||
self.ctx.on_terminal_input_start();
|
||||
// Don't clear selection/scroll down when writing escaped modifier keys.
|
||||
if !is_modifier_key {
|
||||
self.ctx.on_terminal_input_start();
|
||||
}
|
||||
self.ctx.write_to_pty(bytes);
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +129,16 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_modifier_key(key: &KeyEvent) -> bool {
|
||||
matches!(
|
||||
key.logical_key.as_ref(),
|
||||
Key::Named(NamedKey::Shift)
|
||||
| Key::Named(NamedKey::Control)
|
||||
| Key::Named(NamedKey::Alt)
|
||||
| Key::Named(NamedKey::Super)
|
||||
)
|
||||
}
|
||||
|
||||
/// Check whether we should try to build escape sequence for the [`KeyEvent`].
|
||||
fn should_build_sequence(
|
||||
key: &KeyEvent,
|
||||
|
@ -342,18 +356,21 @@ impl SequenceBuilder {
|
|||
};
|
||||
|
||||
if character.chars().count() == 1 {
|
||||
let character = character.chars().next().unwrap();
|
||||
let base_character = character.to_lowercase().next().unwrap();
|
||||
let shift = self.modifiers.contains(SequenceModifiers::SHIFT);
|
||||
|
||||
let alternate_key_code = u32::from(character);
|
||||
let mut unicode_key_code = u32::from(base_character);
|
||||
let ch = character.chars().next().unwrap();
|
||||
let unshifted_ch = if shift { ch.to_lowercase().next().unwrap() } else { ch };
|
||||
|
||||
let alternate_key_code = u32::from(ch);
|
||||
let mut unicode_key_code = u32::from(unshifted_ch);
|
||||
|
||||
// Try to get the base for keys which change based on modifier, like `1` for `!`.
|
||||
match key.key_without_modifiers().as_ref() {
|
||||
Key::Character(unmodded) if alternate_key_code == unicode_key_code => {
|
||||
unicode_key_code = u32::from(unmodded.chars().next().unwrap_or(base_character));
|
||||
},
|
||||
_ => (),
|
||||
//
|
||||
// However it should only be performed when `SHIFT` is pressed.
|
||||
if shift && alternate_key_code == unicode_key_code {
|
||||
if let Key::Character(unmodded) = key.key_without_modifiers().as_ref() {
|
||||
unicode_key_code = u32::from(unmodded.chars().next().unwrap_or(unshifted_ch));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Base layouts are ignored, since winit doesn't expose this information
|
||||
|
|
|
@ -324,7 +324,10 @@ impl<T: EventListener> Execute<T> for Action {
|
|||
#[cfg(not(target_os = "macos"))]
|
||||
Action::Hide => ctx.window().set_visible(false),
|
||||
Action::Minimize => ctx.window().set_minimized(true),
|
||||
Action::Quit => ctx.terminal_mut().exit(),
|
||||
Action::Quit => {
|
||||
ctx.window().hold = false;
|
||||
ctx.terminal_mut().exit();
|
||||
},
|
||||
Action::IncreaseFontSize => ctx.change_font_size(FONT_SIZE_STEP),
|
||||
Action::DecreaseFontSize => ctx.change_font_size(-FONT_SIZE_STEP),
|
||||
Action::ResetFontSize => ctx.reset_font_size(),
|
||||
|
|
|
@ -19,7 +19,10 @@ use crate::event::{Event, EventType};
|
|||
const ALACRITTY_SOCKET_ENV: &str = "ALACRITTY_SOCKET";
|
||||
|
||||
/// Create an IPC socket.
|
||||
pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy<Event>) -> Option<PathBuf> {
|
||||
pub fn spawn_ipc_socket(
|
||||
options: &Options,
|
||||
event_proxy: EventLoopProxy<Event>,
|
||||
) -> IoResult<PathBuf> {
|
||||
// Create the IPC socket and export its path as env.
|
||||
|
||||
let socket_path = options.socket.clone().unwrap_or_else(|| {
|
||||
|
@ -28,13 +31,7 @@ pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy<Event>) -
|
|||
path
|
||||
});
|
||||
|
||||
let listener = match UnixListener::bind(&socket_path) {
|
||||
Ok(listener) => listener,
|
||||
Err(err) => {
|
||||
warn!("Unable to create socket: {:?}", err);
|
||||
return None;
|
||||
},
|
||||
};
|
||||
let listener = UnixListener::bind(&socket_path)?;
|
||||
|
||||
env::set_var(ALACRITTY_SOCKET_ENV, socket_path.as_os_str());
|
||||
if options.daemon {
|
||||
|
@ -80,7 +77,7 @@ pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy<Event>) -
|
|||
}
|
||||
});
|
||||
|
||||
Some(socket_path)
|
||||
Ok(socket_path)
|
||||
}
|
||||
|
||||
/// Send a message to the active Alacritty socket.
|
||||
|
|
|
@ -183,7 +183,14 @@ fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
|
|||
// Create the IPC socket listener.
|
||||
#[cfg(unix)]
|
||||
let socket_path = if config.ipc_socket() {
|
||||
ipc::spawn_ipc_socket(&options, window_event_loop.create_proxy())
|
||||
match ipc::spawn_ipc_socket(&options, window_event_loop.create_proxy()) {
|
||||
Ok(path) => Some(path),
|
||||
Err(err) if options.daemon => return Err(err.into()),
|
||||
Err(err) => {
|
||||
log::warn!("Unable to create socket: {:?}", err);
|
||||
None
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ use ahash::RandomState;
|
|||
use crossfont::Metrics;
|
||||
use glutin::context::{ContextApi, GlContext, PossiblyCurrentContext};
|
||||
use glutin::display::{GetGlDisplay, GlDisplay};
|
||||
use log::{debug, error, info, warn, LevelFilter};
|
||||
use log::{debug, info, LevelFilter};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use alacritty_terminal::index::Point;
|
||||
|
@ -97,6 +97,7 @@ enum TextRendererProvider {
|
|||
pub struct Renderer {
|
||||
text_renderer: TextRendererProvider,
|
||||
rect_renderer: RectRenderer,
|
||||
robustness: bool,
|
||||
}
|
||||
|
||||
/// Wrapper around gl::GetString with error checking and reporting.
|
||||
|
@ -144,6 +145,9 @@ impl Renderer {
|
|||
info!("Running on {renderer}");
|
||||
info!("OpenGL version {gl_version}, shader_version {shader_version}");
|
||||
|
||||
// Check if robustness is supported.
|
||||
let robustness = Self::supports_robustness();
|
||||
|
||||
let is_gles_context = matches!(context.context_api(), ContextApi::Gles(_));
|
||||
|
||||
// Use the config option to enforce a particular renderer configuration.
|
||||
|
@ -175,7 +179,7 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(Self { text_renderer, rect_renderer })
|
||||
Ok(Self { text_renderer, rect_renderer, robustness })
|
||||
}
|
||||
|
||||
pub fn draw_cells<I: Iterator<Item = RenderableCell>>(
|
||||
|
@ -206,10 +210,10 @@ impl Renderer {
|
|||
glyph_cache: &mut GlyphCache,
|
||||
) {
|
||||
let mut wide_char_spacer = false;
|
||||
let cells = string_chars.enumerate().map(|(i, character)| {
|
||||
let cells = string_chars.enumerate().filter_map(|(i, character)| {
|
||||
let flags = if wide_char_spacer {
|
||||
wide_char_spacer = false;
|
||||
Flags::WIDE_CHAR_SPACER
|
||||
return None;
|
||||
} else if character.width() == Some(2) {
|
||||
// The spacer is always following the wide char.
|
||||
wide_char_spacer = true;
|
||||
|
@ -218,7 +222,7 @@ impl Renderer {
|
|||
Flags::empty()
|
||||
};
|
||||
|
||||
RenderableCell {
|
||||
Some(RenderableCell {
|
||||
point: Point::new(point.line, point.column + i),
|
||||
character,
|
||||
extra: None,
|
||||
|
@ -227,7 +231,7 @@ impl Renderer {
|
|||
fg,
|
||||
bg,
|
||||
underline: fg,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
self.draw_cells(size_info, glyph_cache, cells);
|
||||
|
@ -281,6 +285,49 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the context reset status.
|
||||
pub fn was_context_reset(&self) -> bool {
|
||||
// If robustness is not supported, don't use its functions.
|
||||
if !self.robustness {
|
||||
return false;
|
||||
}
|
||||
|
||||
let status = unsafe { gl::GetGraphicsResetStatus() };
|
||||
if status == gl::NO_ERROR {
|
||||
false
|
||||
} else {
|
||||
let reason = match status {
|
||||
gl::GUILTY_CONTEXT_RESET_KHR => "guilty",
|
||||
gl::INNOCENT_CONTEXT_RESET_KHR => "innocent",
|
||||
gl::UNKNOWN_CONTEXT_RESET_KHR => "unknown",
|
||||
_ => "invalid",
|
||||
};
|
||||
|
||||
info!("GPU reset ({})", reason);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn supports_robustness() -> bool {
|
||||
let mut notification_strategy = 0;
|
||||
if GlExtensions::contains("GL_KHR_robustness") {
|
||||
unsafe {
|
||||
gl::GetIntegerv(gl::RESET_NOTIFICATION_STRATEGY_KHR, &mut notification_strategy);
|
||||
}
|
||||
} else {
|
||||
notification_strategy = gl::NO_RESET_NOTIFICATION_KHR as gl::types::GLint;
|
||||
}
|
||||
|
||||
if notification_strategy == gl::LOSE_CONTEXT_ON_RESET_KHR as gl::types::GLint {
|
||||
info!("GPU reset notifications are enabled");
|
||||
true
|
||||
} else {
|
||||
info!("GPU reset notifications are disabled");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(&self) {
|
||||
unsafe {
|
||||
gl::Finish();
|
||||
|
@ -349,7 +396,7 @@ impl GlExtensions {
|
|||
|
||||
extern "system" fn gl_debug_log(
|
||||
_: gl::types::GLenum,
|
||||
kind: gl::types::GLenum,
|
||||
_: gl::types::GLenum,
|
||||
_: gl::types::GLuint,
|
||||
_: gl::types::GLenum,
|
||||
_: gl::types::GLsizei,
|
||||
|
@ -357,11 +404,5 @@ extern "system" fn gl_debug_log(
|
|||
_: *mut std::os::raw::c_void,
|
||||
) {
|
||||
let msg = unsafe { CStr::from_ptr(msg).to_string_lossy() };
|
||||
match kind {
|
||||
gl::DEBUG_TYPE_ERROR | gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => {
|
||||
error!("[gl_render] {}", msg)
|
||||
},
|
||||
gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => warn!("[gl_render] {}", msg),
|
||||
_ => debug!("[gl_render] {}", msg),
|
||||
}
|
||||
debug!("[gl_render] {}", msg);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ use std::num::NonZeroU32;
|
|||
|
||||
use glutin::config::{ColorBufferType, Config, ConfigTemplateBuilder, GetGlConfig};
|
||||
use glutin::context::{
|
||||
ContextApi, ContextAttributesBuilder, GlProfile, NotCurrentContext, Version,
|
||||
ContextApi, ContextAttributesBuilder, GlProfile, NotCurrentContext, Robustness, Version,
|
||||
};
|
||||
use glutin::display::{Display, DisplayApiPreference, GetGlDisplay};
|
||||
use glutin::display::{Display, DisplayApiPreference, DisplayFeatures, GetGlDisplay};
|
||||
use glutin::error::Result as GlutinResult;
|
||||
use glutin::prelude::*;
|
||||
use glutin::surface::{Surface, SurfaceAttributesBuilder, WindowSurface};
|
||||
|
@ -110,18 +110,24 @@ pub fn create_gl_context(
|
|||
raw_window_handle: Option<RawWindowHandle>,
|
||||
) -> GlutinResult<NotCurrentContext> {
|
||||
let debug = log::max_level() >= LevelFilter::Debug;
|
||||
let mut builder = ContextAttributesBuilder::new().with_debug(debug);
|
||||
|
||||
// Try to enable robustness.
|
||||
if gl_display.supported_features().contains(DisplayFeatures::CONTEXT_ROBUSTNESS) {
|
||||
builder = builder.with_robustness(Robustness::RobustLoseContextOnReset);
|
||||
}
|
||||
|
||||
let mut profiles = [
|
||||
ContextAttributesBuilder::new()
|
||||
.with_debug(debug)
|
||||
builder
|
||||
.clone()
|
||||
.with_context_api(ContextApi::OpenGl(Some(Version::new(3, 3))))
|
||||
.build(raw_window_handle),
|
||||
// Try gles before OpenGL 2.1 as it tends to be more stable.
|
||||
ContextAttributesBuilder::new()
|
||||
.with_debug(debug)
|
||||
builder
|
||||
.clone()
|
||||
.with_context_api(ContextApi::Gles(Some(Version::new(2, 0))))
|
||||
.build(raw_window_handle),
|
||||
ContextAttributesBuilder::new()
|
||||
.with_debug(debug)
|
||||
builder
|
||||
.with_profile(GlProfile::Compatibility)
|
||||
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
|
||||
.build(raw_window_handle),
|
||||
|
|
|
@ -82,13 +82,7 @@ impl GlyphCache {
|
|||
pub fn new(mut rasterizer: Rasterizer, font: &Font) -> Result<GlyphCache, crossfont::Error> {
|
||||
let (regular, bold, italic, bold_italic) = Self::compute_font_keys(font, &mut rasterizer)?;
|
||||
|
||||
// Need to load at least one glyph for the face before calling metrics.
|
||||
// The glyph requested here ('m' at the time of writing) has no special
|
||||
// meaning.
|
||||
rasterizer.get_glyph(GlyphKey { font_key: regular, character: 'm', size: font.size() })?;
|
||||
|
||||
let metrics = rasterizer.metrics(regular, font.size())?;
|
||||
|
||||
let metrics = GlyphCache::load_font_metrics(&mut rasterizer, font, regular)?;
|
||||
Ok(Self {
|
||||
cache: Default::default(),
|
||||
rasterizer,
|
||||
|
@ -104,6 +98,22 @@ impl GlyphCache {
|
|||
})
|
||||
}
|
||||
|
||||
// Load font metrics and adjust for glyph offset.
|
||||
fn load_font_metrics(
|
||||
rasterizer: &mut Rasterizer,
|
||||
font: &Font,
|
||||
key: FontKey,
|
||||
) -> Result<Metrics, crossfont::Error> {
|
||||
// Need to load at least one glyph for the face before calling metrics.
|
||||
// The glyph requested here ('m' at the time of writing) has no special
|
||||
// meaning.
|
||||
rasterizer.get_glyph(GlyphKey { font_key: key, character: 'm', size: font.size() })?;
|
||||
|
||||
let mut metrics = rasterizer.metrics(key, font.size())?;
|
||||
metrics.strikeout_position += font.glyph_offset.y as f32;
|
||||
Ok(metrics)
|
||||
}
|
||||
|
||||
fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
|
||||
let size = self.font_size;
|
||||
|
||||
|
@ -279,12 +289,7 @@ impl GlyphCache {
|
|||
let (regular, bold, italic, bold_italic) =
|
||||
Self::compute_font_keys(font, &mut self.rasterizer)?;
|
||||
|
||||
self.rasterizer.get_glyph(GlyphKey {
|
||||
font_key: regular,
|
||||
character: 'm',
|
||||
size: font.size(),
|
||||
})?;
|
||||
let metrics = self.rasterizer.metrics(regular, font.size())?;
|
||||
let metrics = GlyphCache::load_font_metrics(&mut self.rasterizer, font, regular)?;
|
||||
|
||||
info!("Font size changed to {:?} px", font.size().as_px());
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ impl WindowContext {
|
|||
Arc::clone(&terminal),
|
||||
event_proxy.clone(),
|
||||
pty,
|
||||
pty_config.hold,
|
||||
pty_config.drain_on_exit,
|
||||
config.debug.ref_test,
|
||||
)?;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
<Package Name="Alacritty" UpgradeCode="87c21c74-dbd5-4584-89d5-46d9cd0c40a7" Language="1033" Codepage="1252" Version="0.15.0-dev" Manufacturer="Alacritty" InstallerVersion="200">
|
||||
<Package Name="Alacritty" UpgradeCode="87c21c74-dbd5-4584-89d5-46d9cd0c40a7" Language="1033" Codepage="1252" Version="0.16.0-dev" Manufacturer="Alacritty" InstallerVersion="200">
|
||||
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
||||
<Icon Id="AlacrittyIco" SourceFile=".\alacritty\windows\alacritty.ico" />
|
||||
<WixVariable Id="WixUILicenseRtf" Value=".\alacritty\windows\wix\license.rtf" />
|
||||
|
|
|
@ -8,7 +8,19 @@ sections should follow the order `Added`, `Changed`, `Deprecated`, `Fixed` and
|
|||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## 0.24.2-dev
|
||||
## 0.25.1-dev
|
||||
|
||||
### Changed
|
||||
|
||||
- Pass `-q` to `login` on macOS if `~/.hushlogin` is present
|
||||
|
||||
## 0.25.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Replaced `Options::hold` with `Options::drain_on_exit`
|
||||
|
||||
## 0.24.2
|
||||
|
||||
### Added
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "alacritty_terminal"
|
||||
version = "0.24.2-dev"
|
||||
version = "0.25.1-dev"
|
||||
authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"]
|
||||
license = "Apache-2.0"
|
||||
description = "Library for writing terminal emulators"
|
||||
|
@ -24,7 +24,7 @@ parking_lot = "0.12.0"
|
|||
polling = "3.0.0"
|
||||
regex-automata = "0.4.3"
|
||||
unicode-width = "0.1"
|
||||
vte = { version = "0.13.0", default-features = false, features = ["ansi"] }
|
||||
vte = { version = "0.15.0", default-features = false, features = ["std", "ansi"] }
|
||||
serde = { version = "1", features = ["derive", "rc"], optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
|
|
@ -17,8 +17,8 @@ use polling::{Event as PollingEvent, Events, PollMode};
|
|||
use crate::event::{self, Event, EventListener, WindowSize};
|
||||
use crate::sync::FairMutex;
|
||||
use crate::term::Term;
|
||||
use crate::vte::ansi;
|
||||
use crate::{thread, tty};
|
||||
use vte::ansi;
|
||||
|
||||
/// Max bytes to read from the PTY before forced terminal synchronization.
|
||||
pub(crate) const READ_BUFFER_SIZE: usize = 0x10_0000;
|
||||
|
@ -50,7 +50,7 @@ pub struct EventLoop<T: tty::EventedPty, U: EventListener> {
|
|||
tx: Sender<Msg>,
|
||||
terminal: Arc<FairMutex<Term<U>>>,
|
||||
event_proxy: U,
|
||||
hold: bool,
|
||||
drain_on_exit: bool,
|
||||
ref_test: bool,
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ where
|
|||
terminal: Arc<FairMutex<Term<U>>>,
|
||||
event_proxy: U,
|
||||
pty: T,
|
||||
hold: bool,
|
||||
drain_on_exit: bool,
|
||||
ref_test: bool,
|
||||
) -> io::Result<EventLoop<T, U>> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
@ -76,7 +76,7 @@ where
|
|||
rx: PeekableReceiver::new(rx),
|
||||
terminal,
|
||||
event_proxy,
|
||||
hold,
|
||||
drain_on_exit,
|
||||
ref_test,
|
||||
})
|
||||
}
|
||||
|
@ -151,9 +151,7 @@ where
|
|||
}
|
||||
|
||||
// Parse the incoming bytes.
|
||||
for byte in &buf[..unprocessed] {
|
||||
state.parser.advance(&mut **terminal, *byte);
|
||||
}
|
||||
state.parser.advance(&mut **terminal, &buf[..unprocessed]);
|
||||
|
||||
processed += unprocessed;
|
||||
unprocessed = 0;
|
||||
|
@ -263,13 +261,10 @@ where
|
|||
if let Some(code) = code {
|
||||
self.event_proxy.send_event(Event::ChildExit(code));
|
||||
}
|
||||
if self.hold {
|
||||
// With hold enabled, make sure the PTY is drained.
|
||||
if self.drain_on_exit {
|
||||
let _ = self.pty_read(&mut state, &mut buf, pipe.as_mut());
|
||||
} else {
|
||||
// Without hold, shutdown the terminal.
|
||||
self.terminal.lock().exit();
|
||||
}
|
||||
self.terminal.lock().exit();
|
||||
self.event_proxy.send_event(Event::Wakeup);
|
||||
break 'event_loop;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ pub struct Options {
|
|||
/// Shell startup directory.
|
||||
pub working_directory: Option<PathBuf>,
|
||||
|
||||
/// Remain open after child process exits.
|
||||
pub hold: bool,
|
||||
/// Drain the child process output before exiting the terminal.
|
||||
pub drain_on_exit: bool,
|
||||
|
||||
/// Extra environment variables.
|
||||
pub env: HashMap<String, String>,
|
||||
|
|
|
@ -8,6 +8,8 @@ use std::os::fd::OwnedFd;
|
|||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::os::unix::process::CommandExt;
|
||||
#[cfg(target_os = "macos")]
|
||||
use std::path::Path;
|
||||
use std::process::{Child, Command};
|
||||
use std::sync::Arc;
|
||||
use std::{env, ptr};
|
||||
|
@ -158,12 +160,12 @@ impl ShellUser {
|
|||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn default_shell_command(shell: &str, _user: &str) -> Command {
|
||||
fn default_shell_command(shell: &str, _user: &str, _home: &str) -> Command {
|
||||
Command::new(shell)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn default_shell_command(shell: &str, user: &str) -> Command {
|
||||
fn default_shell_command(shell: &str, user: &str, home: &str) -> Command {
|
||||
let shell_name = shell.rsplit('/').next().unwrap();
|
||||
|
||||
// On macOS, use the `login` command so the shell will appear as a tty session.
|
||||
|
@ -173,12 +175,20 @@ fn default_shell_command(shell: &str, user: &str) -> Command {
|
|||
// `login` normally does this itself, but `-l` disables this.
|
||||
let exec = format!("exec -a -{} {}", shell_name, shell);
|
||||
|
||||
// Since we use -l, `login` will not change directory to the user's home. However,
|
||||
// `login` only checks the current working directory for a .hushlogin file, causing
|
||||
// it to miss any in the user's home directory. We can fix this by doing the check
|
||||
// ourselves and passing `-q`
|
||||
let has_home_hushlogin = Path::new(home).join(".hushlogin").exists();
|
||||
|
||||
// -f: Bypasses authentication for the already-logged-in user.
|
||||
// -l: Skips changing directory to $HOME and prepending '-' to argv[0].
|
||||
// -p: Preserves the environment.
|
||||
// -q: Act as if `.hushlogin` exists.
|
||||
//
|
||||
// XXX: we use zsh here over sh due to `exec -a`.
|
||||
login_command.args(["-flp", user, "/bin/zsh", "-fc", &exec]);
|
||||
let flags = if has_home_hushlogin { "-qflp" } else { "-flp" };
|
||||
login_command.args([flags, user, "/bin/zsh", "-fc", &exec]);
|
||||
login_command
|
||||
}
|
||||
|
||||
|
@ -208,7 +218,7 @@ pub fn from_fd(config: &Options, window_id: u64, master: OwnedFd, slave: OwnedFd
|
|||
cmd.args(shell.args.as_slice());
|
||||
cmd
|
||||
} else {
|
||||
default_shell_command(&user.shell, &user.user)
|
||||
default_shell_command(&user.shell, &user.user, &user.home)
|
||||
};
|
||||
|
||||
// Setup child stdin/stdout/stderr as slave fd of PTY.
|
||||
|
@ -231,6 +241,7 @@ pub fn from_fd(config: &Options, window_id: u64, master: OwnedFd, slave: OwnedFd
|
|||
builder.env_remove("XDG_ACTIVATION_TOKEN");
|
||||
builder.env_remove("DESKTOP_STARTUP_ID");
|
||||
|
||||
let working_directory = config.working_directory.clone();
|
||||
unsafe {
|
||||
builder.pre_exec(move || {
|
||||
// Create a new process group.
|
||||
|
@ -239,6 +250,11 @@ pub fn from_fd(config: &Options, window_id: u64, master: OwnedFd, slave: OwnedFd
|
|||
return Err(Error::new(ErrorKind::Other, "Failed to set session id"));
|
||||
}
|
||||
|
||||
// Set working directory, ignoring invalid paths.
|
||||
if let Some(working_directory) = working_directory.as_ref() {
|
||||
let _ = env::set_current_dir(working_directory);
|
||||
}
|
||||
|
||||
set_controlling_terminal(slave_fd);
|
||||
|
||||
// No longer need slave/master fds.
|
||||
|
@ -256,11 +272,6 @@ pub fn from_fd(config: &Options, window_id: u64, master: OwnedFd, slave: OwnedFd
|
|||
});
|
||||
}
|
||||
|
||||
// Handle set working directory option.
|
||||
if let Some(dir) = &config.working_directory {
|
||||
builder.current_dir(dir);
|
||||
}
|
||||
|
||||
// Prepare signal handling before spawning child.
|
||||
let (signals, sig_id) = {
|
||||
let (sender, recv) = UnixStream::pair()?;
|
||||
|
|
|
@ -112,9 +112,7 @@ fn ref_test(dir: &Path) {
|
|||
let mut terminal = Term::new(options, &size, Mock);
|
||||
let mut parser: ansi::Processor = ansi::Processor::new();
|
||||
|
||||
for byte in recording {
|
||||
parser.advance(&mut terminal, byte);
|
||||
}
|
||||
parser.advance(&mut terminal, &recording);
|
||||
|
||||
// Truncate invisible lines from the grid.
|
||||
let mut term_grid = terminal.grid().clone();
|
||||
|
|
|
@ -740,7 +740,8 @@ post_processing = _true_++
|
|||
persist = _false_++
|
||||
mouse.enabled = _true_++
|
||||
binding = { key = _"O"_, mods = _"Control|Shift"_ }++
|
||||
regex = _"(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file:|git://|ssh:|ftp://)[^\\u0000-\\u001F\\u007F-\\u009F<>\\"\\\\s{-}\\\\^⟨⟩`]+"_
|
||||
regex =
|
||||
_"(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file:|git://|ssh:|ftp://)[^\\u0000-\\u001F\\u007F-\\u009F<>\\"\\\\s{-}\\\\^⟨⟩`\\\\\\\\]+"_
|
||||
|
||||
# KEYBOARD
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.15.0-dev</string>
|
||||
<string>0.16.0-dev</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue