diff options
Diffstat (limited to 'alacritty/src/display/window.rs')
-rw-r--r-- | alacritty/src/display/window.rs | 310 |
1 files changed, 82 insertions, 228 deletions
diff --git a/alacritty/src/display/window.rs b/alacritty/src/display/window.rs index f558a654..93e83677 100644 --- a/alacritty/src/display/window.rs +++ b/alacritty/src/display/window.rs @@ -4,7 +4,7 @@ use { std::sync::atomic::AtomicBool, std::sync::Arc, - glutin::platform::unix::{WindowBuilderExtUnix, WindowExtUnix}, + winit::platform::unix::{WindowBuilderExtUnix, WindowExtUnix}, }; #[rustfmt::skip] @@ -12,8 +12,8 @@ use { use { wayland_client::protocol::wl_surface::WlSurface, wayland_client::{Attached, EventQueue, Proxy}, - glutin::platform::unix::EventLoopWindowTargetExtUnix, - glutin::window::Theme, + winit::platform::unix::EventLoopWindowTargetExtUnix, + winit::window::Theme, }; #[rustfmt::skip] @@ -21,39 +21,35 @@ use { use { std::io::Cursor, + glutin::platform::x11::X11VisualInfo, x11_dl::xlib::{Display as XDisplay, PropModeReplace, XErrorEvent, Xlib}, - glutin::window::Icon, + winit::window::Icon, png::Decoder, }; use std::fmt::{self, Display, Formatter}; -use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicU8, Ordering}; -use bitflags::bitflags; #[cfg(target_os = "macos")] use cocoa::base::{id, NO, YES}; -use glutin::dpi::{PhysicalPosition, PhysicalSize}; -use glutin::event_loop::EventLoopWindowTarget; -#[cfg(target_os = "macos")] -use glutin::platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS}; -#[cfg(windows)] -use glutin::platform::windows::IconExtWindows; -use glutin::window::{ - CursorIcon, Fullscreen, UserAttentionType, Window as GlutinWindow, WindowBuilder, WindowId, -}; -use glutin::{self, ContextBuilder, PossiblyCurrent, Rect, WindowedContext}; #[cfg(target_os = "macos")] use objc::{msg_send, sel, sel_impl}; -#[cfg(target_os = "macos")] use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use winit::dpi::{PhysicalPosition, PhysicalSize}; +use winit::event_loop::EventLoopWindowTarget; +#[cfg(target_os = "macos")] +use winit::platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS}; +#[cfg(windows)] +use winit::platform::windows::IconExtWindows; +use winit::window::{ + CursorIcon, Fullscreen, UserAttentionType, Window as WinitWindow, WindowBuilder, WindowId, +}; + use alacritty_terminal::index::Point; use crate::config::window::{Decorations, Identity, WindowConfig}; use crate::config::UiConfig; use crate::display::SizeInfo; -use crate::gl; /// Window icon for `_NET_WM_ICON` property. #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] @@ -63,28 +59,14 @@ static WINDOW_ICON: &[u8] = include_bytes!("../../extra/logo/compat/alacritty-te #[cfg(windows)] const IDI_ICON: u16 = 0x101; -/// Context creation flags from probing config. -static GL_CONTEXT_CREATION_FLAGS: AtomicU8 = AtomicU8::new(GlContextFlags::SRGB.bits); - -bitflags! { - pub struct GlContextFlags: u8 { - const EMPTY = 0b000000000; - const SRGB = 0b0000_0001; - const DEEP_COLOR = 0b0000_0010; - } -} - /// Window errors. #[derive(Debug)] pub enum Error { /// Error creating the window. - ContextCreation(glutin::CreationError), + WindowCreation(winit::error::OsError), /// Error dealing with fonts. Font(crossfont::Error), - - /// Error manipulating the rendering context. - Context(glutin::ContextError), } /// Result of fallible operations concerning a Window. @@ -93,8 +75,7 @@ type Result<T> = std::result::Result<T, Error>; impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - Error::ContextCreation(err) => err.source(), - Error::Context(err) => err.source(), + Error::WindowCreation(err) => err.source(), Error::Font(err) => err.source(), } } @@ -103,22 +84,15 @@ impl std::error::Error for Error { impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Error::ContextCreation(err) => write!(f, "Error creating GL context; {}", err), - Error::Context(err) => write!(f, "Error operating on render context; {}", err), + Error::WindowCreation(err) => write!(f, "Error creating GL context; {}", err), Error::Font(err) => err.fmt(f), } } } -impl From<glutin::CreationError> for Error { - fn from(val: glutin::CreationError) -> Self { - Error::ContextCreation(val) - } -} - -impl From<glutin::ContextError> for Error { - fn from(val: glutin::ContextError) -> Self { - Error::Context(val) +impl From<winit::error::OsError> for Error { + fn from(val: winit::error::OsError) -> Self { + Error::WindowCreation(val) } } @@ -128,34 +102,6 @@ impl From<crossfont::Error> for Error { } } -fn create_gl_window<E>( - mut window: WindowBuilder, - event_loop: &EventLoopWindowTarget<E>, - flags: GlContextFlags, - vsync: bool, - dimensions: Option<PhysicalSize<u32>>, -) -> Result<WindowedContext<PossiblyCurrent>> { - if let Some(dimensions) = dimensions { - window = window.with_inner_size(dimensions); - } - - let mut windowed_context_builder = ContextBuilder::new() - .with_srgb(flags.contains(GlContextFlags::SRGB)) - .with_vsync(vsync) - .with_hardware_acceleration(None); - - if flags.contains(GlContextFlags::DEEP_COLOR) { - windowed_context_builder = windowed_context_builder.with_pixel_format(30, 2); - } - - let windowed_context = windowed_context_builder.build_windowed(window, event_loop)?; - - // Make the context current so OpenGL operations can run. - let windowed_context = unsafe { windowed_context.make_current().map_err(|(_, err)| err)? }; - - Ok(windowed_context) -} - /// A window which can be used for displaying the terminal. /// /// Wraps the underlying windowing library to provide a stable API in Alacritty. @@ -171,10 +117,11 @@ pub struct Window { /// Cached scale factor for quickly scaling pixel sizes. pub scale_factor: f64, + window: WinitWindow, + /// Current window title. title: String, - windowed_context: Replaceable<WindowedContext<PossiblyCurrent>>, current_mouse_cursor: CursorIcon, mouse_visible: bool, } @@ -187,81 +134,65 @@ impl Window { event_loop: &EventLoopWindowTarget<E>, config: &UiConfig, identity: &Identity, - size: Option<PhysicalSize<u32>>, #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] wayland_event_queue: Option<&EventQueue>, + #[rustfmt::skip] + #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] + x11_visual: Option<X11VisualInfo>, ) -> Result<Window> { let identity = identity.clone(); - let mut window_builder = Window::get_platform_window(&identity, &config.window); + let mut window_builder = Window::get_platform_window( + &identity, + &config.window, + #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] + x11_visual, + ); if let Some(position) = config.window.position { window_builder = window_builder .with_position(PhysicalPosition::<i32>::from((position.x, position.y))); } + let window = window_builder.build(event_loop)?; + // Check if we're running Wayland to disable vsync. #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] let is_wayland = event_loop.is_wayland(); - #[cfg(any(not(feature = "wayland"), target_os = "macos", windows))] + #[cfg(all(not(feature = "wayland"), not(any(target_os = "macos", windows))))] let is_wayland = false; - let mut windowed_context = None; - let current_flags = - GlContextFlags::from_bits_truncate(GL_CONTEXT_CREATION_FLAGS.load(Ordering::Relaxed)); - for flags in [ - current_flags, - GlContextFlags::EMPTY, - GlContextFlags::SRGB | GlContextFlags::DEEP_COLOR, - GlContextFlags::DEEP_COLOR, - ] { - windowed_context = Some(create_gl_window( - window_builder.clone(), - event_loop, - flags, - !is_wayland, - size, - )); - if windowed_context.as_ref().unwrap().is_ok() { - GL_CONTEXT_CREATION_FLAGS.store(flags.bits, Ordering::Relaxed); - break; - } - } - let windowed_context = windowed_context.unwrap()?; - // Text cursor. let current_mouse_cursor = CursorIcon::Text; - windowed_context.window().set_cursor_icon(current_mouse_cursor); + window.set_cursor_icon(current_mouse_cursor); // Enable IME. - windowed_context.window().set_ime_allowed(true); - - // Set OpenGL symbol loader. This call MUST be after window.make_current on windows. - gl::load_with(|symbol| windowed_context.get_proc_address(symbol) as *const _); + window.set_ime_allowed(true); #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] if !is_wayland { // On X11, embed the window inside another if the parent ID has been set. if let Some(parent_window_id) = config.window.embed { - x_embed_window(windowed_context.window(), parent_window_id); + x_embed_window(&window, parent_window_id); } } #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] let wayland_surface = if is_wayland { // Attach surface to Alacritty's internal wayland queue to handle frame callbacks. - let surface = windowed_context.window().wayland_surface().unwrap(); + let surface = window.wayland_surface().unwrap(); let proxy: Proxy<WlSurface> = unsafe { Proxy::from_c_ptr(surface as _) }; Some(proxy.attach(wayland_event_queue.as_ref().unwrap().token())) } else { None }; - let scale_factor = windowed_context.window().scale_factor(); + let scale_factor = window.scale_factor(); + log::info!("Window scale factor: {}", scale_factor); Ok(Self { current_mouse_cursor, mouse_visible: true, - windowed_context: Replaceable::new(windowed_context), + window, title: identity.title, #[cfg(not(any(target_os = "macos", windows)))] should_draw: Arc::new(AtomicBool::new(true)), @@ -272,25 +203,30 @@ impl Window { } #[inline] + pub fn raw_window_handle(&self) -> RawWindowHandle { + self.window.raw_window_handle() + } + + #[inline] pub fn set_inner_size(&self, size: PhysicalSize<u32>) { - self.window().set_inner_size(size); + self.window.set_inner_size(size); } #[inline] pub fn inner_size(&self) -> PhysicalSize<u32> { - self.window().inner_size() + self.window.inner_size() } #[inline] pub fn set_visible(&self, visibility: bool) { - self.window().set_visible(visibility); + self.window.set_visible(visibility); } /// Set the window title. #[inline] pub fn set_title(&mut self, title: String) { self.title = title; - self.window().set_title(&self.title); + self.window.set_title(&self.title); } /// Get the window title. @@ -301,14 +237,14 @@ impl Window { #[inline] pub fn request_redraw(&self) { - self.window().request_redraw(); + self.window.request_redraw(); } #[inline] pub fn set_mouse_cursor(&mut self, cursor: CursorIcon) { if cursor != self.current_mouse_cursor { self.current_mouse_cursor = cursor; - self.window().set_cursor_icon(cursor); + self.window.set_cursor_icon(cursor); } } @@ -316,12 +252,18 @@ impl Window { pub fn set_mouse_visible(&mut self, visible: bool) { if visible != self.mouse_visible { self.mouse_visible = visible; - self.window().set_cursor_visible(visible); + self.window.set_cursor_visible(visible); } } #[cfg(not(any(target_os = "macos", windows)))] - pub fn get_platform_window(identity: &Identity, window_config: &WindowConfig) -> WindowBuilder { + pub fn get_platform_window( + identity: &Identity, + window_config: &WindowConfig, + #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] x11_visual: Option< + X11VisualInfo, + >, + ) -> WindowBuilder { #[cfg(feature = "x11")] let icon = { let mut decoder = Decoder::new(Cursor::new(WINDOW_ICON)); @@ -351,6 +293,12 @@ impl Window { None => builder, }; + #[cfg(feature = "x11")] + let builder = match x11_visual { + Some(visual) => builder.with_x11_visual(visual.into_raw()), + None => builder, + }; + #[cfg(feature = "wayland")] let builder = match window_config.decorations_theme_variant() { Some("light") => builder.with_wayland_csd_theme(Theme::Light), @@ -363,7 +311,7 @@ impl Window { #[cfg(windows)] pub fn get_platform_window(identity: &Identity, window_config: &WindowConfig) -> WindowBuilder { - let icon = glutin::window::Icon::from_resource(IDI_ICON, None); + let icon = winit::window::Icon::from_resource(IDI_ICON, None); WindowBuilder::new() .with_title(&identity.title) @@ -402,47 +350,47 @@ impl Window { pub fn set_urgent(&self, is_urgent: bool) { let attention = if is_urgent { Some(UserAttentionType::Critical) } else { None }; - self.window().request_user_attention(attention); + self.window.request_user_attention(attention); } pub fn id(&self) -> WindowId { - self.window().id() + self.window.id() } pub fn set_maximized(&self, maximized: bool) { - self.window().set_maximized(maximized); + self.window.set_maximized(maximized); } pub fn set_minimized(&self, minimized: bool) { - self.window().set_minimized(minimized); + self.window.set_minimized(minimized); } /// Toggle the window's fullscreen state. pub fn toggle_fullscreen(&self) { - self.set_fullscreen(self.window().fullscreen().is_none()); + self.set_fullscreen(self.window.fullscreen().is_none()); } /// Toggle the window's maximized state. pub fn toggle_maximized(&self) { - self.set_maximized(!self.window().is_maximized()); + self.set_maximized(!self.window.is_maximized()); } #[cfg(target_os = "macos")] pub fn toggle_simple_fullscreen(&self) { - self.set_simple_fullscreen(!self.window().simple_fullscreen()); + self.set_simple_fullscreen(!self.window.simple_fullscreen()); } pub fn set_fullscreen(&self, fullscreen: bool) { if fullscreen { - self.window().set_fullscreen(Some(Fullscreen::Borderless(None))); + self.window.set_fullscreen(Some(Fullscreen::Borderless(None))); } else { - self.window().set_fullscreen(None); + self.window.set_fullscreen(None); } } #[cfg(target_os = "macos")] pub fn set_simple_fullscreen(&self, simple_fullscreen: bool) { - self.window().set_simple_fullscreen(simple_fullscreen); + self.window.set_simple_fullscreen(simple_fullscreen); } #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] @@ -451,7 +399,7 @@ impl Window { } pub fn set_ime_allowed(&self, allowed: bool) { - self.windowed_context.window().set_ime_allowed(allowed); + self.window.set_ime_allowed(allowed); } /// Adjust the IME editor position according to the new location of the cursor. @@ -459,56 +407,7 @@ impl Window { let nspot_x = f64::from(size.padding_x() + point.column.0 as f32 * size.cell_width()); let nspot_y = f64::from(size.padding_y() + (point.line + 1) as f32 * size.cell_height()); - self.window().set_ime_position(PhysicalPosition::new(nspot_x, nspot_y)); - } - - pub fn swap_buffers(&self) { - self.windowed_context.swap_buffers().expect("swap buffers"); - } - - pub fn swap_buffers_with_damage(&self, damage: &[Rect]) { - self.windowed_context.swap_buffers_with_damage(damage).expect("swap buffes with damage"); - } - - #[cfg(any(target_os = "macos", windows))] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - // Disable damage tracking on macOS/Windows since there's no observation of it working. - false - } - - #[cfg(not(any(target_os = "macos", windows)))] - pub fn swap_buffers_with_damage_supported(&self) -> bool { - // On X11 damage tracking is behaving in unexpected ways on some NVIDIA systems. Since - // there's no compositor supporting it, damage tracking is disabled on X11. - // - // For more see https://github.com/alacritty/alacritty/issues/6051. - #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] - if self.window().xlib_window().is_some() { - return false; - } - - self.windowed_context.swap_buffers_with_damage_supported() - } - - pub fn resize(&self, size: PhysicalSize<u32>) { - self.windowed_context.resize(size); - } - - pub fn make_not_current(&mut self) { - if self.windowed_context.is_current() { - self.windowed_context.replace_with(|context| unsafe { - // We do ensure that context is current before any rendering operation due to multi - // window support, so we don't need extra "type aid" from glutin here. - context.make_not_current().expect("context swap").treat_as_current() - }); - } - } - - pub fn make_current(&mut self) { - if !self.windowed_context.is_current() { - self.windowed_context - .replace_with(|context| unsafe { context.make_current().expect("context swap") }); - } + self.window.set_ime_position(PhysicalPosition::new(nspot_x, nspot_y)); } /// Disable macOS window shadows. @@ -516,7 +415,7 @@ 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 raw_window = match self.window().raw_window_handle() { + let raw_window = match self.raw_window_handle() { RawWindowHandle::AppKit(handle) => handle.ns_window as id, _ => return, }; @@ -526,14 +425,10 @@ impl Window { let _: id = msg_send![raw_window, setHasShadow: value]; } } - - fn window(&self) -> &GlutinWindow { - self.windowed_context.window() - } } #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] -fn x_embed_window(window: &GlutinWindow, parent_id: std::os::raw::c_ulong) { +fn x_embed_window(window: &WinitWindow, parent_id: std::os::raw::c_ulong) { let (xlib_display, xlib_window) = match (window.xlib_display(), window.xlib_window()) { (Some(display), Some(window)) => (display, window), _ => return, @@ -571,44 +466,3 @@ unsafe extern "C" fn xembed_error_handler(_: *mut XDisplay, _: *mut XErrorEvent) log::error!("Could not embed into specified window."); std::process::exit(1); } - -/// 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() - } -} |