aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2020-05-04 02:29:11 +0300
committerGitHub <noreply@github.com>2020-05-03 23:29:11 +0000
commit04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23 (patch)
tree44f65d044e8c76d44e2cb1fb424a009cc4acc646
parenta425fe6bfe734bb80dce334d6f1c53e0bd41c0db (diff)
downloadalacritty-04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23.tar.gz
alacritty-04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23.zip
Use frame callbacks instead of vsync on Wayland
Instead of blocking on vsync, Alacritty now requests a notification from wayland about when the next frame should be rendered. this helps with input latency, since it gives alacritty more time to process events before a redraw. it also prevents alacritty from drawing unless the compositor tells it to do so. Fixes #2851.
-rw-r--r--CHANGELOG.md1
-rw-r--r--Cargo.lock62
-rw-r--r--alacritty/Cargo.toml1
-rw-r--r--alacritty/src/display.rs154
-rw-r--r--alacritty/src/event.rs65
-rw-r--r--alacritty/src/window.rs65
6 files changed, 274 insertions, 74 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c99e07be..eda674f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fallback to `LC_CTYPE=UTF-8` on macOS without valid system locale
- Resize lag on launch under some X11 wms
- Increased input latency due to vsync behavior on X11
+- Freeze when application is invisible on Wayland
## 0.4.2
diff --git a/Cargo.lock b/Cargo.lock
index 1904799c..bb7bae20 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -38,6 +38,7 @@ dependencies = [
"time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"urlocator 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wayland-client 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1196,6 +1197,11 @@ dependencies = [
]
[[package]]
+name = "once_cell"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "openssl"
version = "0.10.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1533,6 +1539,11 @@ dependencies = [
]
[[package]]
+name = "scoped-tls"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1950,6 +1961,21 @@ dependencies = [
]
[[package]]
+name = "wayland-client"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wayland-commons 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wayland-scanner 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wayland-sys 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "wayland-commons"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1959,6 +1985,17 @@ dependencies = [
]
[[package]]
+name = "wayland-commons"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wayland-sys 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "wayland-protocols"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1980,6 +2017,16 @@ dependencies = [
]
[[package]]
+name = "wayland-scanner"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xml-rs 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "wayland-sys"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1989,6 +2036,15 @@ dependencies = [
]
[[package]]
+name = "wayland-sys"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2295,6 +2351,7 @@ dependencies = [
"checksum objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
"checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
"checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
+"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
"checksum openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)" = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
"checksum openssl-sys 0.9.55 (registry+https://github.com/rust-lang/crates.io-index)" = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8"
@@ -2337,6 +2394,7 @@ dependencies = [
"checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
"checksum schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19"
+"checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
"checksum security-framework 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a"
"checksum security-framework-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddb15a5fec93b7021b8a9e96009c5d8d51c15673569f7c0f6b7204e5b7b404f"
@@ -2389,10 +2447,14 @@ dependencies = [
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum wayland-client 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda"
+"checksum wayland-client 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6eda8c06906f6572f840930588ce28f021485bc9679af0191aeb753ad6c7b46b"
"checksum wayland-commons 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb"
+"checksum wayland-commons 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)" = "951aaed76c01185bff7ec68426769c94e88b36113dfe73f8e2356feaa5f09c1f"
"checksum wayland-protocols 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9"
"checksum wayland-scanner 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d"
+"checksum wayland-scanner 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21ae379761543b2f41ce88010d0f4ee489218c8e8cfa42dd8f070fdb263aa971"
"checksum wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4"
+"checksum wayland-sys 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fab1f1d2ab35838c5fcbbeebee42cf15721c5fdc1add1f13d0e70e219b94e013"
"checksum which 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml
index 97a69a76..5e390fa9 100644
--- a/alacritty/Cargo.toml
+++ b/alacritty/Cargo.toml
@@ -41,6 +41,7 @@ dirs = "2.0.2"
[target.'cfg(not(any(target_os="windows", target_os="macos")))'.dependencies]
x11-dl = "2"
+wayland-client = { version = "0.26", features = ["dlopen"] }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.7", features = ["impl-default", "wincon"]}
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs
index 980b0ea0..aabd2631 100644
--- a/alacritty/src/display.rs
+++ b/alacritty/src/display.rs
@@ -16,6 +16,8 @@
//! GPU drawing.
use std::f64;
use std::fmt::{self, Formatter};
+#[cfg(not(any(target_os = "macos", windows)))]
+use std::sync::atomic::Ordering;
use std::time::Instant;
use glutin::dpi::{PhysicalPosition, PhysicalSize};
@@ -26,6 +28,8 @@ use glutin::platform::unix::EventLoopWindowTargetExtUnix;
use glutin::window::CursorIcon;
use log::{debug, info};
use parking_lot::MutexGuard;
+#[cfg(not(any(target_os = "macos", windows)))]
+use wayland_client::{Display as WaylandDisplay, EventQueue};
use font::{self, Rasterize};
@@ -47,16 +51,16 @@ use crate::window::{self, Window};
#[derive(Debug)]
pub enum Error {
- /// Error with window management
+ /// Error with window management.
Window(window::Error),
- /// Error dealing with fonts
+ /// Error dealing with fonts.
Font(font::Error),
- /// Error in renderer
+ /// Error in renderer.
Render(renderer::Error),
- /// Error during buffer swap
+ /// Error during buffer swap.
ContextError(glutin::ContextError),
}
@@ -106,7 +110,7 @@ impl From<glutin::ContextError> for Error {
}
}
-/// The display wraps a window, font rasterizer, and GPU renderer
+/// The display wraps a window, font rasterizer, and GPU renderer.
pub struct Display {
pub size_info: SizeInfo,
pub window: Window,
@@ -115,6 +119,9 @@ pub struct Display {
/// Currently highlighted URL.
pub highlighted_url: Option<Url>,
+ #[cfg(not(any(target_os = "macos", windows)))]
+ pub wayland_event_queue: Option<EventQueue>,
+
renderer: QuadRenderer,
glyph_cache: GlyphCache,
meter: Meter,
@@ -124,11 +131,11 @@ pub struct Display {
impl Display {
pub fn new(config: &Config, event_loop: &EventLoop<Event>) -> Result<Display, Error> {
- // Guess DPR based on first monitor
+ // Guess DPR based on first monitor.
let estimated_dpr =
event_loop.available_monitors().next().map(|m| m.scale_factor()).unwrap_or(1.);
- // Guess the target window dimensions
+ // Guess the target window dimensions.
let metrics = GlyphCache::static_metrics(config.font.clone(), estimated_dpr)?;
let (cell_width, cell_height) = compute_cell_size(config, &metrics);
let dimensions =
@@ -138,19 +145,37 @@ impl Display {
debug!("Estimated Cell Size: {} x {}", cell_width, cell_height);
debug!("Estimated Dimensions: {:?}", dimensions);
- // Create the window where Alacritty will be displayed
+ #[cfg(not(any(target_os = "macos", windows)))]
+ let mut wayland_event_queue = None;
+
+ // Initialize Wayland event queue, to handle Wayland callbacks.
+ #[cfg(not(any(target_os = "macos", windows)))]
+ {
+ if let Some(display) = event_loop.wayland_display() {
+ let display = unsafe { WaylandDisplay::from_external_display(display as _) };
+ wayland_event_queue = Some(display.create_event_queue());
+ }
+ }
+
+ // Create the window where Alacritty will be displayed.
let size = dimensions.map(|(width, height)| PhysicalSize::new(width, height));
- // Spawn window
- let mut window = Window::new(event_loop, &config, size)?;
+ // Spawn window.
+ let mut window = Window::new(
+ event_loop,
+ &config,
+ size,
+ #[cfg(not(any(target_os = "macos", windows)))]
+ wayland_event_queue.as_ref(),
+ )?;
let dpr = window.scale_factor();
info!("Device pixel ratio: {}", dpr);
- // get window properties for initializing the other subsystems
+ // get window properties for initializing the other subsystems.
let viewport_size = window.inner_size();
- // Create renderer
+ // Create renderer.
let mut renderer = QuadRenderer::new()?;
let (glyph_cache, cell_width, cell_height) =
@@ -169,7 +194,7 @@ impl Display {
window.set_inner_size(PhysicalSize::new(width, height));
}
} else if config.window.dynamic_padding {
- // Make sure additional padding is spread evenly
+ // Make sure additional padding is spread evenly.
padding_x = dynamic_padding(padding_x, viewport_size.width as f32, cell_width);
padding_y = dynamic_padding(padding_y, viewport_size.height as f32, cell_height);
}
@@ -180,7 +205,7 @@ impl Display {
info!("Cell Size: {} x {}", cell_width, cell_height);
info!("Padding: {} x {}", padding_x, padding_y);
- // Create new size with at least one column and row
+ // Create new size with at least one column and row.
let size_info = SizeInfo {
dpr,
width: (viewport_size.width as f32).max(cell_width + 2. * padding_x),
@@ -191,10 +216,10 @@ impl Display {
padding_y,
};
- // Update OpenGL projection
+ // Update OpenGL projection.
renderer.resize(&size_info);
- // Clear screen
+ // Call `clear` before showing the window, to make sure the surface is initialized.
let background_color = config.colors.primary.background;
renderer.with_api(&config, &size_info, |api| {
api.clear(background_color);
@@ -203,12 +228,10 @@ impl Display {
#[cfg(not(any(target_os = "macos", windows)))]
let is_x11 = event_loop.is_x11();
- // We should call `clear` when window is offscreen, so when `window.show()` happens it
- // would be with background color instead of uninitialized surface.
#[cfg(not(any(target_os = "macos", windows)))]
{
// On Wayland we can safely ignore this call, since the window isn't visible until you
- // actually draw something into it.
+ // actually draw something into it and commit those changes.
if is_x11 {
window.swap_buffers();
renderer.with_api(&config, &size_info, |api| {
@@ -219,10 +242,10 @@ impl Display {
window.set_visible(true);
- // Set window position
+ // Set window position.
//
// TODO: replace `set_position` with `with_position` once available
- // Upstream issue: https://github.com/rust-windowing/winit/issues/806
+ // Upstream issue: https://github.com/rust-windowing/winit/issues/806.
if let Some(position) = config.window.position {
window.set_outer_position(PhysicalPosition::from((position.x, position.y)));
}
@@ -247,6 +270,8 @@ impl Display {
highlighted_url: None,
#[cfg(not(any(target_os = "macos", windows)))]
is_x11,
+ #[cfg(not(any(target_os = "macos", windows)))]
+ wayland_event_queue,
})
}
@@ -258,7 +283,7 @@ impl Display {
let font = config.font.clone();
let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?;
- // Initialize glyph cache
+ // Initialize glyph cache.
let glyph_cache = {
info!("Initializing glyph cache...");
let init_start = Instant::now();
@@ -281,7 +306,7 @@ impl Display {
Ok((glyph_cache, cw, ch))
}
- /// Update font size and cell dimensions
+ /// Update font size and cell dimensions.
fn update_glyph_cache(&mut self, config: &Config, font: Font) {
let size_info = &mut self.size_info;
let cache = &mut self.glyph_cache;
@@ -290,7 +315,7 @@ impl Display {
let _ = cache.update_font_size(font, size_info.dpr, &mut api);
});
- // Update cell size
+ // Update cell size.
let (cell_width, cell_height) = compute_cell_size(config, &self.glyph_cache.font_metrics());
size_info.cell_width = cell_width;
size_info.cell_height = cell_height;
@@ -313,7 +338,7 @@ impl Display {
config: &Config,
update_pending: DisplayUpdate,
) {
- // Update font size and cell dimensions
+ // Update font size and cell dimensions.
if let Some(font) = update_pending.font {
self.update_glyph_cache(config, font);
} else if update_pending.cursor {
@@ -323,18 +348,18 @@ impl Display {
let cell_width = self.size_info.cell_width;
let cell_height = self.size_info.cell_height;
- // Recalculate padding
+ // Recalculate padding.
let mut padding_x = f32::from(config.window.padding.x) * self.size_info.dpr as f32;
let mut padding_y = f32::from(config.window.padding.y) * self.size_info.dpr as f32;
- // Update the window dimensions
+ // Update the window dimensions.
if let Some(size) = update_pending.dimensions {
- // Ensure we have at least one column and row
+ // Ensure we have at least one column and row.
self.size_info.width = (size.width as f32).max(cell_width + 2. * padding_x);
self.size_info.height = (size.height as f32).max(cell_height + 2. * padding_y);
}
- // Distribute excess padding equally on all sides
+ // Distribute excess padding equally on all sides.
if config.window.dynamic_padding {
padding_x = dynamic_padding(padding_x, self.size_info.width, cell_width);
padding_y = dynamic_padding(padding_y, self.size_info.height, cell_height);
@@ -345,29 +370,29 @@ impl Display {
let mut pty_size = self.size_info;
- // Subtract message bar lines from pty size
+ // Subtract message bar lines from pty size.
if let Some(message) = message_buffer.message() {
let lines = message.text(&self.size_info).len();
pty_size.height -= pty_size.cell_height * lines as f32;
}
- // Resize PTY
+ // Resize PTY.
pty_resize_handle.on_resize(&pty_size);
- // Resize terminal
+ // Resize terminal.
terminal.resize(&pty_size);
- // Resize renderer
+ // Resize renderer.
let physical = PhysicalSize::new(self.size_info.width as u32, self.size_info.height as u32);
self.window.resize(physical);
self.renderer.resize(&self.size_info);
}
- /// Draw the screen
+ /// Draw the screen.
///
/// A reference to Term whose state is being drawn must be provided.
///
- /// This call may block if vsync is enabled
+ /// This call may block if vsync is enabled.
pub fn draw<T>(
&mut self,
terminal: MutexGuard<'_, Term<T>>,
@@ -393,11 +418,11 @@ impl Display {
None
};
- // Update IME position
+ // Update IME position.
#[cfg(not(windows))]
self.window.update_ime_position(&terminal, &self.size_info);
- // Drop terminal as early as possible to free lock
+ // Drop terminal as early as possible to free lock.
drop(terminal);
self.renderer.with_api(&config, &size_info, |api| {
@@ -407,20 +432,20 @@ impl Display {
let mut lines = RenderLines::new();
let mut urls = Urls::new();
- // Draw grid
+ // Draw grid.
{
let _sampler = self.meter.sampler();
self.renderer.with_api(&config, &size_info, |mut api| {
- // Iterate over all non-empty cells in the grid
+ // Iterate over all non-empty cells in the grid.
for cell in grid_cells {
- // Update URL underlines
+ // Update URL underlines.
urls.update(size_info.cols().0, cell);
- // Update underline/strikeout
+ // Update underline/strikeout.
lines.update(cell);
- // Draw the cell
+ // Draw the cell.
api.render_cell(cell, glyph_cache);
}
});
@@ -428,7 +453,7 @@ impl Display {
let mut rects = lines.rects(&metrics, &size_info);
- // Update visible URLs
+ // Update visible URLs.
self.urls = urls;
if let Some(url) = self.urls.highlighted(config, mouse, mods, mouse_mode, selection) {
rects.append(&mut url.rects(&metrics, &size_info));
@@ -446,14 +471,14 @@ impl Display {
}
}
- // Highlight URLs at the vi mode cursor position
+ // Highlight URLs at the vi mode cursor position.
if let Some(vi_mode_cursor) = vi_mode_cursor {
if let Some(url) = self.urls.find_at(vi_mode_cursor.point) {
rects.append(&mut url.rects(&metrics, &size_info));
}
}
- // Push visual bell after url/underline/strikeout rects
+ // Push visual bell after url/underline/strikeout rects.
if visual_bell_intensity != 0. {
let visual_bell_rect = RenderRect::new(
0.,
@@ -469,19 +494,19 @@ impl Display {
if let Some(message) = message_buffer.message() {
let text = message.text(&size_info);
- // Create a new rectangle for the background
+ // Create a new rectangle for the background.
let start_line = size_info.lines().0 - text.len();
let y = size_info.cell_height.mul_add(start_line as f32, size_info.padding_y);
let message_bar_rect =
RenderRect::new(0., y, size_info.width, size_info.height - y, message.color(), 1.);
- // Push message_bar in the end, so it'll be above all other content
+ // Push message_bar in the end, so it'll be above all other content.
rects.push(message_bar_rect);
- // Draw rectangles
+ // Draw rectangles.
self.renderer.draw_rects(&size_info, rects);
- // Relay messages to the user
+ // Relay messages to the user.
let mut offset = 1;
for message_text in text.iter().rev() {
self.renderer.with_api(&config, &size_info, |mut api| {
@@ -495,11 +520,11 @@ impl Display {
offset += 1;
}
} else {
- // Draw rectangles
+ // Draw rectangles.
self.renderer.draw_rects(&size_info, rects);
}
- // Draw render timer
+ // Draw render timer.
if config.render_timer() {
let timing = format!("{:.3} usec", self.meter.average());
let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
@@ -508,6 +533,11 @@ impl Display {
});
}
+ // Frame event should be requested before swaping buffers, since it requires surface
+ // `commit`, which is done by swap buffers under the hood.
+ #[cfg(not(any(target_os = "macos", windows)))]
+ self.request_frame(&self.window);
+
self.window.swap_buffers();
#[cfg(not(any(target_os = "macos", windows)))]
@@ -522,9 +552,29 @@ impl Display {
}
}
}
+
+ /// Requst a new frame for a window on Wayland.
+ #[inline]
+ #[cfg(not(any(target_os = "macos", windows)))]
+ fn request_frame(&self, window: &Window) {
+ let surface = match window.wayland_surface() {
+ Some(surface) => surface,
+ None => return,
+ };
+
+ let should_draw = self.window.should_draw.clone();
+
+ // Mark that window was drawn.
+ should_draw.store(false, Ordering::Relaxed);
+
+ // Request a new frame.
+ surface.frame().quick_assign(move |_, _, _| {
+ should_draw.store(true, Ordering::Relaxed);
+ });
+ }
}
-/// Calculate padding to spread it evenly around the terminal content
+/// Calculate padding to spread it evenly around the terminal content.
#[inline]
fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 {
padding + ((dimension - 2. * padding) % cell_dimension) / 2.
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 3f2e3f77..94c9a735 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -8,6 +8,8 @@ use std::fs::File;
use std::io::Write;
use std::mem;
use std::path::PathBuf;
+#[cfg(not(any(target_os = "macos", windows)))]
+use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Instant;
@@ -358,6 +360,7 @@ pub struct Processor<N> {
message_buffer: MessageBuffer,
display: Display,
font_size: Size,
+ event_queue: Vec<GlutinEvent<'static, alacritty_terminal::event::Event>>,
}
impl<N: Notify + OnResize> Processor<N> {
@@ -381,41 +384,65 @@ impl<N: Notify + OnResize> Processor<N> {
config,
message_buffer,
display,
+ event_queue: Vec::new(),
}
}
+ /// Return `true` if `event_queue` is empty, `false` otherwise.
+ #[inline]
+ #[cfg(not(any(target_os = "macos", windows)))]
+ fn event_queue_empty(&mut self) -> bool {
+ let wayland_event_queue = match self.display.wayland_event_queue.as_mut() {
+ Some(wayland_event_queue) => wayland_event_queue,
+ // Since frame callbacks do not exist on X11, just check for event queue.
+ None => return self.event_queue.is_empty(),
+ };
+
+ // Check for pending frame callbacks on Wayland.
+ let events_dispatched = wayland_event_queue
+ .dispatch_pending(&mut (), |_, _, _| {})
+ .expect("failed to dispatch event queue");
+
+ self.event_queue.is_empty() && events_dispatched == 0
+ }
+
+ /// Return `true` if `event_queue` is empty, `false` otherwise.
+ #[inline]
+ #[cfg(any(target_os = "macos", windows))]
+ fn event_queue_empty(&mut self) -> bool {
+ self.event_queue.is_empty()
+ }
+
/// Run the event loop.
pub fn run<T>(&mut self, terminal: Arc<FairMutex<Term<T>>>, mut event_loop: EventLoop<Event>)
where
T: EventListener,
{
- let mut event_queue = Vec::new();
-
event_loop.run_return(|event, event_loop, control_flow| {
if self.config.debug.print_events {
info!("glutin event: {:?}", event);
}
- // Ignore all events we do not care about
+ // Ignore all events we do not care about.
if Self::skip_event(&event) {
return;
}
match event {
- // Check for shutdown
+ // Check for shutdown.
GlutinEvent::UserEvent(Event::Exit) => {
*control_flow = ControlFlow::Exit;
return;
},
- // Process events
+ // Process events.
GlutinEvent::RedrawEventsCleared => {
*control_flow = ControlFlow::Wait;
- if event_queue.is_empty() {
+ if self.event_queue_empty() {
return;
}
},
- // Remap DPR change event to remove lifetime
+ // Remap DPR change event to remove lifetime.
GlutinEvent::WindowEvent {
event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size },
..
@@ -423,14 +450,14 @@ impl<N: Notify + OnResize> Processor<N> {
*control_flow = ControlFlow::Poll;
let size = (new_inner_size.width, new_inner_size.height);
let event = GlutinEvent::UserEvent(Event::DPRChanged(scale_factor, size));
- event_queue.push(event);
+ self.event_queue.push(event);
return;
},
// Transmute to extend lifetime, which exists only for `ScaleFactorChanged` event.
// Since we remap that event to remove the lifetime, this is safe.
event => unsafe {
*control_flow = ControlFlow::Poll;
- event_queue.push(mem::transmute(event));
+ self.event_queue.push(mem::transmute(event));
return;
},
}
@@ -457,11 +484,11 @@ impl<N: Notify + OnResize> Processor<N> {
};
let mut processor = input::Processor::new(context, &self.display.highlighted_url);
- for event in event_queue.drain(..) {
+ for event in self.event_queue.drain(..) {
Processor::handle_event(event, &mut processor);
}
- // Process DisplayUpdate events
+ // Process DisplayUpdate events.
if !display_update_pending.is_empty() {
self.display.handle_update(
&mut terminal,
@@ -472,15 +499,25 @@ impl<N: Notify + OnResize> Processor<N> {
);
}
+ #[cfg(not(any(target_os = "macos", windows)))]
+ {
+ // Skip rendering on Wayland until we get frame event from compositor.
+ if event_loop.is_wayland()
+ && !self.display.window.should_draw.load(Ordering::Relaxed)
+ {
+ return;
+ }
+ }
+
if terminal.dirty {
terminal.dirty = false;
- // Request immediate re-draw if visual bell animation is not finished yet
+ // Request immediate re-draw if visual bell animation is not finished yet.
if !terminal.visual_bell.completed() {
- event_queue.push(GlutinEvent::UserEvent(Event::Wakeup));
+ self.event_queue.push(GlutinEvent::UserEvent(Event::Wakeup));
}
- // Redraw screen
+ // Redraw screen.
self.display.draw(
terminal,
&self.message_buffer,
diff --git a/alacritty/src/window.rs b/alacritty/src/window.rs
index 9083fa3e..dfb8517d 100644
--- a/alacritty/src/window.rs
+++ b/alacritty/src/window.rs
@@ -17,6 +17,10 @@ use std::ffi::c_void;
use std::fmt::{self, Display, Formatter};
#[cfg(not(any(target_os = "macos", windows)))]
use std::os::raw::c_ulong;
+#[cfg(not(any(target_os = "macos", windows)))]
+use std::sync::atomic::AtomicBool;
+#[cfg(not(any(target_os = "macos", windows)))]
+use std::sync::Arc;
use glutin::dpi::{PhysicalPosition, PhysicalSize};
use glutin::event_loop::EventLoop;
@@ -51,6 +55,12 @@ use crate::gl;
#[cfg(not(any(target_os = "macos", windows)))]
use crate::wayland_theme::AlacrittyWaylandTheme;
+#[cfg(not(any(target_os = "macos", windows)))]
+use wayland_client::{Attached, EventQueue, Proxy};
+
+#[cfg(not(any(target_os = "macos", windows)))]
+use wayland_client::protocol::wl_surface::WlSurface;
+
// It's required to be in this directory due to the `windows.rc` file
#[cfg(not(any(target_os = "macos", windows)))]
static WINDOW_ICON: &[u8] = include_bytes!("../../extra/windows/alacritty.ico");
@@ -117,6 +127,7 @@ fn create_gl_window(
mut window: WindowBuilder,
event_loop: &EventLoop<Event>,
srgb: bool,
+ vsync: bool,
dimensions: Option<PhysicalSize<u32>>,
) -> Result<WindowedContext<PossiblyCurrent>> {
if let Some(dimensions) = dimensions {
@@ -125,7 +136,7 @@ fn create_gl_window(
let windowed_context = ContextBuilder::new()
.with_srgb(srgb)
- .with_vsync(true)
+ .with_vsync(vsync)
.with_hardware_acceleration(None)
.build_windowed(window, event_loop)?;
@@ -135,10 +146,18 @@ fn create_gl_window(
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.
pub struct Window {
+ /// Flag tracking frame redraw requests from Wayland compositor.
+ #[cfg(not(any(target_os = "macos", windows)))]
+ pub should_draw: Arc<AtomicBool>,
+
+ /// Attached Wayland surface to request new frame events.
+ #[cfg(not(any(target_os = "macos", windows)))]
+ pub wayland_surface: Option<Attached<WlSurface>>,
+
windowed_context: WindowedContext<PossiblyCurrent>,
current_mouse_cursor: CursorIcon,
mouse_visible: bool,
@@ -152,33 +171,58 @@ impl Window {
event_loop: &EventLoop<Event>,
config: &Config,
size: Option<PhysicalSize<u32>>,
+ #[cfg(not(any(target_os = "macos", windows)))] wayland_event_queue: Option<&EventQueue>,
) -> Result<Window> {
let window_builder = Window::get_platform_window(&config.window.title, &config.window);
+
+ // Disable vsync on Wayland.
+ #[cfg(not(any(target_os = "macos", windows)))]
+ let vsync = !event_loop.is_wayland();
+ #[cfg(any(target_os = "macos", windows))]
+ let vsync = true;
+
let windowed_context =
- create_gl_window(window_builder.clone(), &event_loop, false, size)
- .or_else(|_| create_gl_window(window_builder, &event_loop, true, size))?;
+ create_gl_window(window_builder.clone(), &event_loop, false, vsync, size)
+ .or_else(|_| create_gl_window(window_builder, &event_loop, true, vsync, size))?;
- // Text cursor
+ // Text cursor.
let current_mouse_cursor = CursorIcon::Text;
windowed_context.window().set_cursor_icon(current_mouse_cursor);
// 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 _);
- // On X11, embed the window inside another if the parent ID has been set
+ #[cfg(not(any(target_os = "macos", windows)))]
+ let mut wayland_surface = None;
+
#[cfg(not(any(target_os = "macos", windows)))]
{
if event_loop.is_x11() {
+ // On X11, embed the window inside another if the parent ID has been set.
if let Some(parent_window_id) = config.window.embed {
x_embed_window(windowed_context.window(), parent_window_id);
}
} else {
+ // Apply client side decorations theme.
let theme = AlacrittyWaylandTheme::new(&config.colors);
windowed_context.window().set_wayland_theme(theme);
+
+ // Attach surface to Alacritty's internal wayland queue to handle frame callbacks.
+ let surface = windowed_context.window().wayland_surface().unwrap();
+ let proxy: Proxy<WlSurface> = unsafe { Proxy::from_c_ptr(surface as _) };
+ wayland_surface = Some(proxy.attach(wayland_event_queue.as_ref().unwrap().token()));
}
}
- Ok(Self { current_mouse_cursor, mouse_visible: true, windowed_context })
+ Ok(Self {
+ current_mouse_cursor,
+ mouse_visible: true,
+ windowed_context,
+ #[cfg(not(any(target_os = "macos", windows)))]
+ should_draw: Arc::new(AtomicBool::new(true)),
+ #[cfg(not(any(target_os = "macos", windows)))]
+ wayland_surface,
+ })
}
pub fn set_inner_size(&mut self, size: PhysicalSize<u32>) {
@@ -364,6 +408,11 @@ impl Window {
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+ pub fn wayland_surface(&self) -> Option<&Attached<WlSurface>> {
+ self.wayland_surface.as_ref()
+ }
+
+ #[cfg(not(any(target_os = "macos", target_os = "windows")))]
pub fn set_wayland_theme(&mut self, colors: &Colors) {
self.window().set_wayland_theme(AlacrittyWaylandTheme::new(colors));
}