diff options
| author | Ayose <ayosec@gmail.com> | 2025-02-24 00:00:00 +0000 |
|---|---|---|
| committer | Ayose <ayosec@gmail.com> | 2025-02-24 00:00:00 +0000 |
| commit | 814e8fefc60b4457ea155d11df9f27795de830ec (patch) | |
| tree | ecc31d4e95680fd7f2a4dfb61cf30cefae387d3b /alacritty/src | |
| parent | ff44690b072de6cccffbc8e56b869b8842797692 (diff) | |
| parent | 03c2907b44b4189aac5fdeaea331f5aab5c7072e (diff) | |
| download | r-alacritty-814e8fefc60b4457ea155d11df9f27795de830ec.tar.gz r-alacritty-814e8fefc60b4457ea155d11df9f27795de830ec.tar.bz2 r-alacritty-814e8fefc60b4457ea155d11df9f27795de830ec.zip | |
Merge remote-tracking branch 'vendor/master' into graphics
Diffstat (limited to 'alacritty/src')
| -rw-r--r-- | alacritty/src/cli.rs | 9 | ||||
| -rw-r--r-- | alacritty/src/config/ui_config.rs | 4 | ||||
| -rw-r--r-- | alacritty/src/daemon.rs | 13 | ||||
| -rw-r--r-- | alacritty/src/display/mod.rs | 133 | ||||
| -rw-r--r-- | alacritty/src/display/window.rs | 20 | ||||
| -rw-r--r-- | alacritty/src/event.rs | 25 | ||||
| -rw-r--r-- | alacritty/src/input/keyboard.rs | 21 | ||||
| -rw-r--r-- | alacritty/src/input/mod.rs | 5 | ||||
| -rw-r--r-- | alacritty/src/ipc.rs | 15 | ||||
| -rw-r--r-- | alacritty/src/main.rs | 19 | ||||
| -rw-r--r-- | alacritty/src/renderer/mod.rs | 69 | ||||
| -rw-r--r-- | alacritty/src/renderer/platform.rs | 22 | ||||
| -rw-r--r-- | alacritty/src/window_context.rs | 14 |
13 files changed, 235 insertions, 134 deletions
diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index bb0a24f4..feac41bd 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -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(), } } @@ -300,6 +300,11 @@ pub struct WindowOptions { /// The window tabbing identifier to use when building a window. pub window_tabbing_id: Option<String>, + #[clap(skip)] + #[cfg(not(any(target_os = "macos", windows)))] + /// `ActivationToken` that we pass to winit. + pub activation_token: Option<String>, + /// Override configuration file options [example: 'cursor.style="Beam"']. #[clap(short = 'o', long, num_args = 1..)] option: Vec<String>, diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs index b44bda0d..960bdbbc 100644 --- a/alacritty/src/config/ui_config.rs +++ b/alacritty/src/config/ui_config.rs @@ -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. diff --git a/alacritty/src/daemon.rs b/alacritty/src/daemon.rs index c8fb88d1..fa530fa0 100644 --- a/alacritty/src/daemon.rs +++ b/alacritty/src/daemon.rs @@ -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()); } diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index a679fe21..6ff8b1e3 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -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}; @@ -395,10 +399,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, @@ -431,7 +436,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(), @@ -520,9 +525,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(), @@ -548,29 +554,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(_)) @@ -1374,6 +1420,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()), @@ -1387,10 +1435,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) { @@ -1525,47 +1575,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. diff --git a/alacritty/src/display/window.rs b/alacritty/src/display/window.rs index 1c8089bc..f9fb9272 100644 --- a/alacritty/src/display/window.rs +++ b/alacritty/src/display/window.rs @@ -2,6 +2,8 @@ use winit::platform::startup_notify::{ self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, }; +#[cfg(not(any(target_os = "macos", windows)))] +use winit::window::ActivationToken; #[cfg(all(not(feature = "x11"), not(any(target_os = "macos", windows))))] use winit::platform::wayland::WindowAttributesExtWayland; @@ -38,6 +40,7 @@ use winit::window::{ use alacritty_terminal::index::Point; +use crate::cli::WindowOptions; use crate::config::window::{Decorations, Identity, WindowConfig}; use crate::config::UiConfig; use crate::display::SizeInfo; @@ -106,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. @@ -124,9 +130,7 @@ impl Window { event_loop: &ActiveEventLoop, config: &UiConfig, identity: &Identity, - #[rustfmt::skip] - #[cfg(target_os = "macos")] - tabbing_id: &Option<String>, + options: &mut WindowOptions, #[rustfmt::skip] #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] x11_visual: Option<X11VisualInfo>, @@ -138,7 +142,7 @@ impl Window { #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] x11_visual, #[cfg(target_os = "macos")] - tabbing_id, + &options.window_tabbing_id.take(), ); if let Some(position) = config.window.position { @@ -147,7 +151,12 @@ impl Window { } #[cfg(not(any(target_os = "macos", windows)))] - if let Some(token) = event_loop.read_token_from_env() { + if let Some(token) = options + .activation_token + .take() + .map(ActivationToken::from_raw) + .or_else(|| event_loop.read_token_from_env()) + { log::debug!("Activating window with token: {token:?}"); window_attributes = window_attributes.with_activation_token(token); @@ -193,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, diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index 2ac6279d..c761f5ae 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -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); diff --git a/alacritty/src/input/keyboard.rs b/alacritty/src/input/keyboard.rs index af9bfbb2..417f599b 100644 --- a/alacritty/src/input/keyboard.rs +++ b/alacritty/src/input/keyboard.rs @@ -342,18 +342,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 diff --git a/alacritty/src/input/mod.rs b/alacritty/src/input/mod.rs index 60a50529..3f85512f 100644 --- a/alacritty/src/input/mod.rs +++ b/alacritty/src/input/mod.rs @@ -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(), diff --git a/alacritty/src/ipc.rs b/alacritty/src/ipc.rs index 3d14c4ce..919035a6 100644 --- a/alacritty/src/ipc.rs +++ b/alacritty/src/ipc.rs @@ -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. diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index 6bbf8dfd..9260bfa4 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -55,6 +55,8 @@ mod gl { #[cfg(unix)] use crate::cli::MessageOptions; +#[cfg(not(any(target_os = "macos", windows)))] +use crate::cli::SocketMessage; use crate::cli::{Options, Subcommands}; use crate::config::monitor::ConfigMonitor; use crate::config::UiConfig; @@ -89,7 +91,13 @@ fn main() -> Result<(), Box<dyn Error>> { /// `msg` subcommand entrypoint. #[cfg(unix)] -fn msg(options: MessageOptions) -> Result<(), Box<dyn Error>> { +#[allow(unused_mut)] +fn msg(mut options: MessageOptions) -> Result<(), Box<dyn Error>> { + #[cfg(not(any(target_os = "macos", windows)))] + if let SocketMessage::CreateWindow(window_options) = &mut options.message { + window_options.activation_token = + env::var("XDG_ACTIVATION_TOKEN").or_else(|_| env::var("DESKTOP_STARTUP_ID")).ok(); + } ipc::send_message(options.socket, options.message).map_err(|err| err.into()) } @@ -175,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 }; diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index a1ffb311..ce6b0914 100644 --- a/alacritty/src/renderer/mod.rs +++ b/alacritty/src/renderer/mod.rs @@ -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::graphics::UpdateQueues; @@ -101,6 +101,7 @@ pub struct Renderer { text_renderer: TextRendererProvider, rect_renderer: RectRenderer, graphics_renderer: GraphicsRenderer, + robustness: bool, } /// Wrapper around gl::GetString with error checking and reporting. @@ -148,6 +149,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. @@ -181,7 +185,7 @@ impl Renderer { } } - Ok(Self { text_renderer, rect_renderer, graphics_renderer }) + Ok(Self { text_renderer, rect_renderer, graphics_renderer, robustness }) } pub fn draw_cells<I: Iterator<Item = RenderableCell>>( @@ -212,10 +216,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; @@ -224,7 +228,7 @@ impl Renderer { Flags::empty() }; - RenderableCell { + Some(RenderableCell { point: Point::new(point.line, point.column + i), character, extra: None, @@ -233,7 +237,7 @@ impl Renderer { fg, bg, underline: fg, - } + }) }); self.draw_cells(size_info, glyph_cache, cells); @@ -287,6 +291,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(); @@ -387,7 +434,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, @@ -395,11 +442,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); } diff --git a/alacritty/src/renderer/platform.rs b/alacritty/src/renderer/platform.rs index 87ed29c2..3b2e2fce 100644 --- a/alacritty/src/renderer/platform.rs +++ b/alacritty/src/renderer/platform.rs @@ -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), diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs index cfc3cd96..a0e66cc0 100644 --- a/alacritty/src/window_context.rs +++ b/alacritty/src/window_context.rs @@ -73,7 +73,7 @@ impl WindowContext { event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Event>, config: Rc<UiConfig>, - options: WindowOptions, + mut options: WindowOptions, ) -> Result<Self, Box<dyn Error>> { let raw_display_handle = event_loop.display_handle().unwrap().as_raw(); @@ -83,7 +83,7 @@ impl WindowContext { // Windows has different order of GL platform initialization compared to any other platform; // it requires the window first. #[cfg(windows)] - let window = Window::new(event_loop, &config, &identity)?; + let window = Window::new(event_loop, &config, &identity, &mut options)?; #[cfg(windows)] let raw_window_handle = Some(window.raw_window_handle()); @@ -102,10 +102,9 @@ impl WindowContext { event_loop, &config, &identity, + &mut options, #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] gl_config.x11_visual(), - #[cfg(target_os = "macos")] - &options.window_tabbing_id, )?; // Create context. @@ -123,7 +122,7 @@ impl WindowContext { event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Event>, config: Rc<UiConfig>, - options: WindowOptions, + mut options: WindowOptions, config_overrides: ParsedOptions, ) -> Result<Self, Box<dyn Error>> { let gl_display = gl_config.display(); @@ -135,10 +134,9 @@ impl WindowContext { event_loop, &config, &identity, + &mut options, #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] gl_config.x11_visual(), - #[cfg(target_os = "macos")] - &options.window_tabbing_id, )?; // Create context. @@ -214,7 +212,7 @@ impl WindowContext { Arc::clone(&terminal), event_proxy.clone(), pty, - pty_config.hold, + pty_config.drain_on_exit, config.debug.ref_test, )?; |