1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-18 13:55:23 -05:00

Update glutin to 0.30.0

The glutin 0.30.0 update decouples glutin from winit which
provides us with basis for a multithreaded renderer. This
also improves robustness of our configuration picking,
context creation, and surface handling.

As an example we're now able to start on systems without a vsync,
we don't try to build lots of contexts to check if some config works,
and so on.

That also brings us possibility to handle context losses, but that's
a future work.

Fixes #1268.
This commit is contained in:
Kirill Chibisov 2022-11-03 19:37:54 +03:00 committed by GitHub
parent 578e08486d
commit 0e418bc2f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 776 additions and 624 deletions

View file

@ -24,8 +24,8 @@ tasks:
cargo test cargo test
- oldstable: | - oldstable: |
cd alacritty cd alacritty
rustup toolchain install --profile minimal 1.57.0 rustup toolchain install --profile minimal 1.60.0
rustup default 1.57.0 rustup default 1.60.0
cargo test cargo test
- clippy: | - clippy: |
cd alacritty cd alacritty

View file

@ -27,8 +27,8 @@ tasks:
cargo +nightly fmt -- --check cargo +nightly fmt -- --check
- oldstable: | - oldstable: |
cd alacritty cd alacritty
rustup toolchain install --profile minimal 1.57.0 rustup toolchain install --profile minimal 1.60.0
rustup default 1.57.0 rustup default 1.60.0
cargo test cargo test
- clippy: | - clippy: |
cd alacritty cd alacritty

View file

@ -19,7 +19,7 @@ jobs:
run: cargo test run: cargo test
- name: Oldstable - name: Oldstable
run: | run: |
rustup default 1.57.0 rustup default 1.60.0
cargo test cargo test
- name: Clippy - name: Clippy
run: | run: |

View file

@ -23,6 +23,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Packaging ### Packaging
- Minimum Rust version has been bumped to 1.60.0
## 0.11.0
### Packaging
- Minimum Rust version has been bumped to 1.57.0 - Minimum Rust version has been bumped to 1.57.0
- Renamed `io.alacritty.Alacritty.appdata.xml` to `org.alacritty.Alacritty.appdata.xml` - Renamed `io.alacritty.Alacritty.appdata.xml` to `org.alacritty.Alacritty.appdata.xml`
- Renamed `io.alacritty` to `org.alacritty` for `Alacritty.app` - Renamed `io.alacritty` to `org.alacritty` for `Alacritty.app`

View file

@ -42,7 +42,7 @@ and
[easy](https://github.com/alacritty/alacritty/issues?q=is%3Aopen+is%3Aissue+label%3A%22D+-+easy%22) [easy](https://github.com/alacritty/alacritty/issues?q=is%3Aopen+is%3Aissue+label%3A%22D+-+easy%22)
issues. issues.
Please note that the minimum supported version of Alacritty is Rust 1.57.0. All patches are expected Please note that the minimum supported version of Alacritty is Rust 1.60.0. All patches are expected
to work with the minimum supported version. to work with the minimum supported version.
Since `alacritty_terminal`'s version always tracks the next release, make sure that the version is Since `alacritty_terminal`'s version always tracks the next release, make sure that the version is

93
Cargo.lock generated
View file

@ -40,6 +40,7 @@ dependencies = [
"unicode-width", "unicode-width",
"wayland-client", "wayland-client",
"windows-sys", "windows-sys",
"winit",
"x11-dl", "x11-dl",
"xdg", "xdg",
] ]
@ -184,6 +185,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]] [[package]]
name = "cgl" name = "cgl"
version = "0.3.2" version = "0.3.2"
@ -660,55 +667,42 @@ dependencies = [
[[package]] [[package]]
name = "glutin" name = "glutin"
version = "0.29.1" version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "444c9ad294fdcaf20ccf6726b78f380b5450275540c9b68ab62f49726ad1c713" checksum = "34acbf502536f1d125f0fc09b6ad8824e93e6da7b99e86d3383e6b8310ba3554"
dependencies = [ dependencies = [
"bitflags",
"cfg_aliases",
"cgl", "cgl",
"cocoa", "cocoa",
"core-foundation", "core-foundation",
"glutin_egl_sys", "glutin_egl_sys",
"glutin_gles2_sys",
"glutin_glx_sys", "glutin_glx_sys",
"glutin_wgl_sys", "glutin_wgl_sys",
"libloading", "libloading",
"log",
"objc", "objc",
"once_cell", "once_cell",
"osmesa-sys",
"parking_lot 0.12.1",
"raw-window-handle 0.5.0", "raw-window-handle 0.5.0",
"wayland-client", "wayland-sys 0.30.0-beta.12",
"wayland-egl", "windows-sys",
"winapi 0.3.9", "x11-dl",
"winit",
] ]
[[package]] [[package]]
name = "glutin_egl_sys" name = "glutin_egl_sys"
version = "0.1.6" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68900f84b471f31ea1d1355567eb865a2cf446294f06cef8d653ed7bcf5f013d" checksum = "2c3c95a2d7a54bab0c74759794efb5cd63470d4504cbe85ed4114dc82c98bdc1"
dependencies = [ dependencies = [
"gl_generator", "gl_generator",
"winapi 0.3.9", "windows-sys",
]
[[package]]
name = "glutin_gles2_sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103"
dependencies = [
"gl_generator",
"objc",
] ]
[[package]] [[package]]
name = "glutin_glx_sys" name = "glutin_glx_sys"
version = "0.1.8" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93d0575865098580c5b3a423188cd959419912ea60b1e48e8b3b526f6d02468" checksum = "947c4850c58211c9627969c2b4e2674764b81ae5b47bab2c9a477d7942f96e0f"
dependencies = [ dependencies = [
"gl_generator", "gl_generator",
"x11-dl", "x11-dl",
@ -716,9 +710,9 @@ dependencies = [
[[package]] [[package]]
name = "glutin_wgl_sys" name = "glutin_wgl_sys"
version = "0.1.5" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" checksum = "20c33975a6c9d49d72c8f032a60079bf8df536954fbf9e4cee90396ace815c57"
dependencies = [ dependencies = [
"gl_generator", "gl_generator",
] ]
@ -1205,15 +1199,6 @@ version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]]
name = "osmesa-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b"
dependencies = [
"shared_library",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.11.2" version = "0.11.2"
@ -1528,16 +1513,6 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "shared_library"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
dependencies = [
"lazy_static",
"libc",
]
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.3.14" version = "0.3.14"
@ -1868,7 +1843,7 @@ dependencies = [
"scoped-tls", "scoped-tls",
"wayland-commons", "wayland-commons",
"wayland-scanner", "wayland-scanner",
"wayland-sys", "wayland-sys 0.29.5",
] ]
[[package]] [[package]]
@ -1880,7 +1855,7 @@ dependencies = [
"nix", "nix",
"once_cell", "once_cell",
"smallvec", "smallvec",
"wayland-sys", "wayland-sys 0.29.5",
] ]
[[package]] [[package]]
@ -1894,16 +1869,6 @@ dependencies = [
"xcursor", "xcursor",
] ]
[[package]]
name = "wayland-egl"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402de949f81a012926d821a2d659f930694257e76dd92b6e0042ceb27be4107d"
dependencies = [
"wayland-client",
"wayland-sys",
]
[[package]] [[package]]
name = "wayland-protocols" name = "wayland-protocols"
version = "0.29.5" version = "0.29.5"
@ -1938,6 +1903,18 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "wayland-sys"
version = "0.30.0-beta.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1117fe4570fe063122ba2b1b1e39e56fb1a73921d395f9288af06af0dd1c7f55"
dependencies = [
"dlib",
"lazy_static",
"log",
"pkg-config",
]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.60" version = "0.3.60"

View file

@ -7,7 +7,7 @@ description = "A fast, cross-platform, OpenGL terminal emulator"
readme = "README.md" readme = "README.md"
homepage = "https://github.com/alacritty/alacritty" homepage = "https://github.com/alacritty/alacritty"
edition = "2021" edition = "2021"
rust-version = "1.57.0" rust-version = "1.60.0"
[dependencies.alacritty_terminal] [dependencies.alacritty_terminal]
path = "../alacritty_terminal" path = "../alacritty_terminal"
@ -29,11 +29,13 @@ fnv = "1"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_yaml = "0.8" serde_yaml = "0.8"
serde_json = "1" serde_json = "1"
glutin = { version = "0.29.1", default-features = false, features = ["serde"] } glutin = { version = "0.30.0", default-features = false, features = ["egl", "wgl"] }
winit = { version = "0.27.4", default-features = false, features = ["serde"] }
notify = "4" notify = "4"
parking_lot = "0.12.0" parking_lot = "0.12.0"
crossfont = { version = "0.5.0", features = ["force_system_fontconfig"] } crossfont = { version = "0.5.0", features = ["force_system_fontconfig"] }
copypasta = { version = "0.8.1", default-features = false } copypasta = { version = "0.8.1", default-features = false }
raw-window-handle = "0.5.0"
libc = "0.2" libc = "0.2"
unicode-width = "0.1" unicode-width = "0.1"
bitflags = "1" bitflags = "1"
@ -74,11 +76,18 @@ embed-resource = "1.7.2"
[features] [features]
default = ["wayland", "x11"] default = ["wayland", "x11"]
x11 = ["copypasta/x11", "glutin/x11", "x11-dl", "png"] x11 = [
"copypasta/x11",
"winit/x11",
"glutin/x11",
"glutin/glx",
"x11-dl",
"png"]
wayland = [ wayland = [
"copypasta/wayland", "copypasta/wayland",
"glutin/wayland", "glutin/wayland",
"glutin/wayland-dlopen", "winit/wayland",
"glutin/wayland-csd-adwaita", "winit/wayland-dlopen",
"winit/wayland-csd-adwaita",
"wayland-client"] "wayland-client"]
nightly = [] nightly = []

View file

@ -3,11 +3,11 @@
use std::fmt::{self, Debug, Display}; use std::fmt::{self, Debug, Display};
use bitflags::bitflags; use bitflags::bitflags;
use glutin::event::VirtualKeyCode::*;
use glutin::event::{ModifiersState, MouseButton, VirtualKeyCode};
use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor}; use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use serde_yaml::Value as SerdeValue; use serde_yaml::Value as SerdeValue;
use winit::event::VirtualKeyCode::*;
use winit::event::{ModifiersState, MouseButton, VirtualKeyCode};
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
@ -1187,7 +1187,7 @@ impl<'a> Deserialize<'a> for KeyBinding {
} }
} }
/// Newtype for implementing deserialize on glutin Mods. /// Newtype for implementing deserialize on winit Mods.
/// ///
/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the /// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
/// impl below. /// impl below.
@ -1242,7 +1242,7 @@ impl<'a> de::Deserialize<'a> for ModsWrapper {
mod tests { mod tests {
use super::*; use super::*;
use glutin::event::ModifiersState; use winit::event::ModifiersState;
type MockBinding = Binding<usize>; type MockBinding = Binding<usize>;

View file

@ -2,9 +2,9 @@ use std::path::PathBuf;
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use glutin::event_loop::EventLoopProxy;
use log::{debug, error}; use log::{debug, error};
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use winit::event_loop::EventLoopProxy;
use alacritty_terminal::thread; use alacritty_terminal::thread;

View file

@ -3,11 +3,11 @@ use std::fmt::{self, Formatter};
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use glutin::event::{ModifiersState, VirtualKeyCode};
use log::error; use log::error;
use serde::de::{Error as SerdeError, MapAccess, Visitor}; use serde::de::{Error as SerdeError, MapAccess, Visitor};
use serde::{self, Deserialize, Deserializer}; use serde::{self, Deserialize, Deserializer};
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use winit::event::{ModifiersState, VirtualKeyCode};
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
use alacritty_terminal::config::{ use alacritty_terminal::config::{

View file

@ -1,10 +1,10 @@
use std::fmt::{self, Formatter}; use std::fmt::{self, Formatter};
use std::os::raw::c_ulong; use std::os::raw::c_ulong;
use glutin::window::Fullscreen;
use log::{error, warn}; use log::{error, warn};
use serde::de::{self, MapAccess, Visitor}; use serde::de::{self, MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use winit::window::Fullscreen;
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG}; use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG};
@ -116,14 +116,14 @@ impl WindowConfig {
pub fn decorations_theme_variant(&self) -> Option<&str> { pub fn decorations_theme_variant(&self) -> Option<&str> {
self.gtk_theme_variant self.gtk_theme_variant
.as_ref() .as_ref()
.or_else(|| self.decorations_theme_variant.as_ref()) .or(self.decorations_theme_variant.as_ref())
.map(|theme| theme.as_str()) .map(|theme| theme.as_str())
} }
#[inline] #[inline]
pub fn padding(&self, scale_factor: f64) -> (f32, f32) { pub fn padding(&self, scale_factor: f32) -> (f32, f32) {
let padding_x = (f32::from(self.padding.x) * scale_factor as f32).floor(); let padding_x = (f32::from(self.padding.x) * scale_factor).floor();
let padding_y = (f32::from(self.padding.y) * scale_factor as f32).floor(); let padding_y = (f32::from(self.padding.y) * scale_factor).floor();
(padding_x, padding_y) (padding_x, padding_y)
} }

View file

@ -1,7 +1,7 @@
use std::cmp; use std::cmp;
use std::iter::Peekable; use std::iter::Peekable;
use glutin::Rect; use glutin::surface::Rect;
use alacritty_terminal::term::{LineDamageBounds, TermDamageIterator}; use alacritty_terminal::term::{LineDamageBounds, TermDamageIterator};
@ -25,17 +25,23 @@ impl<'a> RenderDamageIterator<'a> {
let x = size_info.padding_x() + line_damage.left as u32 * size_info.cell_width(); let x = size_info.padding_x() + line_damage.left as u32 * size_info.cell_width();
let y = y_top - (line_damage.line + 1) as u32 * size_info.cell_height(); let y = y_top - (line_damage.line + 1) as u32 * size_info.cell_height();
let width = (line_damage.right - line_damage.left + 1) as u32 * size_info.cell_width(); let width = (line_damage.right - line_damage.left + 1) as u32 * size_info.cell_width();
Rect { x, y, height: size_info.cell_height(), width } Rect::new(x as i32, y as i32, width as i32, size_info.cell_height() as i32)
} }
// Make sure to damage near cells to include wide chars. // Make sure to damage near cells to include wide chars.
#[inline] #[inline]
fn overdamage(&self, mut rect: Rect) -> Rect { fn overdamage(&self, mut rect: Rect) -> Rect {
let size_info = &self.size_info; let size_info = &self.size_info;
rect.x = rect.x.saturating_sub(size_info.cell_width()); rect.x = rect.x.saturating_sub(size_info.cell_width() as i32);
rect.width = cmp::min(size_info.width() - rect.x, rect.width + 2 * size_info.cell_width()); rect.width = cmp::min(
rect.y = rect.y.saturating_sub(size_info.cell_height() / 2); size_info.width() as i32 - rect.x,
rect.height = cmp::min(size_info.height() - rect.y, rect.height + size_info.cell_height()); rect.width + 2 * size_info.cell_width() as i32,
);
rect.y = rect.y.saturating_sub(size_info.cell_height() as i32 / 2);
rect.height = cmp::min(
size_info.height() as i32 - rect.y,
rect.height + size_info.cell_height() as i32,
);
rect rect
} }
@ -63,7 +69,7 @@ impl<'a> Iterator for RenderDamageIterator<'a> {
} }
} }
/// Check if two given [`glutin::Rect`] overlap. /// Check if two given [`glutin::surface::Rect`] overlap.
fn rects_overlap(lhs: Rect, rhs: Rect) -> bool { fn rects_overlap(lhs: Rect, rhs: Rect) -> bool {
!( !(
// `lhs` is left of `rhs`. // `lhs` is left of `rhs`.
@ -77,12 +83,12 @@ fn rects_overlap(lhs: Rect, rhs: Rect) -> bool {
) )
} }
/// Merge two [`glutin::Rect`] by producing the smallest rectangle that contains both. /// Merge two [`glutin::surface::Rect`] by producing the smallest rectangle that contains both.
#[inline] #[inline]
fn merge_rects(lhs: Rect, rhs: Rect) -> Rect { fn merge_rects(lhs: Rect, rhs: Rect) -> Rect {
let left_x = cmp::min(lhs.x, rhs.x); let left_x = cmp::min(lhs.x, rhs.x);
let right_x = cmp::max(lhs.x + lhs.width, rhs.x + rhs.width); let right_x = cmp::max(lhs.x + lhs.width, rhs.x + rhs.width);
let y_top = cmp::max(lhs.y + lhs.height, rhs.y + rhs.height); let y_top = cmp::max(lhs.y + lhs.height, rhs.y + rhs.height);
let y_bottom = cmp::min(lhs.y, rhs.y); let y_bottom = cmp::min(lhs.y, rhs.y);
Rect { x: left_x, y: y_bottom, width: right_x - left_x, height: y_top - y_bottom } Rect::new(left_x, y_bottom, right_x - left_x, y_top - y_bottom)
} }

View file

@ -2,7 +2,7 @@ use std::cmp::Reverse;
use std::collections::HashSet; use std::collections::HashSet;
use std::iter; use std::iter;
use glutin::event::ModifiersState; use winit::event::ModifiersState;
use alacritty_terminal::grid::{BidirectionalIterator, Dimensions}; use alacritty_terminal::grid::{BidirectionalIterator, Dimensions};
use alacritty_terminal::index::{Boundary, Column, Direction, Line, Point}; use alacritty_terminal::index::{Boundary, Column, Direction, Line, Point};

View file

@ -1,23 +1,24 @@
//! The display subsystem including window management, font rasterization, and //! The display subsystem including window management, font rasterization, and
//! GPU drawing. //! GPU drawing.
use std::cmp;
use std::fmt::{self, Formatter}; use std::fmt::{self, Formatter};
use std::mem::{self, ManuallyDrop};
use std::num::NonZeroU32;
use std::ops::{Deref, DerefMut};
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::{cmp, mem};
use glutin::dpi::PhysicalSize; use glutin::context::{NotCurrentContext, PossiblyCurrentContext};
use glutin::event::ModifiersState; use glutin::prelude::*;
use glutin::event_loop::EventLoopWindowTarget; use glutin::surface::{Rect as DamageRect, Surface, SwapInterval, WindowSurface};
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
use glutin::platform::unix::EventLoopWindowTargetExtUnix; use log::{debug, info, warn};
use glutin::window::CursorIcon;
use glutin::Rect as DamageRect;
use log::{debug, info};
use parking_lot::MutexGuard; use parking_lot::MutexGuard;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] use winit::dpi::PhysicalSize;
use wayland_client::EventQueue; use winit::event::ModifiersState;
use winit::window::CursorIcon;
use crossfont::{self, Rasterize, Rasterizer}; use crossfont::{self, Rasterize, Rasterizer};
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
@ -33,9 +34,9 @@ use alacritty_terminal::term::color::Rgb;
use alacritty_terminal::term::{self, Term, TermDamage, TermMode, MIN_COLUMNS, MIN_SCREEN_LINES}; use alacritty_terminal::term::{self, Term, TermDamage, TermMode, MIN_COLUMNS, MIN_SCREEN_LINES};
use crate::config::font::Font; use crate::config::font::Font;
use crate::config::window::Dimensions;
#[cfg(not(windows))] #[cfg(not(windows))]
use crate::config::window::StartupMode; use crate::config::window::StartupMode;
use crate::config::window::{Dimensions, Identity};
use crate::config::UiConfig; use crate::config::UiConfig;
use crate::display::bell::VisualBell; use crate::display::bell::VisualBell;
use crate::display::color::List; use crate::display::color::List;
@ -84,8 +85,8 @@ pub enum Error {
/// Error in renderer. /// Error in renderer.
Render(renderer::Error), Render(renderer::Error),
/// Error during buffer swap. /// Error during context operations.
Context(glutin::ContextError), Context(glutin::error::Error),
} }
impl std::error::Error for Error { impl std::error::Error for Error {
@ -128,8 +129,8 @@ impl From<renderer::Error> for Error {
} }
} }
impl From<glutin::ContextError> for Error { impl From<glutin::error::Error> for Error {
fn from(val: glutin::ContextError) -> Self { fn from(val: glutin::error::Error) -> Self {
Error::Context(val) Error::Context(val)
} }
} }
@ -334,17 +335,17 @@ impl DisplayUpdate {
/// The display wraps a window, font rasterizer, and GPU renderer. /// The display wraps a window, font rasterizer, and GPU renderer.
pub struct Display { pub struct Display {
pub size_info: SizeInfo,
pub window: Window, pub window: Window,
pub size_info: SizeInfo,
/// Hint highlighted by the mouse. /// Hint highlighted by the mouse.
pub highlighted_hint: Option<HintMatch>, pub highlighted_hint: Option<HintMatch>,
/// Hint highlighted by the vi mode cursor. /// Hint highlighted by the vi mode cursor.
pub vi_highlighted_hint: Option<HintMatch>, pub vi_highlighted_hint: Option<HintMatch>,
#[cfg(not(any(target_os = "macos", windows)))] pub is_wayland: bool,
pub is_x11: bool,
/// UI cursor visibility for blinking. /// UI cursor visibility for blinking.
pub cursor_hidden: bool, pub cursor_hidden: bool,
@ -369,161 +370,57 @@ pub struct Display {
// Mouse point position when highlighting hints. // Mouse point position when highlighting hints.
hint_mouse_point: Option<Point>, hint_mouse_point: Option<Point>,
is_damage_supported: bool, renderer: ManuallyDrop<Renderer>,
surface: ManuallyDrop<Surface<WindowSurface>>,
context: ManuallyDrop<Replaceable<PossiblyCurrentContext>>,
debug_damage: bool, debug_damage: bool,
damage_rects: Vec<DamageRect>, damage_rects: Vec<DamageRect>,
next_frame_damage_rects: Vec<DamageRect>, next_frame_damage_rects: Vec<DamageRect>,
renderer: Renderer,
glyph_cache: GlyphCache, glyph_cache: GlyphCache,
meter: Meter, meter: Meter,
} }
/// Input method state.
#[derive(Debug, Default)]
pub struct Ime {
/// Whether the IME is enabled.
enabled: bool,
/// Current IME preedit.
preedit: Option<Preedit>,
}
impl Ime {
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn set_enabled(&mut self, is_enabled: bool) {
if is_enabled {
self.enabled = is_enabled
} else {
// Clear state when disabling IME.
*self = Default::default();
}
}
#[inline]
pub fn is_enabled(&self) -> bool {
self.enabled
}
#[inline]
pub fn set_preedit(&mut self, preedit: Option<Preedit>) {
self.preedit = preedit;
}
#[inline]
pub fn preedit(&self) -> Option<&Preedit> {
self.preedit.as_ref()
}
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Preedit {
/// The preedit text.
text: String,
/// Byte offset for cursor start into the preedit text.
///
/// `None` means that the cursor is invisible.
cursor_byte_offset: Option<usize>,
/// The cursor offset from the end of the preedit in char width.
cursor_end_offset: Option<usize>,
}
impl Preedit {
pub fn new(text: String, cursor_byte_offset: Option<usize>) -> Self {
let cursor_end_offset = if let Some(byte_offset) = cursor_byte_offset {
// Convert byte offset into char offset.
let cursor_end_offset =
text[byte_offset..].chars().fold(0, |acc, ch| acc + ch.width().unwrap_or(1));
Some(cursor_end_offset)
} else {
None
};
Self { text, cursor_byte_offset, cursor_end_offset }
}
}
/// Pending renderer updates.
///
/// All renderer updates are cached to be applied just before rendering, to avoid platform-specific
/// rendering issues.
#[derive(Debug, Default, Copy, Clone)]
pub struct RendererUpdate {
/// Should resize the window.
resize: bool,
/// Clear font caches.
clear_font_cache: bool,
}
impl Display { impl Display {
pub fn new<E>( pub fn new(
window: Window,
gl_context: NotCurrentContext,
config: &UiConfig, config: &UiConfig,
event_loop: &EventLoopWindowTarget<E>,
identity: &Identity,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue: Option<&EventQueue>,
) -> Result<Display, Error> { ) -> Result<Display, Error> {
#[cfg(any(not(feature = "x11"), target_os = "macos", windows))] #[cfg(any(not(feature = "wayland"), target_os = "macos", windows))]
let is_x11 = false; let is_wayland = false;
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
let is_x11 = event_loop.is_x11(); let is_wayland = window.wayland_surface().is_some();
// Guess scale_factor based on first monitor. On Wayland the initial frame always renders at let scale_factor = window.scale_factor as f32;
// a scale factor of 1. let rasterizer = Rasterizer::new(scale_factor)?;
let estimated_scale_factor = if cfg!(any(target_os = "macos", windows)) || is_x11 {
event_loop.available_monitors().next().map_or(1., |m| m.scale_factor())
} else {
1.
};
// Guess the target window dimensions.
debug!("Loading \"{}\" font", &config.font.normal().family); debug!("Loading \"{}\" font", &config.font.normal().family);
let font = &config.font; let mut glyph_cache = GlyphCache::new(rasterizer, &config.font)?;
let rasterizer = Rasterizer::new(estimated_scale_factor as f32)?;
let mut glyph_cache = GlyphCache::new(rasterizer, font)?;
let metrics = glyph_cache.font_metrics(); let metrics = glyph_cache.font_metrics();
let (cell_width, cell_height) = compute_cell_size(config, &metrics); let (cell_width, cell_height) = compute_cell_size(config, &metrics);
// Guess the target window size if the user has specified the number of lines/columns. // Resize the window to account for the user configured size.
let dimensions = config.window.dimensions(); if let Some(dimensions) = config.window.dimensions() {
let estimated_size = dimensions.map(|dimensions| { let size = window_size(config, dimensions, cell_width, cell_height, scale_factor);
window_size(config, dimensions, cell_width, cell_height, estimated_scale_factor) window.set_inner_size(size);
}); }
debug!("Estimated scaling factor: {}", estimated_scale_factor); // Create the GL surface to draw into.
debug!("Estimated window size: {:?}", estimated_size); let surface = renderer::platform::create_gl_surface(
debug!("Estimated cell size: {} x {}", cell_width, cell_height); &gl_context,
window.inner_size(),
// Spawn the Alacritty window. window.raw_window_handle(),
let window = Window::new(
event_loop,
config,
identity,
estimated_size,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue,
)?; )?;
// Make the context current.
let context = gl_context.make_current(&surface)?;
// Create renderer. // Create renderer.
let mut renderer = Renderer::new()?; let mut renderer = Renderer::new(&context)?;
let scale_factor = window.scale_factor;
info!("Display scale factor: {}", scale_factor);
// If the scaling factor changed update the glyph cache and mark for resize.
let should_resize = (estimated_scale_factor - window.scale_factor).abs() > f64::EPSILON;
let (cell_width, cell_height) = if should_resize {
Self::update_font_size(&mut glyph_cache, scale_factor, config, font)
} else {
(cell_width, cell_height)
};
// Load font common glyphs to accelerate rendering. // Load font common glyphs to accelerate rendering.
debug!("Filling glyph cache with common glyphs"); debug!("Filling glyph cache with common glyphs");
@ -531,14 +428,7 @@ impl Display {
glyph_cache.reset_glyph_cache(&mut api); glyph_cache.reset_glyph_cache(&mut api);
}); });
if let Some(dimensions) = dimensions.filter(|_| should_resize) { let padding = config.window.padding(window.scale_factor as f32);
// Resize the window again if the scale factor was not estimated correctly.
let size =
window_size(config, dimensions, cell_width, cell_height, window.scale_factor);
window.set_inner_size(size);
}
let padding = config.window.padding(window.scale_factor);
let viewport_size = window.inner_size(); let viewport_size = window.inner_size();
// Create new size with at least one column and row. // Create new size with at least one column and row.
@ -549,7 +439,7 @@ impl Display {
cell_height, cell_height,
padding.0, padding.0,
padding.1, padding.1,
config.window.dynamic_padding && dimensions.is_none(), config.window.dynamic_padding && config.window.dimensions().is_none(),
); );
info!("Cell size: {} x {}", cell_width, cell_height); info!("Cell size: {} x {}", cell_width, cell_height);
@ -570,8 +460,8 @@ impl Display {
// On Wayland we can safely ignore this call, since the window isn't visible until you // On Wayland we can safely ignore this call, since the window isn't visible until you
// actually draw something into it and commit those changes. // actually draw something into it and commit those changes.
#[cfg(not(any(target_os = "macos", windows)))] #[cfg(not(any(target_os = "macos", windows)))]
if is_x11 { if !is_wayland {
window.swap_buffers(); surface.swap_buffers(&context).expect("failed to swap buffers.");
renderer.finish(); renderer.finish();
} }
@ -582,24 +472,35 @@ impl Display {
match config.window.startup_mode { match config.window.startup_mode {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
StartupMode::SimpleFullscreen => window.set_simple_fullscreen(true), StartupMode::SimpleFullscreen => window.set_simple_fullscreen(true),
#[cfg(not(target_os = "macos"))] #[cfg(not(any(target_os = "macos", windows)))]
StartupMode::Maximized if is_x11 => window.set_maximized(true), StartupMode::Maximized if !is_wayland => window.set_maximized(true),
_ => (), _ => (),
} }
let hint_state = HintState::new(config.hints.alphabet()); let hint_state = HintState::new(config.hints.alphabet());
let is_damage_supported = window.swap_buffers_with_damage_supported();
let debug_damage = config.debug.highlight_damage; let debug_damage = config.debug.highlight_damage;
let (damage_rects, next_frame_damage_rects) = if is_damage_supported || debug_damage { let (damage_rects, next_frame_damage_rects) = if is_wayland || debug_damage {
let vec = Vec::with_capacity(size_info.screen_lines()); let vec = Vec::with_capacity(size_info.screen_lines());
(vec.clone(), vec) (vec.clone(), vec)
} else { } else {
(Vec::new(), Vec::new()) (Vec::new(), Vec::new())
}; };
// We use vsync everywhere except wayland.
if !is_wayland {
if let Err(err) =
surface.set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
{
warn!("Error setting vsync: {:?}", err);
}
}
Ok(Self { Ok(Self {
window, window,
renderer, context: ManuallyDrop::new(Replaceable::new(context)),
surface: ManuallyDrop::new(surface),
renderer: ManuallyDrop::new(renderer),
glyph_cache, glyph_cache,
hint_state, hint_state,
meter: Meter::new(), meter: Meter::new(),
@ -607,14 +508,12 @@ impl Display {
ime: Ime::new(), ime: Ime::new(),
highlighted_hint: None, highlighted_hint: None,
vi_highlighted_hint: None, vi_highlighted_hint: None,
#[cfg(not(any(target_os = "macos", windows)))] is_wayland,
is_x11,
cursor_hidden: false, cursor_hidden: false,
visual_bell: VisualBell::from(&config.bell), visual_bell: VisualBell::from(&config.bell),
colors: List::from(&config.colors), colors: List::from(&config.colors),
pending_update: Default::default(), pending_update: Default::default(),
pending_renderer_update: Default::default(), pending_renderer_update: Default::default(),
is_damage_supported,
debug_damage, debug_damage,
damage_rects, damage_rects,
next_frame_damage_rects, next_frame_damage_rects,
@ -622,6 +521,42 @@ impl Display {
}) })
} }
#[inline]
pub fn gl_context(&self) -> &PossiblyCurrentContext {
self.context.get()
}
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()
});
}
}
pub fn make_current(&self) {
if !self.context.get().is_current() {
self.context.make_current(&self.surface).expect("failed to make context current")
}
}
fn swap_buffers(&self) {
#[allow(clippy::single_match)]
match (self.surface.deref(), &self.context.get()) {
#[cfg(not(any(target_os = "macos", windows)))]
(Surface::Egl(surface), PossiblyCurrentContext::Egl(context))
if self.is_wayland && !self.debug_damage =>
{
surface.swap_buffers_with_damage(context, &self.damage_rects)
},
(surface, context) => surface.swap_buffers(context),
}
.expect("failed to swap buffers.");
}
/// Update font size and cell dimensions. /// Update font size and cell dimensions.
/// ///
/// This will return a tuple of the cell width and height. /// This will return a tuple of the cell width and height.
@ -690,7 +625,7 @@ impl Display {
renderer_update.resize = true renderer_update.resize = true
} }
let padding = config.window.padding(self.window.scale_factor); let padding = config.window.padding(self.window.scale_factor as f32);
self.size_info = SizeInfo::new( self.size_info = SizeInfo::new(
width, width,
@ -731,13 +666,13 @@ impl Display {
// Resize renderer. // Resize renderer.
if renderer_update.resize { if renderer_update.resize {
let physical = let width = NonZeroU32::new(self.size_info.width() as u32).unwrap();
PhysicalSize::new(self.size_info.width() as _, self.size_info.height() as _); let height = NonZeroU32::new(self.size_info.height() as u32).unwrap();
self.window.resize(physical); self.surface.resize(&self.context, width, height);
} }
// Ensure we're modifying the correct OpenGL context. // Ensure we're modifying the correct OpenGL context.
self.window.make_current(); self.make_current();
if renderer_update.clear_font_cache { if renderer_update.clear_font_cache {
self.reset_glyph_cache(); self.reset_glyph_cache();
@ -763,12 +698,8 @@ impl Display {
/// Damage the entire window. /// Damage the entire window.
fn fully_damage(&mut self) { fn fully_damage(&mut self) {
let screen_rect = DamageRect { let screen_rect =
x: 0, DamageRect::new(0, 0, self.size_info.width() as i32, self.size_info.height() as i32);
y: 0,
width: self.size_info.width() as u32,
height: self.size_info.height() as u32,
};
self.damage_rects.push(screen_rect); self.damage_rects.push(screen_rect);
} }
@ -847,7 +778,7 @@ impl Display {
drop(terminal); drop(terminal);
// Make sure this window's OpenGL context is active. // Make sure this window's OpenGL context is active.
self.window.make_current(); self.make_current();
self.renderer.clear(background_color, config.window_opacity()); self.renderer.clear(background_color, config.window_opacity());
let mut lines = RenderLines::new(); let mut lines = RenderLines::new();
@ -1036,14 +967,10 @@ impl Display {
self.request_frame(&self.window); self.request_frame(&self.window);
// Clearing debug highlights from the previous frame requires full redraw. // Clearing debug highlights from the previous frame requires full redraw.
if self.is_damage_supported && !self.debug_damage { self.swap_buffers();
self.window.swap_buffers_with_damage(&self.damage_rects);
} else {
self.window.swap_buffers();
}
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
if self.is_x11 { if !self.is_wayland {
// On X11 `swap_buffers` does not block for vsync. However the next OpenGl command // On X11 `swap_buffers` does not block for vsync. However the next OpenGl command
// will block to synchronize (this is `glClear` in Alacritty), which causes a // will block to synchronize (this is `glClear` in Alacritty), which causes a
// permanent one frame delay. // permanent one frame delay.
@ -1397,7 +1324,7 @@ impl Display {
let y_top = size_info.height() - size_info.padding_y(); let y_top = size_info.height() - size_info.padding_y();
let y = y_top - (point.line as u32 + 1) * size_info.cell_height(); let y = y_top - (point.line as u32 + 1) * size_info.cell_height();
let width = len * size_info.cell_width(); let width = len * size_info.cell_width();
DamageRect { x, y, width, height: size_info.cell_height() } DamageRect::new(x as i32, y as i32, width as i32, size_info.cell_height() as i32)
} }
/// Damage currently highlighted `Display` hints. /// Damage currently highlighted `Display` hints.
@ -1420,7 +1347,7 @@ impl Display {
/// Returns `true` if damage information should be collected, `false` otherwise. /// Returns `true` if damage information should be collected, `false` otherwise.
#[inline] #[inline]
fn collect_damage(&self) -> bool { fn collect_damage(&self) -> bool {
self.is_damage_supported || self.debug_damage self.is_wayland || self.debug_damage
} }
/// Highlight damaged rects. /// Highlight damaged rects.
@ -1462,8 +1389,138 @@ impl Display {
impl Drop for Display { impl Drop for Display {
fn drop(&mut self) { fn drop(&mut self) {
// Switch OpenGL context before dropping, otherwise objects (like programs) from other // Switch OpenGL context before dropping, otherwise objects (like programs) from other
// contexts might be deleted. // contexts might be deleted during droping renderer.
self.window.make_current() self.make_current();
unsafe {
ManuallyDrop::drop(&mut self.renderer);
ManuallyDrop::drop(&mut self.context);
ManuallyDrop::drop(&mut self.surface);
}
}
}
/// Input method state.
#[derive(Debug, Default)]
pub struct Ime {
/// Whether the IME is enabled.
enabled: bool,
/// Current IME preedit.
preedit: Option<Preedit>,
}
impl Ime {
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn set_enabled(&mut self, is_enabled: bool) {
if is_enabled {
self.enabled = is_enabled
} else {
// Clear state when disabling IME.
*self = Default::default();
}
}
#[inline]
pub fn is_enabled(&self) -> bool {
self.enabled
}
#[inline]
pub fn set_preedit(&mut self, preedit: Option<Preedit>) {
self.preedit = preedit;
}
#[inline]
pub fn preedit(&self) -> Option<&Preedit> {
self.preedit.as_ref()
}
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Preedit {
/// The preedit text.
text: String,
/// Byte offset for cursor start into the preedit text.
///
/// `None` means that the cursor is invisible.
cursor_byte_offset: Option<usize>,
/// The cursor offset from the end of the preedit in char width.
cursor_end_offset: Option<usize>,
}
impl Preedit {
pub fn new(text: String, cursor_byte_offset: Option<usize>) -> Self {
let cursor_end_offset = if let Some(byte_offset) = cursor_byte_offset {
// Convert byte offset into char offset.
let cursor_end_offset =
text[byte_offset..].chars().fold(0, |acc, ch| acc + ch.width().unwrap_or(1));
Some(cursor_end_offset)
} else {
None
};
Self { text, cursor_byte_offset, cursor_end_offset }
}
}
/// Pending renderer updates.
///
/// All renderer updates are cached to be applied just before rendering, to avoid platform-specific
/// rendering issues.
#[derive(Debug, Default, Copy, Clone)]
pub struct RendererUpdate {
/// Should resize the window.
resize: bool,
/// Clear font caches.
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()
} }
} }
@ -1486,7 +1543,7 @@ fn window_size(
dimensions: Dimensions, dimensions: Dimensions,
cell_width: f32, cell_width: f32,
cell_height: f32, cell_height: f32,
scale_factor: f64, scale_factor: f32,
) -> PhysicalSize<u32> { ) -> PhysicalSize<u32> {
let padding = config.window.padding(scale_factor); let padding = config.window.padding(scale_factor);

View file

@ -4,7 +4,7 @@ use {
std::sync::atomic::AtomicBool, std::sync::atomic::AtomicBool,
std::sync::Arc, std::sync::Arc,
glutin::platform::unix::{WindowBuilderExtUnix, WindowExtUnix}, winit::platform::unix::{WindowBuilderExtUnix, WindowExtUnix},
}; };
#[rustfmt::skip] #[rustfmt::skip]
@ -12,8 +12,8 @@ use {
use { use {
wayland_client::protocol::wl_surface::WlSurface, wayland_client::protocol::wl_surface::WlSurface,
wayland_client::{Attached, EventQueue, Proxy}, wayland_client::{Attached, EventQueue, Proxy},
glutin::platform::unix::EventLoopWindowTargetExtUnix, winit::platform::unix::EventLoopWindowTargetExtUnix,
glutin::window::Theme, winit::window::Theme,
}; };
#[rustfmt::skip] #[rustfmt::skip]
@ -21,39 +21,35 @@ use {
use { use {
std::io::Cursor, std::io::Cursor,
glutin::platform::x11::X11VisualInfo,
x11_dl::xlib::{Display as XDisplay, PropModeReplace, XErrorEvent, Xlib}, x11_dl::xlib::{Display as XDisplay, PropModeReplace, XErrorEvent, Xlib},
glutin::window::Icon, winit::window::Icon,
png::Decoder, png::Decoder,
}; };
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicU8, Ordering};
use bitflags::bitflags;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use cocoa::base::{id, NO, YES}; 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")] #[cfg(target_os = "macos")]
use objc::{msg_send, sel, sel_impl}; use objc::{msg_send, sel, sel_impl};
#[cfg(target_os = "macos")]
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; 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 alacritty_terminal::index::Point;
use crate::config::window::{Decorations, Identity, WindowConfig}; use crate::config::window::{Decorations, Identity, WindowConfig};
use crate::config::UiConfig; use crate::config::UiConfig;
use crate::display::SizeInfo; use crate::display::SizeInfo;
use crate::gl;
/// Window icon for `_NET_WM_ICON` property. /// Window icon for `_NET_WM_ICON` property.
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] #[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)] #[cfg(windows)]
const IDI_ICON: u16 = 0x101; 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. /// Window errors.
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Error creating the window. /// Error creating the window.
ContextCreation(glutin::CreationError), WindowCreation(winit::error::OsError),
/// Error dealing with fonts. /// Error dealing with fonts.
Font(crossfont::Error), Font(crossfont::Error),
/// Error manipulating the rendering context.
Context(glutin::ContextError),
} }
/// Result of fallible operations concerning a Window. /// 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 { impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self { match self {
Error::ContextCreation(err) => err.source(), Error::WindowCreation(err) => err.source(),
Error::Context(err) => err.source(),
Error::Font(err) => err.source(), Error::Font(err) => err.source(),
} }
} }
@ -103,22 +84,15 @@ impl std::error::Error for Error {
impl Display for Error { impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
Error::ContextCreation(err) => write!(f, "Error creating GL context; {}", err), Error::WindowCreation(err) => write!(f, "Error creating GL context; {}", err),
Error::Context(err) => write!(f, "Error operating on render context; {}", err),
Error::Font(err) => err.fmt(f), Error::Font(err) => err.fmt(f),
} }
} }
} }
impl From<glutin::CreationError> for Error { impl From<winit::error::OsError> for Error {
fn from(val: glutin::CreationError) -> Self { fn from(val: winit::error::OsError) -> Self {
Error::ContextCreation(val) Error::WindowCreation(val)
}
}
impl From<glutin::ContextError> for Error {
fn from(val: glutin::ContextError) -> Self {
Error::Context(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. /// A window which can be used for displaying the terminal.
/// ///
/// Wraps the underlying windowing library to provide a stable API in Alacritty. /// 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. /// Cached scale factor for quickly scaling pixel sizes.
pub scale_factor: f64, pub scale_factor: f64,
window: WinitWindow,
/// Current window title. /// Current window title.
title: String, title: String,
windowed_context: Replaceable<WindowedContext<PossiblyCurrent>>,
current_mouse_cursor: CursorIcon, current_mouse_cursor: CursorIcon,
mouse_visible: bool, mouse_visible: bool,
} }
@ -187,81 +134,65 @@ impl Window {
event_loop: &EventLoopWindowTarget<E>, event_loop: &EventLoopWindowTarget<E>,
config: &UiConfig, config: &UiConfig,
identity: &Identity, identity: &Identity,
size: Option<PhysicalSize<u32>>,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue: Option<&EventQueue>, wayland_event_queue: Option<&EventQueue>,
#[rustfmt::skip]
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
x11_visual: Option<X11VisualInfo>,
) -> Result<Window> { ) -> Result<Window> {
let identity = identity.clone(); 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 { if let Some(position) = config.window.position {
window_builder = window_builder window_builder = window_builder
.with_position(PhysicalPosition::<i32>::from((position.x, position.y))); .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. // Check if we're running Wayland to disable vsync.
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
let is_wayland = event_loop.is_wayland(); 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 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. // Text cursor.
let current_mouse_cursor = CursorIcon::Text; let current_mouse_cursor = CursorIcon::Text;
windowed_context.window().set_cursor_icon(current_mouse_cursor); window.set_cursor_icon(current_mouse_cursor);
// Enable IME. // Enable IME.
windowed_context.window().set_ime_allowed(true); 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 _);
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
if !is_wayland { if !is_wayland {
// On X11, embed the window inside another if the parent ID has been set. // On X11, embed the window inside another if the parent ID has been set.
if let Some(parent_window_id) = config.window.embed { 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))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
let wayland_surface = if is_wayland { let wayland_surface = if is_wayland {
// Attach surface to Alacritty's internal wayland queue to handle frame callbacks. // 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 _) }; let proxy: Proxy<WlSurface> = unsafe { Proxy::from_c_ptr(surface as _) };
Some(proxy.attach(wayland_event_queue.as_ref().unwrap().token())) Some(proxy.attach(wayland_event_queue.as_ref().unwrap().token()))
} else { } else {
None None
}; };
let scale_factor = windowed_context.window().scale_factor(); let scale_factor = window.scale_factor();
log::info!("Window scale factor: {}", scale_factor);
Ok(Self { Ok(Self {
current_mouse_cursor, current_mouse_cursor,
mouse_visible: true, mouse_visible: true,
windowed_context: Replaceable::new(windowed_context), window,
title: identity.title, title: identity.title,
#[cfg(not(any(target_os = "macos", windows)))] #[cfg(not(any(target_os = "macos", windows)))]
should_draw: Arc::new(AtomicBool::new(true)), should_draw: Arc::new(AtomicBool::new(true)),
@ -271,26 +202,31 @@ impl Window {
}) })
} }
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
self.window.raw_window_handle()
}
#[inline] #[inline]
pub fn set_inner_size(&self, size: PhysicalSize<u32>) { pub fn set_inner_size(&self, size: PhysicalSize<u32>) {
self.window().set_inner_size(size); self.window.set_inner_size(size);
} }
#[inline] #[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> { pub fn inner_size(&self) -> PhysicalSize<u32> {
self.window().inner_size() self.window.inner_size()
} }
#[inline] #[inline]
pub fn set_visible(&self, visibility: bool) { pub fn set_visible(&self, visibility: bool) {
self.window().set_visible(visibility); self.window.set_visible(visibility);
} }
/// Set the window title. /// Set the window title.
#[inline] #[inline]
pub fn set_title(&mut self, title: String) { pub fn set_title(&mut self, title: String) {
self.title = title; self.title = title;
self.window().set_title(&self.title); self.window.set_title(&self.title);
} }
/// Get the window title. /// Get the window title.
@ -301,14 +237,14 @@ impl Window {
#[inline] #[inline]
pub fn request_redraw(&self) { pub fn request_redraw(&self) {
self.window().request_redraw(); self.window.request_redraw();
} }
#[inline] #[inline]
pub fn set_mouse_cursor(&mut self, cursor: CursorIcon) { pub fn set_mouse_cursor(&mut self, cursor: CursorIcon) {
if cursor != self.current_mouse_cursor { if cursor != self.current_mouse_cursor {
self.current_mouse_cursor = 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) { pub fn set_mouse_visible(&mut self, visible: bool) {
if visible != self.mouse_visible { if visible != self.mouse_visible {
self.mouse_visible = visible; self.mouse_visible = visible;
self.window().set_cursor_visible(visible); self.window.set_cursor_visible(visible);
} }
} }
#[cfg(not(any(target_os = "macos", windows)))] #[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")] #[cfg(feature = "x11")]
let icon = { let icon = {
let mut decoder = Decoder::new(Cursor::new(WINDOW_ICON)); let mut decoder = Decoder::new(Cursor::new(WINDOW_ICON));
@ -351,6 +293,12 @@ impl Window {
None => builder, None => builder,
}; };
#[cfg(feature = "x11")]
let builder = match x11_visual {
Some(visual) => builder.with_x11_visual(visual.into_raw()),
None => builder,
};
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
let builder = match window_config.decorations_theme_variant() { let builder = match window_config.decorations_theme_variant() {
Some("light") => builder.with_wayland_csd_theme(Theme::Light), Some("light") => builder.with_wayland_csd_theme(Theme::Light),
@ -363,7 +311,7 @@ impl Window {
#[cfg(windows)] #[cfg(windows)]
pub fn get_platform_window(identity: &Identity, window_config: &WindowConfig) -> WindowBuilder { 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() WindowBuilder::new()
.with_title(&identity.title) .with_title(&identity.title)
@ -402,47 +350,47 @@ impl Window {
pub fn set_urgent(&self, is_urgent: bool) { pub fn set_urgent(&self, is_urgent: bool) {
let attention = if is_urgent { Some(UserAttentionType::Critical) } else { None }; 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 { pub fn id(&self) -> WindowId {
self.window().id() self.window.id()
} }
pub fn set_maximized(&self, maximized: bool) { pub fn set_maximized(&self, maximized: bool) {
self.window().set_maximized(maximized); self.window.set_maximized(maximized);
} }
pub fn set_minimized(&self, minimized: bool) { pub fn set_minimized(&self, minimized: bool) {
self.window().set_minimized(minimized); self.window.set_minimized(minimized);
} }
/// Toggle the window's fullscreen state. /// Toggle the window's fullscreen state.
pub fn toggle_fullscreen(&self) { 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. /// Toggle the window's maximized state.
pub fn toggle_maximized(&self) { pub fn toggle_maximized(&self) {
self.set_maximized(!self.window().is_maximized()); self.set_maximized(!self.window.is_maximized());
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn toggle_simple_fullscreen(&self) { 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) { pub fn set_fullscreen(&self, fullscreen: bool) {
if fullscreen { if fullscreen {
self.window().set_fullscreen(Some(Fullscreen::Borderless(None))); self.window.set_fullscreen(Some(Fullscreen::Borderless(None)));
} else { } else {
self.window().set_fullscreen(None); self.window.set_fullscreen(None);
} }
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn set_simple_fullscreen(&self, simple_fullscreen: bool) { 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))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
@ -451,7 +399,7 @@ impl Window {
} }
pub fn set_ime_allowed(&self, allowed: bool) { 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. /// 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_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()); 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)); 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") });
}
} }
/// Disable macOS window shadows. /// Disable macOS window shadows.
@ -516,7 +415,7 @@ impl Window {
/// This prevents rendering artifacts from showing up when the window is transparent. /// This prevents rendering artifacts from showing up when the window is transparent.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn set_has_shadow(&self, has_shadows: bool) { 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, RawWindowHandle::AppKit(handle) => handle.ns_window as id,
_ => return, _ => return,
}; };
@ -526,14 +425,10 @@ impl Window {
let _: id = msg_send![raw_window, setHasShadow: value]; 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))))] #[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()) { let (xlib_display, xlib_window) = match (window.xlib_display(), window.xlib_window()) {
(Some(display), Some(window)) => (display, window), (Some(display), Some(window)) => (display, window),
_ => return, _ => return,
@ -571,44 +466,3 @@ unsafe extern "C" fn xembed_error_handler(_: *mut XDisplay, _: *mut XErrorEvent)
log::error!("Could not embed into specified window."); log::error!("Could not embed into specified window.");
std::process::exit(1); 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()
}
}

View file

@ -13,20 +13,20 @@ use std::rc::Rc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{env, f32, mem}; use std::{env, f32, mem};
use glutin::dpi::PhysicalSize;
use glutin::event::{
ElementState, Event as GlutinEvent, Ime, ModifiersState, MouseButton, StartCause, WindowEvent,
};
use glutin::event_loop::{
ControlFlow, DeviceEventFilter, EventLoop, EventLoopProxy, EventLoopWindowTarget,
};
use glutin::platform::run_return::EventLoopExtRunReturn;
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use glutin::platform::unix::EventLoopWindowTargetExtUnix;
use glutin::window::WindowId;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use wayland_client::{Display as WaylandDisplay, EventQueue}; use wayland_client::{Display as WaylandDisplay, EventQueue};
use winit::dpi::PhysicalSize;
use winit::event::{
ElementState, Event as WinitEvent, Ime, ModifiersState, MouseButton, StartCause, WindowEvent,
};
use winit::event_loop::{
ControlFlow, DeviceEventFilter, EventLoop, EventLoopProxy, EventLoopWindowTarget,
};
use winit::platform::run_return::EventLoopExtRunReturn;
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use winit::platform::unix::EventLoopWindowTargetExtUnix;
use winit::window::WindowId;
use crossfont::{self, Size}; use crossfont::{self, Size};
@ -81,9 +81,9 @@ impl Event {
} }
} }
impl From<Event> for GlutinEvent<'_, Event> { impl From<Event> for WinitEvent<'_, Event> {
fn from(event: Event) -> Self { fn from(event: Event) -> Self {
GlutinEvent::UserEvent(event) WinitEvent::UserEvent(event)
} }
} }
@ -1080,10 +1080,10 @@ impl Mouse {
} }
impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> { impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
/// Handle events from glutin. /// Handle events from winit.
pub fn handle_event(&mut self, event: GlutinEvent<'_, Event>) { pub fn handle_event(&mut self, event: WinitEvent<'_, Event>) {
match event { match event {
GlutinEvent::UserEvent(Event { payload, .. }) => match payload { WinitEvent::UserEvent(Event { payload, .. }) => match payload {
EventType::ScaleFactorChanged(scale_factor, (width, height)) => { EventType::ScaleFactorChanged(scale_factor, (width, height)) => {
let display_update_pending = &mut self.ctx.display.pending_update; let display_update_pending = &mut self.ctx.display.pending_update;
@ -1171,8 +1171,8 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
EventType::IpcConfig(_) => (), EventType::IpcConfig(_) => (),
EventType::ConfigReload(_) | EventType::CreateWindow(_) => (), EventType::ConfigReload(_) | EventType::CreateWindow(_) => (),
}, },
GlutinEvent::RedrawRequested(_) => *self.ctx.dirty = true, WinitEvent::RedrawRequested(_) => *self.ctx.dirty = true,
GlutinEvent::WindowEvent { event, .. } => { WinitEvent::WindowEvent { event, .. } => {
match event { match event {
WindowEvent::CloseRequested => self.ctx.terminal.exit(), WindowEvent::CloseRequested => self.ctx.terminal.exit(),
WindowEvent::Resized(size) => { WindowEvent::Resized(size) => {
@ -1279,13 +1279,13 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
| WindowEvent::Moved(_) => (), | WindowEvent::Moved(_) => (),
} }
}, },
GlutinEvent::Suspended { .. } WinitEvent::Suspended { .. }
| GlutinEvent::NewEvents { .. } | WinitEvent::NewEvents { .. }
| GlutinEvent::DeviceEvent { .. } | WinitEvent::DeviceEvent { .. }
| GlutinEvent::MainEventsCleared | WinitEvent::MainEventsCleared
| GlutinEvent::RedrawEventsCleared | WinitEvent::RedrawEventsCleared
| GlutinEvent::Resumed | WinitEvent::Resumed
| GlutinEvent::LoopDestroyed => (), | WinitEvent::LoopDestroyed => (),
} }
} }
} }
@ -1327,6 +1327,30 @@ impl Processor {
} }
} }
/// Create initial window and load GL platform.
///
/// This will initialize the OpenGL Api and pick a config that
/// will be used for the rest of the windows.
pub fn create_initial_window(
&mut self,
event_loop: &EventLoopWindowTarget<Event>,
proxy: EventLoopProxy<Event>,
options: WindowOptions,
) -> Result<(), Box<dyn Error>> {
let window_context = WindowContext::initial(
event_loop,
proxy,
self.config.clone(),
options,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
self.wayland_event_queue.as_ref(),
)?;
self.windows.insert(window_context.id(), window_context);
Ok(())
}
/// Create a new terminal window. /// Create a new terminal window.
pub fn create_window( pub fn create_window(
&mut self, &mut self,
@ -1334,14 +1358,16 @@ impl Processor {
proxy: EventLoopProxy<Event>, proxy: EventLoopProxy<Event>,
options: WindowOptions, options: WindowOptions,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let window_context = WindowContext::new( let window = self.windows.iter().next().as_ref().unwrap().1;
self.config.clone(), let window_context = window.additional(
&options,
event_loop, event_loop,
proxy, proxy,
self.config.clone(),
options,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
self.wayland_event_queue.as_ref(), self.wayland_event_queue.as_ref(),
)?; )?;
self.windows.insert(window_context.id(), window_context); self.windows.insert(window_context.id(), window_context);
Ok(()) Ok(())
} }
@ -1369,7 +1395,7 @@ impl Processor {
let exit_code = event_loop.run_return(move |event, event_loop, control_flow| { let exit_code = event_loop.run_return(move |event, event_loop, control_flow| {
if self.config.debug.print_events { if self.config.debug.print_events {
info!("glutin event: {:?}", event); info!("winit event: {:?}", event);
} }
// Ignore all events we do not care about. // Ignore all events we do not care about.
@ -1379,7 +1405,7 @@ impl Processor {
match event { match event {
// The event loop just got initialized. Create a window. // The event loop just got initialized. Create a window.
GlutinEvent::Resumed => { WinitEvent::Resumed => {
// Creating window inside event loop is required for platforms like macOS to // Creating window inside event loop is required for platforms like macOS to
// properly initialize state, like tab management. Othwerwise the first window // properly initialize state, like tab management. Othwerwise the first window
// won't handle tabs. // won't handle tabs.
@ -1388,9 +1414,11 @@ impl Processor {
None => return, None => return,
}; };
if let Err(err) = if let Err(err) = self.create_initial_window(
self.create_window(event_loop, proxy.clone(), initial_window_options) event_loop,
{ proxy.clone(),
initial_window_options,
) {
// Log the error right away since we can't return it. // Log the error right away since we can't return it.
eprintln!("Error: {}", err); eprintln!("Error: {}", err);
*control_flow = ControlFlow::ExitWithCode(1); *control_flow = ControlFlow::ExitWithCode(1);
@ -1400,7 +1428,7 @@ impl Processor {
info!("Initialisation complete"); info!("Initialisation complete");
}, },
// Check for shutdown. // Check for shutdown.
GlutinEvent::UserEvent(Event { WinitEvent::UserEvent(Event {
window_id: Some(window_id), window_id: Some(window_id),
payload: EventType::Terminal(TerminalEvent::Exit), payload: EventType::Terminal(TerminalEvent::Exit),
}) => { }) => {
@ -1424,7 +1452,7 @@ impl Processor {
} }
}, },
// Process all pending events. // Process all pending events.
GlutinEvent::RedrawEventsCleared => { WinitEvent::RedrawEventsCleared => {
*control_flow = match scheduler.update() { *control_flow = match scheduler.update() {
Some(instant) => ControlFlow::WaitUntil(instant), Some(instant) => ControlFlow::WaitUntil(instant),
None => ControlFlow::Wait, None => ControlFlow::Wait,
@ -1445,14 +1473,12 @@ impl Processor {
&proxy, &proxy,
&mut clipboard, &mut clipboard,
&mut scheduler, &mut scheduler,
GlutinEvent::RedrawEventsCleared, WinitEvent::RedrawEventsCleared,
); );
} }
}, },
// Process config update. // Process config update.
GlutinEvent::UserEvent(Event { WinitEvent::UserEvent(Event { payload: EventType::ConfigReload(path), .. }) => {
payload: EventType::ConfigReload(path), ..
}) => {
// Clear config logs from message bar for all terminals. // Clear config logs from message bar for all terminals.
for window_context in self.windows.values_mut() { for window_context in self.windows.values_mut() {
if !window_context.message_buffer.is_empty() { if !window_context.message_buffer.is_empty() {
@ -1472,7 +1498,7 @@ impl Processor {
}, },
// Process IPC config update. // Process IPC config update.
#[cfg(unix)] #[cfg(unix)]
GlutinEvent::UserEvent(Event { WinitEvent::UserEvent(Event {
payload: EventType::IpcConfig(ipc_config), payload: EventType::IpcConfig(ipc_config),
window_id, window_id,
}) => { }) => {
@ -1485,14 +1511,14 @@ impl Processor {
} }
}, },
// Create a new terminal window. // Create a new terminal window.
GlutinEvent::UserEvent(Event { WinitEvent::UserEvent(Event {
payload: EventType::CreateWindow(options), .. payload: EventType::CreateWindow(options), ..
}) => { }) => {
// XXX Ensure that no context is current when creating a new window, otherwise // XXX Ensure that no context is current when creating a new window, otherwise
// it may lock the backing buffer of the surface of current context when asking // it may lock the backing buffer of the surface of current context when asking
// e.g. EGL on Wayland to create a new context. // e.g. EGL on Wayland to create a new context.
for window_context in self.windows.values_mut() { for window_context in self.windows.values_mut() {
window_context.display.window.make_not_current(); window_context.display.make_not_current();
} }
if let Err(err) = self.create_window(event_loop, proxy.clone(), options) { if let Err(err) = self.create_window(event_loop, proxy.clone(), options) {
@ -1500,7 +1526,7 @@ impl Processor {
} }
}, },
// Process events affecting all windows. // Process events affecting all windows.
GlutinEvent::UserEvent(event @ Event { window_id: None, .. }) => { WinitEvent::UserEvent(event @ Event { window_id: None, .. }) => {
for window_context in self.windows.values_mut() { for window_context in self.windows.values_mut() {
window_context.handle_event( window_context.handle_event(
event_loop, event_loop,
@ -1512,9 +1538,9 @@ impl Processor {
} }
}, },
// Process window-specific events. // Process window-specific events.
GlutinEvent::WindowEvent { window_id, .. } WinitEvent::WindowEvent { window_id, .. }
| GlutinEvent::UserEvent(Event { window_id: Some(window_id), .. }) | WinitEvent::UserEvent(Event { window_id: Some(window_id), .. })
| GlutinEvent::RedrawRequested(window_id) => { | WinitEvent::RedrawRequested(window_id) => {
if let Some(window_context) = self.windows.get_mut(&window_id) { if let Some(window_context) = self.windows.get_mut(&window_id) {
window_context.handle_event( window_context.handle_event(
event_loop, event_loop,
@ -1537,10 +1563,10 @@ impl Processor {
} }
/// Check if an event is irrelevant and can be skipped. /// Check if an event is irrelevant and can be skipped.
fn skip_event(event: &GlutinEvent<'_, Event>) -> bool { fn skip_event(event: &WinitEvent<'_, Event>) -> bool {
match event { match event {
GlutinEvent::NewEvents(StartCause::Init) => false, WinitEvent::NewEvents(StartCause::Init) => false,
GlutinEvent::WindowEvent { event, .. } => matches!( WinitEvent::WindowEvent { event, .. } => matches!(
event, event,
WindowEvent::KeyboardInput { is_synthetic: true, .. } WindowEvent::KeyboardInput { is_synthetic: true, .. }
| WindowEvent::TouchpadPressure { .. } | WindowEvent::TouchpadPressure { .. }
@ -1552,10 +1578,10 @@ impl Processor {
| WindowEvent::Touch(_) | WindowEvent::Touch(_)
| WindowEvent::Moved(_) | WindowEvent::Moved(_)
), ),
GlutinEvent::Suspended { .. } WinitEvent::Suspended { .. }
| GlutinEvent::NewEvents { .. } | WinitEvent::NewEvents { .. }
| GlutinEvent::MainEventsCleared | WinitEvent::MainEventsCleared
| GlutinEvent::LoopDestroyed => true, | WinitEvent::LoopDestroyed => true,
_ => false, _ => false,
} }
} }

View file

@ -1,4 +1,4 @@
//! Handle input from glutin. //! Handle input from winit.
//! //!
//! Certain key combinations should send some escape sequence back to the PTY. //! Certain key combinations should send some escape sequence back to the PTY.
//! In order to figure that out, state about which modifier keys are pressed //! In order to figure that out, state about which modifier keys are pressed
@ -12,14 +12,14 @@ use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use glutin::dpi::PhysicalPosition; use winit::dpi::PhysicalPosition;
use glutin::event::{ use winit::event::{
ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase,
}; };
use glutin::event_loop::EventLoopWindowTarget; use winit::event_loop::EventLoopWindowTarget;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use glutin::platform::macos::EventLoopWindowTargetExtMacOS; use winit::platform::macos::EventLoopWindowTargetExtMacOS;
use glutin::window::CursorIcon; use winit::window::CursorIcon;
use alacritty_terminal::ansi::{ClearMode, Handler}; use alacritty_terminal::ansi::{ClearMode, Handler};
use alacritty_terminal::event::EventListener; use alacritty_terminal::event::EventListener;
@ -51,7 +51,7 @@ const MIN_SELECTION_SCROLLING_HEIGHT: f64 = 5.;
/// Number of pixels for increasing the selection scrolling speed factor by one. /// Number of pixels for increasing the selection scrolling speed factor by one.
const SELECTION_SCROLLING_STEP: f64 = 20.; const SELECTION_SCROLLING_STEP: f64 = 20.;
/// Processes input from glutin. /// Processes input from winit.
/// ///
/// An escape sequence may be emitted in case specific keys or key combinations /// An escape sequence may be emitted in case specific keys or key combinations
/// are activated. /// are activated.
@ -990,8 +990,8 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
mod tests { mod tests {
use super::*; use super::*;
use glutin::event::{DeviceId, Event as GlutinEvent, VirtualKeyCode, WindowEvent}; use winit::event::{DeviceId, Event as WinitEvent, VirtualKeyCode, WindowEvent};
use glutin::window::WindowId; use winit::window::WindowId;
use alacritty_terminal::event::Event as TerminalEvent; use alacritty_terminal::event::Event as TerminalEvent;
@ -1158,8 +1158,8 @@ mod tests {
let mut processor = Processor::new(context); let mut processor = Processor::new(context);
let event: GlutinEvent::<'_, TerminalEvent> = $input; let event: WinitEvent::<'_, TerminalEvent> = $input;
if let GlutinEvent::WindowEvent { if let WinitEvent::WindowEvent {
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
state, state,
button, button,
@ -1199,7 +1199,7 @@ mod tests {
name: single_click, name: single_click,
initial_state: ClickState::None, initial_state: ClickState::None,
initial_button: MouseButton::Other(0), initial_button: MouseButton::Other(0),
input: GlutinEvent::WindowEvent { input: WinitEvent::WindowEvent {
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,
button: MouseButton::Left, button: MouseButton::Left,
@ -1215,7 +1215,7 @@ mod tests {
name: single_right_click, name: single_right_click,
initial_state: ClickState::None, initial_state: ClickState::None,
initial_button: MouseButton::Other(0), initial_button: MouseButton::Other(0),
input: GlutinEvent::WindowEvent { input: WinitEvent::WindowEvent {
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,
button: MouseButton::Right, button: MouseButton::Right,
@ -1231,7 +1231,7 @@ mod tests {
name: single_middle_click, name: single_middle_click,
initial_state: ClickState::None, initial_state: ClickState::None,
initial_button: MouseButton::Other(0), initial_button: MouseButton::Other(0),
input: GlutinEvent::WindowEvent { input: WinitEvent::WindowEvent {
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,
button: MouseButton::Middle, button: MouseButton::Middle,
@ -1247,7 +1247,7 @@ mod tests {
name: double_click, name: double_click,
initial_state: ClickState::Click, initial_state: ClickState::Click,
initial_button: MouseButton::Left, initial_button: MouseButton::Left,
input: GlutinEvent::WindowEvent { input: WinitEvent::WindowEvent {
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,
button: MouseButton::Left, button: MouseButton::Left,
@ -1263,7 +1263,7 @@ mod tests {
name: triple_click, name: triple_click,
initial_state: ClickState::DoubleClick, initial_state: ClickState::DoubleClick,
initial_button: MouseButton::Left, initial_button: MouseButton::Left,
input: GlutinEvent::WindowEvent { input: WinitEvent::WindowEvent {
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,
button: MouseButton::Left, button: MouseButton::Left,
@ -1279,7 +1279,7 @@ mod tests {
name: multi_click_separate_buttons, name: multi_click_separate_buttons,
initial_state: ClickState::DoubleClick, initial_state: ClickState::DoubleClick,
initial_button: MouseButton::Left, initial_button: MouseButton::Left,
input: GlutinEvent::WindowEvent { input: WinitEvent::WindowEvent {
event: WindowEvent::MouseInput { event: WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,
button: MouseButton::Right, button: MouseButton::Right,

View file

@ -6,9 +6,9 @@ use std::os::unix::net::{UnixListener, UnixStream};
use std::path::PathBuf; use std::path::PathBuf;
use std::{env, fs, process}; use std::{env, fs, process};
use glutin::event_loop::EventLoopProxy;
use glutin::window::WindowId;
use log::warn; use log::warn;
use winit::event_loop::EventLoopProxy;
use winit::window::WindowId;
use alacritty_terminal::thread; use alacritty_terminal::thread;

View file

@ -12,8 +12,8 @@ use std::sync::{Arc, Mutex};
use std::time::Instant; use std::time::Instant;
use std::{env, process}; use std::{env, process};
use glutin::event_loop::EventLoopProxy;
use log::{self, Level, LevelFilter}; use log::{self, Level, LevelFilter};
use winit::event_loop::EventLoopProxy;
use alacritty_terminal::config::LOG_TARGET_CONFIG; use alacritty_terminal::config::LOG_TARGET_CONFIG;

View file

@ -20,12 +20,12 @@ use std::fs;
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::PathBuf; use std::path::PathBuf;
use glutin::event_loop::EventLoopBuilder as GlutinEventLoopBuilder;
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
use glutin::platform::unix::EventLoopWindowTargetExtUnix;
use log::info; use log::info;
#[cfg(windows)] #[cfg(windows)]
use windows_sys::Win32::System::Console::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS}; use windows_sys::Win32::System::Console::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
use winit::event_loop::EventLoopBuilder as WinitEventLoopBuilder;
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
use winit::platform::unix::EventLoopWindowTargetExtUnix;
use alacritty_terminal::tty; use alacritty_terminal::tty;
@ -124,8 +124,8 @@ impl Drop for TemporaryFiles {
/// Creates a window, the terminal state, PTY, I/O event loop, input processor, /// Creates a window, the terminal state, PTY, I/O event loop, input processor,
/// config change monitor, and runs the main display loop. /// config change monitor, and runs the main display loop.
fn alacritty(options: Options) -> Result<(), Box<dyn Error>> { fn alacritty(options: Options) -> Result<(), Box<dyn Error>> {
// Setup glutin event loop. // Setup winit event loop.
let window_event_loop = GlutinEventLoopBuilder::<Event>::with_user_event().build(); let window_event_loop = WinitEventLoopBuilder::<Event>::with_user_event().build();
// Initialize the logger as soon as possible as to capture output from other subsystems. // Initialize the logger as soon as possible as to capture output from other subsystems.
let log_file = logging::initialize(&options, window_event_loop.create_proxy()) let log_file = logging::initialize(&options, window_event_loop.create_proxy())

View file

@ -1,8 +1,11 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::ffi::CStr; use std::ffi::{CStr, CString};
use std::fmt; use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use crossfont::Metrics; use crossfont::Metrics;
use glutin::context::PossiblyCurrentContext;
use glutin::display::{GetGlDisplay, GlDisplay};
use log::info; use log::info;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -16,6 +19,7 @@ use crate::gl;
use crate::renderer::rects::{RectRenderer, RenderRect}; use crate::renderer::rects::{RectRenderer, RenderRect};
use crate::renderer::shader::ShaderError; use crate::renderer::shader::ShaderError;
pub mod platform;
pub mod rects; pub mod rects;
mod shader; mod shader;
mod text; mod text;
@ -33,6 +37,9 @@ macro_rules! cstr {
} }
pub(crate) use cstr; pub(crate) use cstr;
/// Whether the OpenGL functions have been loaded.
pub static GL_FUNS_LOADED: AtomicBool = AtomicBool::new(false);
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Shader error. /// Shader error.
@ -80,7 +87,17 @@ impl Renderer {
/// ///
/// This will automatically pick between the GLES2 and GLSL3 renderer based on the GPU's /// This will automatically pick between the GLES2 and GLSL3 renderer based on the GPU's
/// supported OpenGL version. /// supported OpenGL version.
pub fn new() -> Result<Self, Error> { pub fn new(context: &PossiblyCurrentContext) -> Result<Self, Error> {
// We need to load OpenGL functions once per instance, but only after we make our context
// current due to WGL limitations.
if !GL_FUNS_LOADED.swap(true, Ordering::Relaxed) {
let gl_display = context.display();
gl::load_with(|symbol| {
let symbol = CString::new(symbol).unwrap();
gl_display.get_proc_address(symbol.as_c_str()).cast()
});
}
let (version, renderer) = unsafe { let (version, renderer) = unsafe {
let renderer = CStr::from_ptr(gl::GetString(gl::RENDERER) as *mut _); let renderer = CStr::from_ptr(gl::GetString(gl::RENDERER) as *mut _);
let version = CStr::from_ptr(gl::GetString(gl::SHADING_LANGUAGE_VERSION) as *mut _); let version = CStr::from_ptr(gl::GetString(gl::SHADING_LANGUAGE_VERSION) as *mut _);

View file

@ -0,0 +1,116 @@
//! The graphics platform that is used by the renderer.
use std::num::NonZeroU32;
use glutin::config::{ColorBufferType, Config, ConfigTemplateBuilder, GetGlConfig};
use glutin::context::{
ContextApi, ContextAttributesBuilder, GlProfile, NotCurrentContext, Version,
};
use glutin::display::{Display, DisplayApiPreference, GetGlDisplay};
use glutin::error::Result as GlutinResult;
use glutin::prelude::*;
use glutin::surface::{Surface, SurfaceAttributesBuilder, WindowSurface};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use winit::dpi::PhysicalSize;
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
use winit::platform::unix;
/// Create the GL display.
pub fn create_gl_display(
raw_display_handle: RawDisplayHandle,
_raw_window_handle: Option<RawWindowHandle>,
) -> GlutinResult<Display> {
#[cfg(target_os = "macos")]
let preference = DisplayApiPreference::Cgl;
#[cfg(windows)]
let preference = DisplayApiPreference::Wgl(Some(_raw_window_handle.unwrap()));
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
let preference = DisplayApiPreference::GlxThenEgl(Box::new(unix::register_xlib_error_hook));
#[cfg(all(not(feature = "x11"), not(any(target_os = "macos", windows))))]
let preference = DisplayApiPreference::Egl;
let display = unsafe { Display::new(raw_display_handle, preference)? };
log::info!("Using {}", { display.version_string() });
Ok(display)
}
pub fn pick_gl_config(
gl_display: &Display,
raw_window_handle: Option<RawWindowHandle>,
) -> Result<Config, String> {
let mut default_config = ConfigTemplateBuilder::new().with_transparency(true);
if let Some(raw_window_handle) = raw_window_handle {
default_config = default_config.compatible_with_native_window(raw_window_handle);
}
let config_10bit = default_config
.clone()
.with_buffer_type(ColorBufferType::Rgb { r_size: 10, g_size: 10, b_size: 10 })
.with_alpha_size(2);
let configs = [
default_config.clone(),
config_10bit.clone(),
default_config.with_transparency(false),
config_10bit.with_transparency(false),
];
for config in configs {
let gl_config = unsafe {
gl_display.find_configs(config.build()).ok().and_then(|mut configs| configs.next())
};
if let Some(gl_config) = gl_config {
return Ok(gl_config);
}
}
Err(String::from("failed to find suitable GL configuration."))
}
pub fn create_gl_context(
gl_display: &Display,
gl_config: &Config,
raw_window_handle: Option<RawWindowHandle>,
) -> GlutinResult<NotCurrentContext> {
let context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::OpenGl(Some(Version::new(3, 3))))
.build(raw_window_handle);
unsafe {
if let Ok(gl_context) = gl_display.create_context(gl_config, &context_attributes) {
Ok(gl_context)
} else {
let context_attributes = ContextAttributesBuilder::new()
.with_profile(GlProfile::Compatibility)
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
.build(raw_window_handle);
gl_display.create_context(gl_config, &context_attributes)
}
}
}
pub fn create_gl_surface(
gl_context: &NotCurrentContext,
size: PhysicalSize<u32>,
raw_window_handle: RawWindowHandle,
) -> GlutinResult<Surface<WindowSurface>> {
// Get the display and the config used to create that context.
let gl_display = gl_context.display();
let gl_config = gl_context.config();
let surface_attributes =
SurfaceAttributesBuilder::<WindowSurface>::new().with_srgb(Some(false)).build(
raw_window_handle,
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
// Create the GL surface to draw into.
unsafe { gl_display.create_window_surface(&gl_config, &surface_attributes) }
}

View file

@ -3,8 +3,8 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use glutin::event_loop::EventLoopProxy; use winit::event_loop::EventLoopProxy;
use glutin::window::WindowId; use winit::window::WindowId;
use crate::event::Event; use crate::event::Event;
@ -80,7 +80,7 @@ impl Scheduler {
.timers .timers
.iter() .iter()
.position(|timer| timer.deadline > deadline) .position(|timer| timer.deadline > deadline)
.unwrap_or_else(|| self.timers.len()); .unwrap_or(self.timers.len());
// Set the automatic event repeat rate. // Set the automatic event repeat rate.
let interval = if repeat { Some(interval) } else { None }; let interval = if repeat { Some(interval) } else { None };

View file

@ -12,13 +12,19 @@ use std::sync::atomic::Ordering;
use std::sync::Arc; use std::sync::Arc;
use crossfont::Size; use crossfont::Size;
use glutin::event::{Event as GlutinEvent, ModifiersState, WindowEvent}; use glutin::config::GetGlConfig;
use glutin::event_loop::{EventLoopProxy, EventLoopWindowTarget}; use glutin::context::NotCurrentContext;
use glutin::window::WindowId; use glutin::display::GetGlDisplay;
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
use glutin::platform::x11::X11GlConfigExt;
use log::{error, info}; use log::{error, info};
use raw_window_handle::HasRawDisplayHandle;
use serde_json as json; use serde_json as json;
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use wayland_client::EventQueue; use wayland_client::EventQueue;
use winit::event::{Event as WinitEvent, ModifiersState, WindowEvent};
use winit::event_loop::{EventLoopProxy, EventLoopWindowTarget};
use winit::window::WindowId;
use alacritty_config::SerdeReplace; use alacritty_config::SerdeReplace;
use alacritty_terminal::event::Event as TerminalEvent; use alacritty_terminal::event::Event as TerminalEvent;
@ -35,18 +41,19 @@ use crate::cli::IpcConfig;
use crate::cli::WindowOptions; use crate::cli::WindowOptions;
use crate::clipboard::Clipboard; use crate::clipboard::Clipboard;
use crate::config::UiConfig; use crate::config::UiConfig;
use crate::display::window::Window;
use crate::display::Display; use crate::display::Display;
use crate::event::{ActionContext, Event, EventProxy, EventType, Mouse, SearchState}; use crate::event::{ActionContext, Event, EventProxy, EventType, Mouse, SearchState};
use crate::input;
use crate::logging::LOG_TARGET_IPC_CONFIG; use crate::logging::LOG_TARGET_IPC_CONFIG;
use crate::message_bar::MessageBuffer; use crate::message_bar::MessageBuffer;
use crate::scheduler::Scheduler; use crate::scheduler::Scheduler;
use crate::{input, renderer};
/// Event context for one individual Alacritty window. /// Event context for one individual Alacritty window.
pub struct WindowContext { pub struct WindowContext {
pub message_buffer: MessageBuffer, pub message_buffer: MessageBuffer,
pub display: Display, pub display: Display,
event_queue: Vec<GlutinEvent<'static, Event>>, event_queue: Vec<WinitEvent<'static, Event>>,
terminal: Arc<FairMutex<Term<EventProxy>>>, terminal: Arc<FairMutex<Term<EventProxy>>>,
cursor_blink_timed_out: bool, cursor_blink_timed_out: bool,
modifiers: ModifiersState, modifiers: ModifiersState,
@ -68,32 +75,109 @@ pub struct WindowContext {
} }
impl WindowContext { impl WindowContext {
/// Create a new terminal window context. /// Create initial window context that dous bootstrapping the graphics Api we're going to use.
pub fn new( pub fn initial(
config: Rc<UiConfig>, event_loop: &EventLoopWindowTarget<Event>,
options: &WindowOptions,
window_event_loop: &EventLoopWindowTarget<Event>,
proxy: EventLoopProxy<Event>, proxy: EventLoopProxy<Event>,
config: Rc<UiConfig>,
options: WindowOptions,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue: Option<&EventQueue>, wayland_event_queue: Option<&EventQueue>,
) -> Result<Self, Box<dyn Error>> {
let raw_display_handle = event_loop.raw_display_handle();
let mut identity = config.window.identity.clone();
options.window_identity.override_identity_config(&mut identity);
// 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)?;
#[cfg(windows)]
let raw_window_handle = Some(window.raw_window_handle());
#[cfg(not(windows))]
let raw_window_handle = None;
let gl_display =
renderer::platform::create_gl_display(raw_display_handle, raw_window_handle)?;
let gl_config = renderer::platform::pick_gl_config(&gl_display, raw_window_handle)?;
#[cfg(not(windows))]
let window = Window::new(
event_loop,
&config,
&identity,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue,
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
gl_config.x11_visual(),
)?;
// Create context.
let gl_context =
renderer::platform::create_gl_context(&gl_display, &gl_config, raw_window_handle)?;
Self::new(window, gl_context, config, options, proxy)
}
/// Create additional context with the graphics platform other windows are using.
pub fn additional(
&self,
event_loop: &EventLoopWindowTarget<Event>,
proxy: EventLoopProxy<Event>,
config: Rc<UiConfig>,
options: WindowOptions,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue: Option<&EventQueue>,
) -> Result<Self, Box<dyn Error>> {
// Get any window and take its GL config and display to build a new context.
let (gl_display, gl_config) = {
let gl_context = self.display.gl_context();
(gl_context.display(), gl_context.config())
};
let mut identity = config.window.identity.clone();
options.window_identity.override_identity_config(&mut identity);
let window = Window::new(
event_loop,
&config,
&identity,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue,
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
gl_config.x11_visual(),
)?;
// Create context.
let raw_window_handle = window.raw_window_handle();
let gl_context = renderer::platform::create_gl_context(
&gl_display,
&gl_config,
Some(raw_window_handle),
)?;
Self::new(window, gl_context, config, options, proxy)
}
/// Create a new terminal window context.
fn new(
window: Window,
context: NotCurrentContext,
config: Rc<UiConfig>,
options: WindowOptions,
proxy: EventLoopProxy<Event>,
) -> Result<Self, Box<dyn Error>> { ) -> Result<Self, Box<dyn Error>> {
let mut pty_config = config.terminal_config.pty_config.clone(); let mut pty_config = config.terminal_config.pty_config.clone();
options.terminal_options.override_pty_config(&mut pty_config); options.terminal_options.override_pty_config(&mut pty_config);
let mut identity = config.window.identity.clone();
let preserve_title = options.window_identity.title.is_some(); let preserve_title = options.window_identity.title.is_some();
options.window_identity.override_identity_config(&mut identity);
// Create a display. // Create a display.
// //
// The display manages a window and can draw the terminal. // The display manages a window and can draw the terminal.
let display = Display::new( let display = Display::new(window, context, &config)?;
&config,
window_event_loop,
&identity,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue,
)?;
info!( info!(
"PTY dimensions: {:?} x {:?}", "PTY dimensions: {:?} x {:?}",
@ -307,17 +391,17 @@ impl WindowContext {
event_proxy: &EventLoopProxy<Event>, event_proxy: &EventLoopProxy<Event>,
clipboard: &mut Clipboard, clipboard: &mut Clipboard,
scheduler: &mut Scheduler, scheduler: &mut Scheduler,
event: GlutinEvent<'_, Event>, event: WinitEvent<'_, Event>,
) { ) {
match event { match event {
// Skip further event handling with no staged updates. // Skip further event handling with no staged updates.
GlutinEvent::RedrawEventsCleared if self.event_queue.is_empty() && !self.dirty => { WinitEvent::RedrawEventsCleared if self.event_queue.is_empty() && !self.dirty => {
return; return;
}, },
// Continue to process all pending events. // Continue to process all pending events.
GlutinEvent::RedrawEventsCleared => (), WinitEvent::RedrawEventsCleared => (),
// Remap scale_factor change event to remove the lifetime. // Remap scale_factor change event to remove the lifetime.
GlutinEvent::WindowEvent { WinitEvent::WindowEvent {
event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size }, event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size },
window_id, window_id,
} => { } => {
@ -396,7 +480,7 @@ impl WindowContext {
// Skip rendering on Wayland until we get frame event from compositor. // Skip rendering on Wayland until we get frame event from compositor.
#[cfg(not(any(target_os = "macos", windows)))] #[cfg(not(any(target_os = "macos", windows)))]
if !self.display.is_x11 && !self.display.window.should_draw.load(Ordering::Relaxed) { if self.display.is_wayland && !self.display.window.should_draw.load(Ordering::Relaxed) {
return; return;
} }

View file

@ -6,7 +6,7 @@ license = "MIT/Apache-2.0"
description = "Alacritty configuration abstractions" description = "Alacritty configuration abstractions"
homepage = "https://github.com/alacritty/alacritty" homepage = "https://github.com/alacritty/alacritty"
edition = "2021" edition = "2021"
rust-version = "1.57.0" rust-version = "1.60.0"
[dependencies] [dependencies]
log = { version = "0.4.17", features = ["serde"] } log = { version = "0.4.17", features = ["serde"] }

View file

@ -6,7 +6,7 @@ license = "MIT/Apache-2.0"
description = "Failure resistant deserialization derive" description = "Failure resistant deserialization derive"
homepage = "https://github.com/alacritty/alacritty" homepage = "https://github.com/alacritty/alacritty"
edition = "2021" edition = "2021"
rust-version = "1.57.0" rust-version = "1.60.0"
[lib] [lib]
proc-macro = true proc-macro = true

View file

@ -7,7 +7,7 @@ description = "Library for writing terminal emulators"
readme = "../README.md" readme = "../README.md"
homepage = "https://github.com/alacritty/alacritty" homepage = "https://github.com/alacritty/alacritty"
edition = "2021" edition = "2021"
rust-version = "1.57.0" rust-version = "1.60.0"
[dependencies.alacritty_config_derive] [dependencies.alacritty_config_derive]
path = "../alacritty_config_derive" path = "../alacritty_config_derive"