diff options
| author | GNUqb114514 <GNUqb114514@outlook.com> | 2024-12-27 21:18:49 +0800 |
|---|---|---|
| committer | GNUqb114514 <GNUqb114514@outlook.com> | 2024-12-27 21:18:49 +0800 |
| commit | d8b533d96f54f274e5d3c1b542f940309a579cde (patch) | |
| tree | 7c48ff8cb851b522a6c6224f2749d6f7c64d1867 | |
| parent | 984f4382e9f51ff4290e168bb49aaff4fb4d1b64 (diff) | |
| parent | 9e9e43d209f19e368153e79c898c09ced9671971 (diff) | |
| download | r-alacritty-d8b533d96f54f274e5d3c1b542f940309a579cde.tar.gz r-alacritty-d8b533d96f54f274e5d3c1b542f940309a579cde.tar.bz2 r-alacritty-d8b533d96f54f274e5d3c1b542f940309a579cde.zip | |
Merge remote-tracking branch 'upstream/graphics' into graphics
41 files changed, 375 insertions, 213 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b3d2b46..94672511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,25 @@ 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 + +### Added + +- Config option `window.level = "AlwaysOnTop"` to force Alacritty to always be the toplevel window + +### Changed + +- Always focus new windows on macOS + +### Fixed + +- Mouse/Vi cursor hint highlighting broken on the terminal cursor line +- Hint launcher opening arbitrary text, when terminal content changed while opening +- `SemanticRight`/`SemanticLeft` vi motions breaking with wide semantic escape characters +- `alacritty migrate` crashing with recursive toml imports +- Migrating nonexistent toml import breaking the entire migration +- First daemon mode window ignoring window options passed through CLI + ## 0.14.0 ### Packaging @@ -30,6 +49,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its - Moved config option `shell` to `terminal.shell` - `ctrl+shift+u` binding to open links to `ctrl+shift+o` to avoid collisions with IMEs - Use `Beam` cursor for single char cursor inside the IME preview +- Always emit `1` for the first parameter when having modifiers in kitty keyboard protocol ### Fixed @@ -51,6 +71,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its - Invalid URL highlights after terminal scrolling - Hollow block cursor not spanning multiple chars being edited inside the IME preview - Vi inline search only working for direct key input without modifiers +- Crash when pressing certain modifier keys on macOS 15+ ## 0.13.2 @@ -32,7 +32,7 @@ dependencies = [ [[package]] name = "alacritty" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "ahash", "alacritty_config", @@ -41,7 +41,6 @@ dependencies = [ "bitflags 2.6.0", "clap", "clap_complete", - "cocoa", "copypasta", "crossfont", "dirs", @@ -53,7 +52,9 @@ dependencies = [ "log", "memoffset", "notify", - "objc", + "objc2", + "objc2-app-kit", + "objc2-foundation", "parking_lot", "png", "serde", @@ -71,7 +72,7 @@ dependencies = [ [[package]] name = "alacritty_config" -version = "0.2.2" +version = "0.2.3-dev" dependencies = [ "alacritty_config_derive", "log", @@ -81,7 +82,7 @@ dependencies = [ [[package]] name = "alacritty_config_derive" -version = "0.2.4" +version = "0.2.5-dev" dependencies = [ "alacritty_config", "log", @@ -94,7 +95,7 @@ dependencies = [ [[package]] name = "alacritty_terminal" -version = "0.24.1" +version = "0.24.2-dev" dependencies = [ "base64", "bitflags 2.6.0", @@ -113,7 +114,7 @@ dependencies = [ "smallvec", "unicode-width", "vte-graphics", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -304,9 +305,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" dependencies = [ "jobserver", "libc", @@ -1584,9 +1585,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1884,9 +1885,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.91" +version = "2.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" dependencies = [ "proc-macro2", "quote", @@ -2540,9 +2541,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.6" +version = "0.30.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3d72dfa0f47e429290cd0d236884ca02f22dbd5dd33a43ad2b8bf4d79b6c18" +checksum = "dba50bc8ef4b6f1a75c9274fb95aa9a8f63fbc66c56f391bd85cf68d51e7b1a3" dependencies = [ "ahash", "android-activity", @@ -84,7 +84,7 @@ to build Alacritty. Here's an apt command that should install all of them. If something is still found to be missing, please open an issue. ```sh -apt install cmake pkg-config libfreetype6-dev libfontconfig1-dev libxcb-xfixes0-dev libxkbcommon-dev python3 +apt install cmake g++ pkg-config libfreetype6-dev libfontconfig1-dev libxcb-xfixes0-dev libxkbcommon-dev python3 ``` #### Arch Linux diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml index 453ac70c..f144e578 100644 --- a/alacritty/Cargo.toml +++ b/alacritty/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty" -version = "0.14.0" +version = "0.15.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,15 +12,15 @@ rust-version = "1.74.0" [dependencies.alacritty_terminal] path = "../alacritty_terminal" -version = "0.24.1" +version = "0.24.2-dev" [dependencies.alacritty_config_derive] path = "../alacritty_config_derive" -version = "0.2.4" +version = "0.2.5-dev" [dependencies.alacritty_config] path = "../alacritty_config" -version = "0.2.2" +version = "0.2.3-dev" [dependencies] ahash = { version = "0.8.6", features = ["no-rng"] } @@ -43,7 +43,7 @@ tempfile = "3.12.0" toml = "0.8.2" toml_edit = "0.22.21" unicode-width = "0.1" -winit = { version = "0.30.4", default-features = false, features = ["rwh_06", "serde"] } +winit = { version = "0.30.7", default-features = false, features = ["rwh_06", "serde"] } [build-dependencies] gl_generator = "0.14.0" @@ -58,8 +58,19 @@ xdg = "2.5.0" png = { version = "0.17.5", default-features = false, optional = true } [target.'cfg(target_os = "macos")'.dependencies] -cocoa = "0.25.0" -objc = "0.2.2" +objc2 = "0.5.2" +objc2-foundation = { version = "0.2.2", default-features = false, features = [ + "std", + "NSString", + "NSLocale", +] } +objc2-app-kit = { version = "0.2.2", default-features = false, features = [ + "std", + "NSColorSpace", + "NSResponder", + "NSView", + "NSWindow", +] } [target.'cfg(windows)'.dependencies] dirs = "5.0.1" diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs index dfe31853..931b0583 100644 --- a/alacritty/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -5,6 +5,7 @@ use std::fmt::{self, Debug, Display}; use bitflags::bitflags; use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor}; use serde::{Deserialize, Deserializer}; +use std::rc::Rc; use toml::Value as SerdeValue; use winit::event::MouseButton; use winit::keyboard::{ @@ -96,7 +97,7 @@ pub enum Action { /// Regex keyboard hints. #[config(skip)] - Hint(Hint), + Hint(Rc<Hint>), /// Move vi mode cursor. #[config(skip)] @@ -790,7 +791,7 @@ impl<'a> Deserialize<'a> for ModeWrapper { { struct ModeVisitor; - impl<'a> Visitor<'a> for ModeVisitor { + impl Visitor<'_> for ModeVisitor { type Value = ModeWrapper; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -844,7 +845,7 @@ impl<'a> Deserialize<'a> for MouseButtonWrapper { { struct MouseButtonVisitor; - impl<'a> Visitor<'a> for MouseButtonVisitor { + impl Visitor<'_> for MouseButtonVisitor { type Value = MouseButtonWrapper; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -956,7 +957,7 @@ impl<'a> Deserialize<'a> for RawBinding { { struct FieldVisitor; - impl<'a> Visitor<'a> for FieldVisitor { + impl Visitor<'_> for FieldVisitor { type Value = Field; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1204,7 +1205,7 @@ impl<'a> de::Deserialize<'a> for ModsWrapper { { struct ModsVisitor; - impl<'a> Visitor<'a> for ModsVisitor { + impl Visitor<'_> for ModsVisitor { type Value = ModsWrapper; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/alacritty/src/config/font.rs b/alacritty/src/config/font.rs index 061c0f42..760c26d5 100644 --- a/alacritty/src/config/font.rs +++ b/alacritty/src/config/font.rs @@ -144,7 +144,7 @@ impl<'de> Deserialize<'de> for Size { D: Deserializer<'de>, { struct NumVisitor; - impl<'v> Visitor<'v> for NumVisitor { + impl Visitor<'_> for NumVisitor { type Value = Size; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs index 69716dee..b44bda0d 100644 --- a/alacritty/src/config/ui_config.rs +++ b/alacritty/src/config/ui_config.rs @@ -253,7 +253,7 @@ pub struct Hints { alphabet: HintsAlphabet, /// All configured terminal hints. - pub enabled: Vec<Hint>, + pub enabled: Vec<Rc<Hint>>, } impl Default for Hints { @@ -274,7 +274,7 @@ impl Default for Hints { }); Self { - enabled: vec![Hint { + enabled: vec![Rc::new(Hint { content, action, persist: false, @@ -288,7 +288,7 @@ impl Default for Hints { mods: ModsWrapper(ModifiersState::SHIFT | ModifiersState::CONTROL), mode: Default::default(), }), - }], + })], alphabet: Default::default(), } } @@ -619,7 +619,7 @@ impl SerdeReplace for Program { } pub(crate) struct StringVisitor; -impl<'de> serde::de::Visitor<'de> for StringVisitor { +impl serde::de::Visitor<'_> for StringVisitor { type Value = String; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs index 380f7a6f..358bb76d 100644 --- a/alacritty/src/config/window.rs +++ b/alacritty/src/config/window.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize}; #[cfg(target_os = "macos")] use winit::platform::macos::OptionAsAlt as WinitOptionAsAlt; -use winit::window::{Fullscreen, Theme as WinitTheme}; +use winit::window::{Fullscreen, Theme as WinitTheme, WindowLevel as WinitWindowLevel}; use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; @@ -61,6 +61,9 @@ pub struct WindowConfig { /// System decorations theme variant. decorations_theme_variant: Option<Theme>, + + /// Window level. + pub level: WindowLevel, } impl Default for WindowConfig { @@ -80,6 +83,7 @@ impl Default for WindowConfig { resize_increments: Default::default(), decorations_theme_variant: Default::default(), option_as_alt: Default::default(), + level: Default::default(), } } } @@ -306,3 +310,19 @@ impl From<Theme> for WinitTheme { } } } + +#[derive(ConfigDeserialize, Default, Debug, Clone, Copy, PartialEq, Eq)] +pub enum WindowLevel { + #[default] + Normal, + AlwaysOnTop, +} + +impl From<WindowLevel> for WinitWindowLevel { + fn from(level: WindowLevel) -> Self { + match level { + WindowLevel::Normal => WinitWindowLevel::Normal, + WindowLevel::AlwaysOnTop => WinitWindowLevel::AlwaysOnTop, + } + } +} diff --git a/alacritty/src/display/color.rs b/alacritty/src/display/color.rs index 669bf502..2e854a3f 100644 --- a/alacritty/src/display/color.rs +++ b/alacritty/src/display/color.rs @@ -18,7 +18,7 @@ pub const DIM_FACTOR: f32 = 0.66; #[derive(Copy, Clone)] pub struct List([Rgb; COUNT]); -impl<'a> From<&'a Colors> for List { +impl From<&'_ Colors> for List { fn from(colors: &Colors) -> List { // Type inference fails without this annotation. let mut list = List([Rgb::default(); COUNT]); @@ -235,7 +235,7 @@ impl<'de> Deserialize<'de> for Rgb { b: u8, } - impl<'a> Visitor<'a> for RgbVisitor { + impl Visitor<'_> for RgbVisitor { type Value = Rgb; fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -331,7 +331,7 @@ impl<'de> Deserialize<'de> for CellRgb { const EXPECTING: &str = "CellForeground, CellBackground, or hex color like #ff00ff"; struct CellRgbVisitor; - impl<'a> Visitor<'a> for CellRgbVisitor { + impl Visitor<'_> for CellRgbVisitor { type Value = CellRgb; fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs index 02ac50e3..9a7de3fe 100644 --- a/alacritty/src/display/content.rs +++ b/alacritty/src/display/content.rs @@ -153,7 +153,7 @@ impl<'a> RenderableContent<'a> { } } -impl<'a> Iterator for RenderableContent<'a> { +impl Iterator for RenderableContent<'_> { type Item = RenderableCell; /// Gets the next renderable cell. @@ -482,7 +482,7 @@ struct Hint<'a> { labels: &'a Vec<Vec<char>>, } -impl<'a> Hint<'a> { +impl Hint<'_> { /// Advance the hint iterator. /// /// If the point is within a hint, the keyboard shortcut character that should be displayed at @@ -572,7 +572,7 @@ impl<'a> HintMatches<'a> { } } -impl<'a> Deref for HintMatches<'a> { +impl Deref for HintMatches<'_> { type Target = [Match]; fn deref(&self) -> &Self::Target { diff --git a/alacritty/src/display/damage.rs b/alacritty/src/display/damage.rs index 8efe0133..b0736375 100644 --- a/alacritty/src/display/damage.rs +++ b/alacritty/src/display/damage.rs @@ -193,9 +193,11 @@ impl FrameDamage { /// Check if a range is damaged. #[inline] pub fn intersects(&self, start: Point<usize>, end: Point<usize>) -> bool { + let start_line = &self.lines[start.line]; + let end_line = &self.lines[end.line]; self.full - || self.lines[start.line].left <= start.column - || self.lines[end.line].right >= end.column + || (start_line.left..=start_line.right).contains(&start.column) + || (end_line.left..=end_line.right).contains(&end.column) || (start.line + 1..end.line).any(|line| self.lines[line].is_damaged()) } } @@ -249,7 +251,7 @@ impl<'a> RenderDamageIterator<'a> { } } -impl<'a> Iterator for RenderDamageIterator<'a> { +impl Iterator for RenderDamageIterator<'_> { type Item = Rect; fn next(&mut self) -> Option<Rect> { diff --git a/alacritty/src/display/hint.rs b/alacritty/src/display/hint.rs index a01a1d03..3f10b4e5 100644 --- a/alacritty/src/display/hint.rs +++ b/alacritty/src/display/hint.rs @@ -1,6 +1,8 @@ +use std::borrow::Cow; use std::cmp::Reverse; use std::collections::HashSet; use std::iter; +use std::rc::Rc; use ahash::RandomState; use winit::keyboard::ModifiersState; @@ -23,7 +25,7 @@ const HINT_SPLIT_PERCENTAGE: f32 = 0.5; /// Keyboard regex hint state. pub struct HintState { /// Hint currently in use. - hint: Option<Hint>, + hint: Option<Rc<Hint>>, /// Alphabet for hint labels. alphabet: String, @@ -56,7 +58,7 @@ impl HintState { } /// Start the hint selection process. - pub fn start(&mut self, hint: Hint) { + pub fn start(&mut self, hint: Rc<Hint>) { self.hint = Some(hint); } @@ -150,7 +152,7 @@ impl HintState { // Check if the selected label is fully matched. if label.len() == 1 { let bounds = self.matches[index].clone(); - let action = hint.action.clone(); + let hint = hint.clone(); // Exit hint mode unless it requires explicit dismissal. if hint.persist { @@ -161,7 +163,7 @@ impl HintState { // Hyperlinks take precedence over regex matches. let hyperlink = term.grid()[*bounds.start()].hyperlink(); - Some(HintMatch { action, bounds, hyperlink }) + Some(HintMatch { bounds, hyperlink, hint }) } else { // Store character to preserve the selection. self.keys.push(c); @@ -192,13 +194,14 @@ impl HintState { /// Hint match which was selected by the user. #[derive(PartialEq, Eq, Debug, Clone)] pub struct HintMatch { - /// Action for handling the text. - action: HintAction, - /// Terminal range matching the hint. bounds: Match, + /// OSC 8 hyperlink. hyperlink: Option<Hyperlink>, + + /// Hint which triggered this match. + hint: Rc<Hint>, } impl HintMatch { @@ -210,7 +213,7 @@ impl HintMatch { #[inline] pub fn action(&self) -> &HintAction { - &self.action + &self.hint.action } #[inline] @@ -221,6 +224,29 @@ impl HintMatch { pub fn hyperlink(&self) -> Option<&Hyperlink> { self.hyperlink.as_ref() } + + /// Get the text content of the hint match. + /// + /// This will always revalidate the hint text, to account for terminal content + /// changes since the [`HintMatch`] was constructed. The text of the hint might + /// be different from its original value, but it will **always** be a valid + /// match for this hint. + pub fn text<T>(&self, term: &Term<T>) -> Option<Cow<'_, str>> { + // Revalidate hyperlink match. + if let Some(hyperlink) = &self.hyperlink { + let (validated, bounds) = hyperlink_at(term, *self.bounds.start())?; + return (&validated == hyperlink && bounds == self.bounds) + .then(|| hyperlink.uri().into()); + } + + // Revalidate regex match. + let regex = self.hint.content.regex.as_ref()?; + let bounds = regex.with_compiled(|regex| { + regex_match_at(term, *self.bounds.start(), regex, self.hint.post_processing) + })??; + (bounds == self.bounds) + .then(|| term.bounds_to_string(*bounds.start(), *bounds.end()).into()) + } } /// Generator for creating new hint labels. @@ -382,18 +408,14 @@ pub fn highlighted_at<T>( if let Some((hyperlink, bounds)) = hint.content.hyperlinks.then(|| hyperlink_at(term, point)).flatten() { - return Some(HintMatch { - bounds, - action: hint.action.clone(), - hyperlink: Some(hyperlink), - }); + return Some(HintMatch { bounds, hyperlink: Some(hyperlink), hint: hint.clone() }); } let bounds = hint.content.regex.as_ref().and_then(|regex| { regex.with_compiled(|regex| regex_match_at(term, point, regex, hint.post_processing)) }); if let Some(bounds) = bounds.flatten() { - return Some(HintMatch { bounds, action: hint.action.clone(), hyperlink: None }); + return Some(HintMatch { bounds, hint: hint.clone(), hyperlink: None }); } None @@ -554,7 +576,7 @@ impl<'a, T> HintPostProcessor<'a, T> { } } -impl<'a, T> Iterator for HintPostProcessor<'a, T> { +impl<T> Iterator for HintPostProcessor<'_, T> { type Item = Match; fn next(&mut self) -> Option<Self::Item> { diff --git a/alacritty/src/display/meter.rs b/alacritty/src/display/meter.rs index e263100f..20cbbd1b 100644 --- a/alacritty/src/display/meter.rs +++ b/alacritty/src/display/meter.rs @@ -57,7 +57,7 @@ impl<'a> Sampler<'a> { } } -impl<'a> Drop for Sampler<'a> { +impl Drop for Sampler<'_> { fn drop(&mut self) { self.meter.add_sample(self.alive_duration()); } diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index 17ab461c..a679fe21 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -297,12 +297,12 @@ impl TermDimensions for SizeInfo { #[inline] fn cell_height(&self) -> f32 { - self.cell_height() + self.cell_height } #[inline] fn cell_width(&self) -> f32 { - self.cell_width() + self.cell_width } } @@ -352,9 +352,13 @@ pub struct Display { /// Hint highlighted by the mouse. pub highlighted_hint: Option<HintMatch>, + /// Frames since hint highlight was created. + highlighted_hint_age: usize, /// Hint highlighted by the vi mode cursor. pub vi_highlighted_hint: Option<HintMatch>, + /// Frames since hint highlight was created. + vi_highlighted_hint_age: usize, pub raw_window_handle: RawWindowHandle, @@ -490,6 +494,10 @@ impl Display { window.set_visible(true); + // Always focus new windows, even if no Alacritty window is currently focused. + #[cfg(target_os = "macos")] + window.focus_window(); + #[allow(clippy::single_match)] #[cfg(not(windows))] if !_tabbed { @@ -526,6 +534,8 @@ impl Display { font_size, window, pending_renderer_update: Default::default(), + vi_highlighted_hint_age: Default::default(), + highlighted_hint_age: Default::default(), vi_highlighted_hint: Default::default(), highlighted_hint: Default::default(), hint_mouse_point: Default::default(), @@ -771,7 +781,7 @@ impl Display { drop(terminal); // Invalidate highlighted hints if grid has changed. - self.validate_hints(display_offset); + self.validate_hint_highlights(display_offset); // Add damage from alacritty's UI elements overlapping terminal. @@ -1042,9 +1052,10 @@ impl Display { }; let mut dirty = vi_highlighted_hint != self.vi_highlighted_hint; self.vi_highlighted_hint = vi_highlighted_hint; + self.vi_highlighted_hint_age = 0; // Force full redraw if the vi mode highlight was cleared. - if dirty && self.vi_highlighted_hint.is_none() { + if dirty { self.damage_tracker.frame().mark_fully_damaged(); } @@ -1080,9 +1091,10 @@ impl Display { let mouse_highlight_dirty = self.highlighted_hint != highlighted_hint; dirty |= mouse_highlight_dirty; self.highlighted_hint = highlighted_hint; + self.highlighted_hint_age = 0; - // Force full redraw if the mouse cursor highlight was cleared. - if mouse_highlight_dirty && self.highlighted_hint.is_none() { + // Force full redraw if the mouse cursor highlight was changed. + if mouse_highlight_dirty { self.damage_tracker.frame().mark_fully_damaged(); } @@ -1356,16 +1368,24 @@ impl Display { } /// Check whether a hint highlight needs to be cleared. - fn validate_hints(&mut self, display_offset: usize) { + fn validate_hint_highlights(&mut self, display_offset: usize) { let frame = self.damage_tracker.frame(); - for (hint, reset_mouse) in - [(&mut self.highlighted_hint, true), (&mut self.vi_highlighted_hint, false)] - { + let hints = [ + (&mut self.highlighted_hint, &mut self.highlighted_hint_age, true), + (&mut self.vi_highlighted_hint, &mut self.vi_highlighted_hint_age, false), + ]; + for (hint, hint_age, reset_mouse) in hints { let (start, end) = match hint { Some(hint) => (*hint.bounds().start(), *hint.bounds().end()), - None => return, + None => continue, }; + // Ignore hints that were created this frame. + *hint_age += 1; + if *hint_age == 1 { + continue; + } + // 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(|| { diff --git a/alacritty/src/display/window.rs b/alacritty/src/display/window.rs index 1427dc75..1c8089bc 100644 --- a/alacritty/src/display/window.rs +++ b/alacritty/src/display/window.rs @@ -20,9 +20,8 @@ use std::fmt::{self, Display, Formatter}; #[cfg(target_os = "macos")] use { - cocoa::appkit::NSColorSpace, - cocoa::base::{id, nil, NO, YES}, - objc::{msg_send, sel, sel_impl}, + objc2_app_kit::{NSColorSpace, NSView}, + objc2_foundation::is_main_thread, winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS}, }; @@ -45,7 +44,7 @@ use crate::display::SizeInfo; /// Window icon for `_NET_WM_ICON` property. #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] -static WINDOW_ICON: &[u8] = include_bytes!("../../extra/logo/compat/alacritty-term.png"); +const WINDOW_ICON: &[u8] = include_bytes!("../../extra/logo/compat/alacritty-term.png"); /// This should match the definition of IDI_ICON from `alacritty.rc`. #[cfg(windows)] @@ -170,7 +169,8 @@ impl Window { .with_transparent(true) .with_blur(config.window.blur) .with_maximized(config.window.maximized()) - .with_fullscreen(config.window.fullscreen()); + .with_fullscreen(config.window.fullscreen()) + .with_window_level(config.window.level.into()); let window = event_loop.create_window(window_attributes)?; @@ -224,6 +224,12 @@ impl Window { self.window.set_visible(visibility); } + #[cfg(target_os = "macos")] + #[inline] + pub fn focus_window(&self) { + self.window.focus_window(); + } + /// Set the window title. #[inline] pub fn set_title(&mut self, title: String) { @@ -445,16 +451,15 @@ impl Window { /// This prevents rendering artifacts from showing up when the window is transparent. #[cfg(target_os = "macos")] pub fn set_has_shadow(&self, has_shadows: bool) { - let ns_view = match self.raw_window_handle() { - RawWindowHandle::AppKit(handle) => handle.ns_view.as_ptr() as id, + let view = match self.raw_window_handle() { + RawWindowHandle::AppKit(handle) => { + assert!(is_main_thread()); + unsafe { handle.ns_view.cast::<NSView>().as_ref() } + }, _ => return, }; - let value = if has_shadows { YES } else { NO }; - unsafe { - let ns_window: id = msg_send![ns_view, window]; - let _: id = msg_send![ns_window, setHasShadow: value]; - } + view.window().unwrap().setHasShadow(has_shadows); } /// Select tab at the given `index`. @@ -489,13 +494,15 @@ impl Window { #[cfg(target_os = "macos")] fn use_srgb_color_space(window: &WinitWindow) { - let ns_view = match window.window_handle().unwrap().as_raw() { - RawWindowHandle::AppKit(handle) => handle.ns_view.as_ptr() as id, + let view = match window.window_handle().unwrap().as_raw() { + RawWindowHandle::AppKit(handle) => { + assert!(is_main_thread()); + unsafe { handle.ns_view.cast::<NSView>().as_ref() } + }, _ => return, }; unsafe { - let ns_window: id = msg_send![ns_view, window]; - let _: () = msg_send![ns_window, setColorSpace: NSColorSpace::sRGBColorSpace(nil)]; + view.window().unwrap().setColorSpace(Some(&NSColorSpace::sRGBColorSpace())); } } diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index f0159060..79f31eef 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -140,14 +140,14 @@ impl Processor { pub fn create_initial_window( &mut self, event_loop: &ActiveEventLoop, + window_options: WindowOptions, ) -> Result<(), Box<dyn Error>> { - let options = match self.initial_window_options.take() { - Some(options) => options, - None => return Ok(()), - }; - - let window_context = - WindowContext::initial(event_loop, self.proxy.clone(), self.config.clone(), options)?; + let window_context = WindowContext::initial( + event_loop, + self.proxy.clone(), + self.config.clone(), + window_options, + )?; self.gl_config = Some(window_context.display.gl_context().config()); self.windows.insert(window_context.id(), window_context); @@ -225,10 +225,12 @@ impl ApplicationHandler<Event> for Processor { return; } - if let Err(err) = self.create_initial_window(event_loop) { - self.initial_window_error = Some(err); - event_loop.exit(); - return; + if let Some(window_options) = self.initial_window_options.take() { + if let Err(err) = self.create_initial_window(event_loop, window_options) { + self.initial_window_error = Some(err); + event_loop.exit(); + return; + } } info!("Initialisation complete"); @@ -276,7 +278,7 @@ impl ApplicationHandler<Event> for Processor { } // Handle events which don't mandate the WindowId. - match (&event.payload, event.window_id.as_ref()) { + match (event.payload, event.window_id.as_ref()) { // Process IPC config update. #[cfg(unix)] (EventType::IpcConfig(ipc_config), window_id) => { @@ -315,7 +317,7 @@ impl ApplicationHandler<Event> for Processor { } // Load config and update each terminal. - if let Ok(config) = config::reload(path, &mut self.cli_options) { + if let Ok(config) = config::reload(&path, &mut self.cli_options) { self.config = Rc::new(config); // Restart config monitor if imports changed. @@ -346,17 +348,17 @@ impl ApplicationHandler<Event> for Processor { if self.gl_config.is_none() { // Handle initial window creation in daemon mode. - if let Err(err) = self.create_initial_window(event_loop) { + if let Err(err) = self.create_initial_window(event_loop, options) { self.initial_window_error = Some(err); event_loop.exit(); } - } else if let Err(err) = self.create_window(event_loop, options.clone()) { + } else if let Err(err) = self.create_window(event_loop, options) { error!("Could not open window: {:?}", err); } }, // Process events affecting all windows. - (_, None) => { - let event = WinitEvent::UserEvent(event); + (payload, None) => { + let event = WinitEvent::UserEvent(Event::new(payload, None)); for window_context in self.windows.values_mut() { window_context.handle_event( #[cfg(target_os = "macos")] @@ -405,7 +407,7 @@ impl ApplicationHandler<Event> for Processor { } } }, - (_, Some(window_id)) => { + (payload, Some(window_id)) => { if let Some(window_context) = self.windows.get_mut(window_id) { window_context.handle_event( #[cfg(target_os = "macos")] @@ -413,7 +415,7 @@ impl ApplicationHandler<Event> for Processor { &self.proxy, &mut self.clipboard, &mut self.scheduler, - WinitEvent::UserEvent(event), + WinitEvent::UserEvent(Event::new(payload, *window_id)), ); } }, @@ -1141,16 +1143,16 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } let hint_bounds = hint.bounds(); - let text = match hint.hyperlink() { - Some(hyperlink) => hyperlink.uri().to_owned(), - None => self.terminal.bounds_to_string(*hint_bounds.start(), *hint_bounds.end()), + let text = match hint.text(self.terminal) { + Some(text) => text, + None => return, }; match &hint.action() { // Launch an external program. HintAction::Command(command) => { let mut args = command.args().to_vec(); - args.push(text); + args.push(text.into()); self.spawn_daemon(command.program(), &args); }, // Copy the text to the clipboard. diff --git a/alacritty/src/input/keyboard.rs b/alacritty/src/input/keyboard.rs index 14755594..85734109 100644 --- a/alacritty/src/input/keyboard.rs +++ b/alacritty/src/input/keyboard.rs @@ -280,7 +280,7 @@ fn build_sequence(key: KeyEvent, mods: ModifiersState, mode: TermMode) -> Vec<u8 let sequence_base = context .try_build_numpad(&key) .or_else(|| context.try_build_named_kitty(&key)) - .or_else(|| context.try_build_named_normal(&key)) + .or_else(|| context.try_build_named_normal(&key, associated_text.is_some())) .or_else(|| context.try_build_control_char_or_mod(&key, &mut modifiers)) .or_else(|| context.try_build_textual(&key, associated_text)); @@ -483,14 +483,23 @@ impl SequenceBuilder { } /// Try building from [`NamedKey`]. - fn try_build_named_normal(&self, key: &KeyEvent) -> Option<SequenceBase> { + fn try_build_named_normal( + &self, + key: &KeyEvent, + has_associated_text: bool, + ) -> Option<SequenceBase> { let named = match key.logical_key { Key::Named(named) => named, _ => return None, }; // The default parameter is 1, so we can omit it. - let one_based = if self.modifiers.is_empty() && !self.kitty_event_type { "" } else { "1" }; + let one_based = + if self.modifiers.is_empty() && !self.kitty_event_type && !has_associated_text { + "" + } else { + "1" + }; let (base, terminator) = match named { NamedKey::PageUp => ("5", SequenceTerminator::Normal('~')), NamedKey::PageDown => ("6", SequenceTerminator::Normal('~')), diff --git a/alacritty/src/input/mod.rs b/alacritty/src/input/mod.rs index bbd8673f..1c9d6008 100644 --- a/alacritty/src/input/mod.rs +++ b/alacritty/src/input/mod.rs @@ -1136,7 +1136,7 @@ mod tests { inline_search_state: &'a mut InlineSearchState, } - impl<'a, T: EventListener> super::ActionContext<T> for ActionContext<'a, T> { + impl<T: EventListener> super::ActionContext<T> for ActionContext<'_, T> { fn search_next( &mut self, _origin: Point, diff --git a/alacritty/src/macos/locale.rs b/alacritty/src/macos/locale.rs index 2a47ace8..46996515 100644 --- a/alacritty/src/macos/locale.rs +++ b/alacritty/src/macos/locale.rs @@ -1,13 +1,12 @@ #![allow(clippy::let_unit_value)] use std::ffi::{CStr, CString}; -use std::os::raw::c_char; -use std::{env, slice, str}; +use std::{env, str}; use libc::{setlocale, LC_ALL, LC_CTYPE}; use log::debug; -use objc::runtime::{Class, Object}; -use objc::{msg_send, sel, sel_impl}; +use objc2::sel; +use objc2_foundation::{NSLocale, NSObjectProtocol}; const FALLBACK_LOCALE: &str = "UTF-8"; @@ -50,9 +49,7 @@ pub fn set_locale_environment() { /// Determine system locale based on language and country code. fn system_locale() -> String { unsafe { - let locale_class = Class::get("NSLocale").unwrap(); - let locale: *const Object = msg_send![locale_class, currentLocale]; - let _: () = msg_send![locale_class, release]; + let locale = NSLocale::currentLocale(); // `localeIdentifier` returns extra metadata with the locale (including currency and // collator) on newer versions of macOS. This is not a valid locale, so we use @@ -61,38 +58,19 @@ fn system_locale() -> String { // https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc // https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc // https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc - let is_language_code_supported: bool = - msg_send![locale, respondsToSelector: sel!(languageCode)]; - let is_country_code_supported: bool = - msg_send![locale, respondsToSelector: sel!(countryCode)]; - let locale_id = if is_language_code_supported && is_country_code_supported { - let language_code: *const Object = msg_send![locale, languageCode]; - let language_code_str = nsstring_as_str(language_code).to_owned(); - let _: () = msg_send![language_code, release]; - - let country_code: *const Object = msg_send![locale, countryCode]; - let country_code_str = nsstring_as_str(country_code).to_owned(); - let _: () = msg_send![country_code, release]; - - format!("{}_{}.UTF-8", &language_code_str, &country_code_str) + let is_language_code_supported: bool = locale.respondsToSelector(sel!(languageCode)); + let is_country_code_supported: bool = locale.respondsToSelector(sel!(countryCode)); + if is_language_code_supported && is_country_code_supported { + let language_code = locale.languageCode(); + #[allow(deprecated)] + if let Some(country_code) = locale.countryCode() { + format!("{}_{}.UTF-8", language_code, country_code) + } else { + // Fall back to en_US in case the country code is not available. + "en_US.UTF-8".into() + } } else { - let identifier: *const Object = msg_send![locale, localeIdentifier]; - let identifier_str = nsstring_as_str(identifier).to_owned(); - let _: () = msg_send![identifier, release]; - - identifier_str + ".UTF-8" - }; - - let _: () = msg_send![locale, release]; - - locale_id + locale.localeIdentifier().to_string() + ".UTF-8" + } } } - -const UTF8_ENCODING: usize = 4; - -unsafe fn nsstring_as_str<'a>(nsstring: *const Object) -> &'a str { - let cstr: *const c_char = msg_send![nsstring, UTF8String]; - let len: usize = msg_send![nsstring, lengthOfBytesUsingEncoding: UTF8_ENCODING]; - str::from_utf8(slice::from_raw_parts(cstr as *const u8, len)).unwrap() -} diff --git a/alacritty/src/migrate/mod.rs b/alacritty/src/migrate/mod.rs index 2f806d36..bfa72294 100644 --- a/alacritty/src/migrate/mod.rs +++ b/alacritty/src/migrate/mod.rs @@ -151,7 +151,15 @@ fn migrate_imports( // Migrate each import. for import in imports.into_iter().filter_map(|item| item.as_str()) { let normalized_path = config::normalize_import(path, import); - let migration = migrate_config(options, &normalized_path, recursion_limit)?; + + if !normalized_path.exists() { + if options.dry_run { + println!("Skipping migration for nonexistent path: {}", normalized_path.display()); + } + continue; + } + + let migration = migrate_config(options, &normalized_path, recursion_limit - 1)?; if options.dry_run { println!("{}", migration.success_message(true)); } @@ -244,7 +252,7 @@ enum Migration<'a> { Yaml((&'a Path, String)), } -impl<'a> Migration<'a> { +impl Migration<'_> { /// Get the success message for this migration. fn success_message(&self, import: bool) -> String { match self { diff --git a/alacritty/src/renderer/graphics/shader.rs b/alacritty/src/renderer/graphics/shader.rs index e046c9ae..650af0a5 100644 --- a/alacritty/src/renderer/graphics/shader.rs +++ b/alacritty/src/renderer/graphics/shader.rs @@ -54,8 +54,8 @@ pub struct Vertex { } /// Sources for the graphics rendering program. -static GRAPHICS_SHADER_F: &str = include_str!("../../../res/graphics.f.glsl"); -static GRAPHICS_SHADER_V: &str = include_str!("../../../res/graphics.v.glsl"); +const GRAPHICS_SHADER_F: &str = include_str!("../../../res/graphics.f.glsl"); +const GRAPHICS_SHADER_V: &str = include_str!("../../../res/graphics.v.glsl"); /// Graphics rendering program. #[derive(Debug)] diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs index 0bbcb832..d394073b 100644 --- a/alacritty/src/renderer/rects.rs +++ b/alacritty/src/renderer/rects.rs @@ -232,8 +232,8 @@ impl RenderLines { } /// Shader sources for rect rendering program. -static RECT_SHADER_F: &str = include_str!("../../res/rect.f.glsl"); -static RECT_SHADER_V: &str = include_str!("../../res/rect.v.glsl"); +const RECT_SHADER_F: &str = include_str!("../../res/rect.f.glsl"); +const RECT_SHADER_V: &str = include_str!("../../res/rect.v.glsl"); #[repr(C)] #[derive(Debug, Clone, Copy)] diff --git a/alacritty/src/renderer/text/gles2.rs b/alacritty/src/renderer/text/gles2.rs index 36b817ac..5268341d 100644 --- a/alacritty/src/renderer/text/gles2.rs +++ b/alacritty/src/renderer/text/gles2.rs @@ -20,8 +20,8 @@ use super::{ }; // Shader source. -static TEXT_SHADER_F: &str = include_str!("../../../res/gles2/text.f.glsl"); -static TEXT_SHADER_V: &str = include_str!("../../../res/gles2/text.v.glsl"); +const TEXT_SHADER_F: &str = include_str!("../../../res/gles2/text.f.glsl"); +const TEXT_SHADER_V: &str = include_str!("../../../res/gles2/text.v.glsl"); #[derive(Debug)] pub struct Gles2Renderer { @@ -350,7 +350,7 @@ pub struct RenderApi<'a> { dual_source_blending: bool, } -impl<'a> Drop for RenderApi<'a> { +impl Drop for RenderApi<'_> { fn drop(&mut self) { if !self.batch.is_empty() { self.render_batch(); @@ -358,7 +358,7 @@ impl<'a> Drop for RenderApi<'a> { } } -impl<'a> LoadGlyph for RenderApi<'a> { +impl LoadGlyph for RenderApi<'_> { fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph { Atlas::load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized) } @@ -368,7 +368,7 @@ impl<'a> LoadGlyph for RenderApi<'a> { } } -impl<'a> TextRenderApi<Batch> for RenderApi<'a> { +impl TextRenderApi<Batch> for RenderApi<'_> { fn batch(&mut self) -> &mut Batch { self.batch } diff --git a/alacritty/src/renderer/text/glsl3.rs b/alacritty/src/renderer/text/glsl3.rs index 536fe048..07fcdff0 100644 --- a/alacritty/src/renderer/text/glsl3.rs +++ b/alacritty/src/renderer/text/glsl3.rs @@ -20,8 +20,8 @@ use super::{ }; // Shader source. -pub static TEXT_SHADER_F: &str = include_str!("../../../res/glsl3/text.f.glsl"); -static TEXT_SHADER_V: &str = include_str!("../../../res/glsl3/text.v.glsl"); +pub const TEXT_SHADER_F: &str = include_str!("../../../res/glsl3/text.f.glsl"); +const TEXT_SHADER_V: &str = include_str!("../../../res/glsl3/text.v.glsl"); /// Maximum items to be drawn in a batch. const BATCH_MAX: usize = 0x1_0000; @@ -219,7 +219,7 @@ pub struct RenderApi<'a> { program: &'a mut TextShaderProgram, } -impl<'a> TextRenderApi<Batch> for RenderApi<'a> { +impl TextRenderApi<Batch> for RenderApi<'_> { fn batch(&mut self) -> &mut Batch { self.batch } @@ -265,7 +265,7 @@ impl<'a> TextRenderApi<Batch> for RenderApi<'a> { } } -impl<'a> LoadGlyph for RenderApi<'a> { +impl LoadGlyph for RenderApi<'_> { fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph { Atlas::load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized) } @@ -275,7 +275,7 @@ impl<'a> LoadGlyph for RenderApi<'a> { } } -impl<'a> Drop for RenderApi<'a> { +impl Drop for RenderApi<'_> { fn drop(&mut self) { if !self.batch.is_empty() { self.render_batch(); diff --git a/alacritty/src/renderer/text/mod.rs b/alacritty/src/renderer/text/mod.rs index 5ee6bc4c..2742704d 100644 --- a/alacritty/src/renderer/text/mod.rs +++ b/alacritty/src/renderer/text/mod.rs @@ -188,7 +188,7 @@ pub struct LoaderApi<'a> { current_atlas: &'a mut usize, } -impl<'a> LoadGlyph for LoaderApi<'a> { +impl LoadGlyph for LoaderApi<'_> { fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph { Atlas::load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized) } diff --git a/alacritty/src/string.rs b/alacritty/src/string.rs index a7af4394..b8c47d3b 100644 --- a/alacritty/src/string.rs +++ b/alacritty/src/string.rs @@ -106,7 +106,7 @@ impl<'a> StrShortener<'a> { } } -impl<'a> Iterator for StrShortener<'a> { +impl Iterator for StrShortener<'_> { type Item = char; fn next(&mut self) -> Option<Self::Item> { diff --git a/alacritty/windows/wix/alacritty.wxs b/alacritty/windows/wix/alacritty.wxs index cd00bda0..5c05d821 100644 --- a/alacritty/windows/wix/alacritty.wxs +++ b/alacritty/windows/wix/alacritty.wxs @@ -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.14.0" Manufacturer="Alacritty" InstallerVersion="200"> + <Package Name="Alacritty" UpgradeCode="87c21c74-dbd5-4584-89d5-46d9cd0c40a7" Language="1033" Codepage="1252" Version="0.15.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" /> diff --git a/alacritty_config/Cargo.toml b/alacritty_config/Cargo.toml index 862c6eb8..a6d651bc 100644 --- a/alacritty_config/Cargo.toml +++ b/alacritty_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty_config" -version = "0.2.2" +version = "0.2.3-dev" authors = ["Christian Duerr <contact@christianduerr.com>"] license = "MIT OR Apache-2.0" description = "Alacritty configuration abstractions" @@ -15,5 +15,5 @@ serde = "1.0.163" toml = "0.8.2" [dev-dependencies] -alacritty_config_derive = { version = "0.2.4", path = "../alacritty_config_derive" } +alacritty_config_derive = { version = "0.2.5-dev", path = "../alacritty_config_derive" } serde = { version = "1.0.163", features = ["derive"] } diff --git a/alacritty_config_derive/Cargo.toml b/alacritty_config_derive/Cargo.toml index bf439ce7..7d04eacc 100644 --- a/alacritty_config_derive/Cargo.toml +++ b/alacritty_config_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty_config_derive" -version = "0.2.4" +version = "0.2.5-dev" authors = ["Christian Duerr <contact@christianduerr.com>"] license = "MIT OR Apache-2.0" description = "Failure resistant deserialization derive" @@ -19,7 +19,7 @@ syn = { version = "2.0.16", features = ["derive", "parsing", "proc-macro", "prin [dev-dependencies.alacritty_config] path = "../alacritty_config" -version = "0.2.2" +version = "0.2.3-dev" [dev-dependencies] log = "0.4.11" diff --git a/alacritty_terminal/CHANGELOG.md b/alacritty_terminal/CHANGELOG.md index e612e749..28ed7540 100644 --- a/alacritty_terminal/CHANGELOG.md +++ b/alacritty_terminal/CHANGELOG.md @@ -8,6 +8,8 @@ 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.24.1 ### Changed diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index b8c0db4c..f8abf51d 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty_terminal" -version = "0.24.1" +version = "0.24.2-dev" authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"] license = "Apache-2.0" description = "Library for writing terminal emulators" @@ -35,7 +35,7 @@ signal-hook = "0.3.10" [target.'cfg(windows)'.dependencies] piper = "0.2.1" miow = "0.6.0" -windows-sys = { version = "0.52.0", features = [ +windows-sys = { version = "0.59.0", features = [ "Win32_System_Console", "Win32_Foundation", "Win32_Security", diff --git a/alacritty_terminal/src/graphics/mod.rs b/alacritty_terminal/src/graphics/mod.rs index 7d2a5edb..5ecdf29c 100644 --- a/alacritty_terminal/src/graphics/mod.rs +++ b/alacritty_terminal/src/graphics/mod.rs @@ -621,7 +621,7 @@ pub fn insert_graphic<L: EventListener>( } if term.mode().contains(TermMode::SIXEL_CURSOR_TO_THE_RIGHT) { - let graphic_columns = (graphic.width + cell_width - 1) / cell_width; + let graphic_columns = graphic.width.div_ceil(cell_width); term.move_forward(graphic_columns); } else if scrolling { term.linefeed(); diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs index 8c913de8..a1b4d67b 100644 --- a/alacritty_terminal/src/grid/mod.rs +++ b/alacritty_terminal/src/grid/mod.rs @@ -627,7 +627,7 @@ pub trait BidirectionalIterator: Iterator { fn prev(&mut self) -> Option<Self::Item>; } -impl<'a, T> BidirectionalIterator for GridIterator<'a, T> { +impl<T> BidirectionalIterator for GridIterator<'_, T> { fn prev(&mut self) -> Option<Self::Item> { let topmost_line = self.grid.topmost_line(); let last_column = self.grid.last_column(); diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 6b0ccccd..46b65377 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -207,7 +207,7 @@ impl<'a> TermDamageIterator<'a> { } } -impl<'a> Iterator for TermDamageIterator<'a> { +impl Iterator for TermDamageIterator<'_> { type Item = LineDamageBounds; fn next(&mut self) -> Option<Self::Item> { @@ -956,6 +956,11 @@ impl<T> Term<T> { &self.config.semantic_escape_chars } + #[cfg(test)] + pub(crate) fn set_semantic_escape_chars(&mut self, semantic_escape_chars: &str) { + self.config.semantic_escape_chars = semantic_escape_chars.into(); + } + /// Active terminal cursor style. /// /// While vi mode is active, this will automatically return the vi mode cursor style. diff --git a/alacritty_terminal/src/term/search.rs b/alacritty_terminal/src/term/search.rs index 33f6ee05..20a427b7 100644 --- a/alacritty_terminal/src/term/search.rs +++ b/alacritty_terminal/src/term/search.rs @@ -656,7 +656,7 @@ impl<'a, T> RegexIter<'a, T> { } } -impl<'a, T> Iterator for RegexIter<'a, T> { +impl<T> Iterator for RegexIter<'_, T> { type Item = Match; fn next(&mut self) -> Option<Self::Item> { diff --git a/alacritty_terminal/src/tty/windows/child.rs b/alacritty_terminal/src/tty/windows/child.rs index dc6b992c..573eb475 100644 --- a/alacritty_terminal/src/tty/windows/child.rs +++ b/alacritty_terminal/src/tty/windows/child.rs @@ -1,6 +1,7 @@ use std::ffi::c_void; use std::io::Error; use std::num::NonZeroU32; +use std::ptr; use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::{mpsc, Arc, Mutex}; @@ -50,7 +51,7 @@ pub struct ChildExitWatcher { wait_handle: AtomicPtr<c_void>, event_rx: mpsc::Receiver<ChildEvent>, interest: Arc<Mutex<Option<Interest>>>, - child_handle: HANDLE, + child_handle: AtomicPtr<c_void>, pid: Option<NonZeroU32>, } @@ -58,12 +59,12 @@ impl ChildExitWatcher { pub fn new(child_handle: HANDLE) -> Result<ChildExitWatcher, Error> { let (event_tx, event_rx) = mpsc::channel(); - let mut wait_handle: HANDLE = 0; + let mut wait_handle: HANDLE = ptr::null_mut(); let interest = Arc::new(Mutex::new(None)); let sender_ref = Box::new(ChildExitSender { sender: event_tx, interest: interest.clone(), - child_handle: AtomicPtr::from(child_handle as *mut c_void), + child_handle: AtomicPtr::from(child_handle), }); let success = unsafe { @@ -82,11 +83,11 @@ impl ChildExitWatcher { } else { let pid = unsafe { NonZeroU32::new(GetProcessId(child_handle)) }; Ok(ChildExitWatcher { - wait_handle: AtomicPtr::from(wait_handle as *mut c_void), event_rx, interest, - child_handle, pid, + child_handle: AtomicPtr::from(child_handle), + wait_handle: AtomicPtr::from(wait_handle), }) } } @@ -112,7 +113,7 @@ impl ChildExitWatcher { /// If you terminate the process using this handle, the terminal will get a /// timeout error, and the child watcher will emit an `Exited` event. pub fn raw_handle(&self) -> HANDLE { - self.child_handle + self.child_handle.load(Ordering::Relaxed) as HANDLE } /// Retrieve the Process ID associated to the underlying child process. diff --git a/alacritty_terminal/src/tty/windows/conpty.rs b/alacritty_terminal/src/tty/windows/conpty.rs index 28289f90..bcbc1f0e 100644 --- a/alacritty_terminal/src/tty/windows/conpty.rs +++ b/alacritty_terminal/src/tty/windows/conpty.rs @@ -72,7 +72,7 @@ impl ConptyApi { type LoadedFn = unsafe extern "system" fn() -> isize; unsafe { let hmodule = LoadLibraryW(w!("conpty.dll")); - if hmodule == 0 { + if hmodule.is_null() { return None; } let create_fn = GetProcAddress(hmodule, s!("CreatePseudoConsole"))?; diff --git a/alacritty_terminal/src/vi_mode.rs b/alacritty_terminal/src/vi_mode.rs index 7065544e..e23e9b80 100644 --- a/alacritty_terminal/src/vi_mode.rs +++ b/alacritty_terminal/src/vi_mode.rs @@ -265,14 +265,14 @@ fn semantic<T: EventListener>( } }; - // Make sure we jump above wide chars. - point = term.expand_wide(point, direction); - // Move to word boundary. if direction != side && !is_boundary(term, point, direction) { point = expand_semantic(point); } + // Make sure we jump above wide chars. + point = term.expand_wide(point, direction); + // Skip whitespace. let mut next_point = advance(term, point, direction); while !is_boundary(term, point, direction) && is_space(term, next_point) { @@ -283,6 +283,11 @@ fn semantic<T: EventListener>( // Assure minimum movement of one cell. if !is_boundary(term, point, direction) { point = advance(term, point, direction); + + // Skip over wide cell spacers. + if direction == Direction::Left { + point = term.expand_wide(point, direction); + } } // Move to word boundary. @@ -820,4 +825,42 @@ mod tests { cursor = cursor.scroll(&term, -20); assert_eq!(cursor.point, Point::new(Line(19), Column(0))); } + + #[test] + fn wide_semantic_char() { + let mut term = term(); + term.set_semantic_escape_chars("-"); + term.grid_mut()[Line(0)][Column(0)].c = 'x'; + term.grid_mut()[Line(0)][Column(1)].c = 'x'; + term.grid_mut()[Line(0)][Column(2)].c = '-'; + term.grid_mut()[Line(0)][Column(2)].flags.insert(Flags::WIDE_CHAR); + term.grid_mut()[Line(0)][Column(3)].c = ' '; + term.grid_mut()[Line(0)][Column(3)].flags.insert(Flags::WIDE_CHAR_SPACER); + term.grid_mut()[Line(0)][Column(4)].c = 'x'; + term.grid_mut()[Line(0)][Column(5)].c = 'x'; + + // Test motion to the right. + + let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(0))); + cursor = cursor.motion(&mut term, ViMotion::SemanticRight); + assert_eq!(cursor.point, Point::new(Line(0), Column(2))); + + let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(2))); + cursor = cursor.motion(&mut term, ViMotion::SemanticRight); + assert_eq!(cursor.point, Point::new(Line(0), Column(4))); + + // Test motion to the left. + + let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(5))); + cursor = cursor.motion(&mut term, ViMotion::SemanticLeft); + assert_eq!(cursor.point, Point::new(Line(0), Column(4))); + + let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(4))); + cursor = cursor.motion(&mut term, ViMotion::SemanticLeft); + assert_eq!(cursor.point, Point::new(Line(0), Column(2))); + + let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(2))); + cursor = cursor.motion(&mut term, ViMotion::SemanticLeft); + assert_eq!(cursor.point, Point::new(Line(0), Column(0))); + } } diff --git a/extra/completions/_alacritty b/extra/completions/_alacritty index 0ccce66f..a0ac0e3b 100644 --- a/extra/completions/_alacritty +++ b/extra/completions/_alacritty @@ -15,17 +15,17 @@ _alacritty() { local context curcontext="$curcontext" state line _arguments "${_arguments_options[@]}" : \ -'--embed=[X11 window ID to embed Alacritty within (decimal or hexadecimal with "0x" prefix)]:EMBED: ' \ +'--embed=[X11 window ID to embed Alacritty within (decimal or hexadecimal with "0x" prefix)]:EMBED:_default' \ '--config-file=[Specify alternative configuration file \[default\: \$XDG_CONFIG_HOME/alacritty/alacritty.toml\]]:CONFIG_FILE:_files' \ '--socket=[Path for IPC socket creation]:SOCKET:_files' \ '--working-directory=[Start the shell in the specified working directory]:WORKING_DIRECTORY:_files' \ -'*-e+[Command and args to execute (must be last argument)]:COMMAND: ' \ -'*--command=[Command and args to execute (must be last argument)]:COMMAND: ' \ -'-T+[Defines the window title \[default\: Alacritty\]]:TITLE: ' \ -'--title=[Defines the window title \[default\: Alacritty\]]:TITLE: ' \ -'--class=[Defines window class/app_id on X11/Wayland \[default\: Alacritty\]]:general> | <general>,<instance: ' \ -'*-o+[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \ -'*--option=[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \ +'*-e+[Command and args to execute (must be last argument)]:COMMAND:_default' \ +'*--command=[Command and args to execute (must be last argument)]:COMMAND:_default' \ +'-T+[Defines the window title \[default\: Alacritty\]]:TITLE:_default' \ +'--title=[Defines the window title \[default\: Alacritty\]]:TITLE:_default' \ +'--class=[Defines window class/app_id on X11/Wayland \[default\: Alacritty\]]:general> | <general>,<instance:_default' \ +'*-o+[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION:_default' \ +'*--option=[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION:_default' \ '--print-events[Print all events to STDOUT]' \ '(--daemon)--ref-test[Generates ref test]' \ '(-v)*-q[Reduces the level of verbosity (the min level is -qq)]' \ @@ -64,13 +64,13 @@ _arguments "${_arguments_options[@]}" : \ (create-window) _arguments "${_arguments_options[@]}" : \ '--working-directory=[Start the shell in the specified working directory]:WORKING_DIRECTORY:_files' \ -'*-e+[Command and args to execute (must be last argument)]:COMMAND: ' \ -'*--command=[Command and args to execute (must be last argument)]:COMMAND: ' \ -'-T+[Defines the window title \[default\: Alacritty\]]:TITLE: ' \ -'--title=[Defines the window title \[default\: Alacritty\]]:TITLE: ' \ -'--class=[Defines window class/app_id on X11/Wayland \[default\: Alacritty\]]:general> | <general>,<instance: ' \ -'*-o+[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \ -'*--option=[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \ +'*-e+[Command and args to execute (must be last argument)]:COMMAND:_default' \ +'*--command=[Command and args to execute (must be last argument)]:COMMAND:_default' \ +'-T+[Defines the window title \[default\: Alacritty\]]:TITLE:_default' \ +'--title=[Defines the window title \[default\: Alacritty\]]:TITLE:_default' \ +'--class=[Defines window class/app_id on X11/Wayland \[default\: Alacritty\]]:general> | <general>,<instance:_default' \ +'*-o+[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION:_default' \ +'*--option=[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION:_default' \ '--hold[Remain open after child process exit]' \ '-h[Print help]' \ '--help[Print help]' \ @@ -78,13 +78,13 @@ _arguments "${_arguments_options[@]}" : \ ;; (config) _arguments "${_arguments_options[@]}" : \ -'-w+[Window ID for the new config]:WINDOW_ID: ' \ -'--window-id=[Window ID for the new config]:WINDOW_ID: ' \ +'-w+[Window ID for the new config]:WINDOW_ID:_default' \ +'--window-id=[Window ID for the new config]:WINDOW_ID:_default' \ '()-r[Clear all runtime configuration changes]' \ '()--reset[Clear all runtime configuration changes]' \ '-h[Print help (see more with '\''--help'\'')]' \ '--help[Print help (see more with '\''--help'\'')]' \ -'*::options -- Configuration file options \[example\: '\''cursor.style="Beam"'\''\]:' \ +'*::options -- Configuration file options \[example\: '\''cursor.style="Beam"'\''\]:_default' \ && ret=0 ;; (help) diff --git a/extra/man/alacritty.5.scd b/extra/man/alacritty.5.scd index 8e92734a..18172487 100644 --- a/extra/man/alacritty.5.scd +++ b/extra/man/alacritty.5.scd @@ -195,6 +195,17 @@ This section documents the *[window]* table of the configuration file. Default: _"None"_ +*level* = _"Normal"_ | _"AlwaysOnTop"_ + + Sets window level. + + *Normal* + Window adheres to system's default z-order. + *AlwaysOnTop* + Window is a toplevel window. + + Default: _"Normal"_ + Example: *[window]*++ padding = { x = _3_, y = _3_ }++ @@ -601,9 +612,7 @@ This section documents the *[terminal]* table of the configuration file. Windows: _"powershell"_ Example: - *[shell]*++ -program = _"/bin/zsh"_++ -args = [_"-l"_] + *shell* = { program = _"/bin/zsh"_, args = [_"-l"_] } *osc52* = _"Disabled"_ | _"OnlyCopy"_ | _"OnlyPaste"_ | _"CopyPaste"_ diff --git a/extra/osx/Alacritty.app/Contents/Info.plist b/extra/osx/Alacritty.app/Contents/Info.plist index 08bf4270..59c70ff2 100644 --- a/extra/osx/Alacritty.app/Contents/Info.plist +++ b/extra/osx/Alacritty.app/Contents/Info.plist @@ -15,7 +15,7 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>0.14.0</string> + <string>0.15.0-dev</string> <key>CFBundleSupportedPlatforms</key> <array> <string>MacOSX</string> |