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.
This commit is contained in:
Kirill Chibisov 2020-05-04 02:29:11 +03:00 committed by GitHub
parent a425fe6bfe
commit 04f0bcaf54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 275 additions and 75 deletions

View File

@ -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

62
Cargo.lock generated
View File

@ -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)",
@ -1195,6 +1196,11 @@ dependencies = [
"objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[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"
@ -1532,6 +1538,11 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[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"
@ -1949,6 +1960,21 @@ dependencies = [
"wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[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"
@ -1958,6 +1984,17 @@ dependencies = [
"wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[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"
@ -1979,6 +2016,16 @@ dependencies = [
"xml-rs 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[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"
@ -1988,6 +2035,15 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[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"
@ -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"

View File

@ -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"]}

View File

@ -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.

View File

@ -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,

View File

@ -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);
let windowed_context =
create_gl_window(window_builder.clone(), &event_loop, false, size)
.or_else(|_| create_gl_window(window_builder, &event_loop, true, size))?;
// Text cursor
// 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, vsync, size)
.or_else(|_| create_gl_window(window_builder, &event_loop, true, vsync, size))?;
// 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>) {
@ -363,6 +407,11 @@ impl Window {
self.window().wayland_display()
}
#[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));