2019-10-05 00:29:26 +00:00
|
|
|
//! The display subsystem including window management, font rasterization, and
|
|
|
|
//! GPU drawing.
|
2020-06-06 18:49:14 +00:00
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
use std::cmp::min;
|
2019-10-05 00:29:26 +00:00
|
|
|
use std::f64;
|
2020-01-03 00:17:22 +00:00
|
|
|
use std::fmt::{self, Formatter};
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
use std::sync::atomic::Ordering;
|
2019-10-05 00:29:26 +00:00
|
|
|
use std::time::Instant;
|
|
|
|
|
|
|
|
use glutin::dpi::{PhysicalPosition, PhysicalSize};
|
2019-11-03 20:59:28 +00:00
|
|
|
use glutin::event::ModifiersState;
|
2019-10-05 00:29:26 +00:00
|
|
|
use glutin::event_loop::EventLoop;
|
2019-12-31 16:53:27 +00:00
|
|
|
#[cfg(not(any(target_os = "macos", windows)))]
|
|
|
|
use glutin::platform::unix::EventLoopWindowTargetExtUnix;
|
2019-11-03 20:59:28 +00:00
|
|
|
use glutin::window::CursorIcon;
|
2019-10-05 00:29:26 +00:00
|
|
|
use log::{debug, info};
|
|
|
|
use parking_lot::MutexGuard;
|
2020-07-09 21:45:22 +00:00
|
|
|
use unicode_width::UnicodeWidthChar;
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
use wayland_client::{Display as WaylandDisplay, EventQueue};
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-07-18 01:27:41 +00:00
|
|
|
use crossfont::{self, Rasterize, Rasterizer};
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
use alacritty_terminal::ansi::NamedColor;
|
2020-07-09 21:45:22 +00:00
|
|
|
use alacritty_terminal::event::{EventListener, OnResize};
|
2021-01-01 05:19:03 +00:00
|
|
|
use alacritty_terminal::grid::Dimensions as _;
|
|
|
|
use alacritty_terminal::index::{Column, Direction, Line, Point};
|
2019-11-03 20:59:28 +00:00
|
|
|
use alacritty_terminal::selection::Selection;
|
2021-01-01 05:19:03 +00:00
|
|
|
use alacritty_terminal::term::{SizeInfo, Term, TermMode, MIN_COLS, MIN_SCREEN_LINES};
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-07-11 17:03:09 +00:00
|
|
|
use crate::config::font::Font;
|
2020-10-07 14:40:50 +00:00
|
|
|
use crate::config::window::Dimensions;
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
use crate::config::window::StartupMode;
|
2019-10-05 00:29:26 +00:00
|
|
|
use crate::config::Config;
|
2021-01-24 21:45:36 +00:00
|
|
|
use crate::display::bell::VisualBell;
|
|
|
|
use crate::display::color::List;
|
|
|
|
use crate::display::content::RenderableContent;
|
|
|
|
use crate::display::cursor::IntoRects;
|
2021-03-01 19:50:39 +00:00
|
|
|
use crate::display::hint::HintState;
|
2021-01-24 21:45:36 +00:00
|
|
|
use crate::display::meter::Meter;
|
|
|
|
use crate::display::window::Window;
|
2020-07-17 01:26:53 +00:00
|
|
|
use crate::event::{Mouse, SearchState};
|
2020-08-13 11:59:35 +00:00
|
|
|
use crate::message_bar::{MessageBuffer, MessageType};
|
2019-11-23 17:08:52 +00:00
|
|
|
use crate::renderer::rects::{RenderLines, RenderRect};
|
|
|
|
use crate::renderer::{self, GlyphCache, QuadRenderer};
|
2019-11-03 20:59:28 +00:00
|
|
|
use crate::url::{Url, Urls};
|
2021-01-24 21:45:36 +00:00
|
|
|
|
|
|
|
pub mod content;
|
|
|
|
pub mod cursor;
|
2021-03-01 19:50:39 +00:00
|
|
|
pub mod hint;
|
2021-01-24 21:45:36 +00:00
|
|
|
pub mod window;
|
|
|
|
|
|
|
|
mod bell;
|
|
|
|
mod color;
|
|
|
|
mod meter;
|
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
|
|
|
mod wayland_theme;
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-07-17 01:26:53 +00:00
|
|
|
const FORWARD_SEARCH_LABEL: &str = "Search: ";
|
|
|
|
const BACKWARD_SEARCH_LABEL: &str = "Backward Search: ";
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
2020-05-03 23:29:11 +00:00
|
|
|
/// Error with window management.
|
2019-10-05 00:29:26 +00:00
|
|
|
Window(window::Error),
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
/// Error dealing with fonts.
|
2020-07-18 01:27:41 +00:00
|
|
|
Font(crossfont::Error),
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
/// Error in renderer.
|
2019-10-05 00:29:26 +00:00
|
|
|
Render(renderer::Error),
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
/// Error during buffer swap.
|
2019-10-05 00:29:26 +00:00
|
|
|
ContextError(glutin::ContextError),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for Error {
|
2020-01-03 00:17:22 +00:00
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
|
|
match self {
|
|
|
|
Error::Window(err) => err.source(),
|
|
|
|
Error::Font(err) => err.source(),
|
|
|
|
Error::Render(err) => err.source(),
|
|
|
|
Error::ContextError(err) => err.source(),
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
2020-01-03 00:17:22 +00:00
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Error::Window(err) => err.fmt(f),
|
|
|
|
Error::Font(err) => err.fmt(f),
|
|
|
|
Error::Render(err) => err.fmt(f),
|
|
|
|
Error::ContextError(err) => err.fmt(f),
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<window::Error> for Error {
|
2020-01-03 00:17:22 +00:00
|
|
|
fn from(val: window::Error) -> Self {
|
2019-10-05 00:29:26 +00:00
|
|
|
Error::Window(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-18 01:27:41 +00:00
|
|
|
impl From<crossfont::Error> for Error {
|
|
|
|
fn from(val: crossfont::Error) -> Self {
|
2019-10-05 00:29:26 +00:00
|
|
|
Error::Font(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<renderer::Error> for Error {
|
2020-01-03 00:17:22 +00:00
|
|
|
fn from(val: renderer::Error) -> Self {
|
2019-10-05 00:29:26 +00:00
|
|
|
Error::Render(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<glutin::ContextError> for Error {
|
2020-01-03 00:17:22 +00:00
|
|
|
fn from(val: glutin::ContextError) -> Self {
|
2019-10-05 00:29:26 +00:00
|
|
|
Error::ContextError(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
#[derive(Default, Clone, Debug, PartialEq)]
|
|
|
|
pub struct DisplayUpdate {
|
|
|
|
pub dirty: bool,
|
|
|
|
|
|
|
|
dimensions: Option<PhysicalSize<u32>>,
|
|
|
|
cursor_dirty: bool,
|
2020-09-27 22:36:08 +00:00
|
|
|
font: Option<Font>,
|
2020-07-09 21:45:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DisplayUpdate {
|
|
|
|
pub fn dimensions(&self) -> Option<PhysicalSize<u32>> {
|
|
|
|
self.dimensions
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn font(&self) -> Option<&Font> {
|
|
|
|
self.font.as_ref()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cursor_dirty(&self) -> bool {
|
|
|
|
self.cursor_dirty
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_dimensions(&mut self, dimensions: PhysicalSize<u32>) {
|
|
|
|
self.dimensions = Some(dimensions);
|
|
|
|
self.dirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_font(&mut self, font: Font) {
|
|
|
|
self.font = Some(font);
|
|
|
|
self.dirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_cursor_dirty(&mut self) {
|
|
|
|
self.cursor_dirty = true;
|
|
|
|
self.dirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
/// The display wraps a window, font rasterizer, and GPU renderer.
|
2019-10-05 00:29:26 +00:00
|
|
|
pub struct Display {
|
|
|
|
pub size_info: SizeInfo,
|
|
|
|
pub window: Window,
|
2019-11-03 20:59:28 +00:00
|
|
|
pub urls: Urls,
|
|
|
|
|
|
|
|
/// Currently highlighted URL.
|
|
|
|
pub highlighted_url: Option<Url>,
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
pub wayland_event_queue: Option<EventQueue>,
|
|
|
|
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(not(any(target_os = "macos", windows)))]
|
|
|
|
pub is_x11: bool,
|
|
|
|
|
2020-11-23 23:11:03 +00:00
|
|
|
/// UI cursor visibility for blinking.
|
|
|
|
pub cursor_hidden: bool,
|
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
pub visual_bell: VisualBell,
|
|
|
|
|
|
|
|
/// Mapped RGB values for each terminal color.
|
|
|
|
pub colors: List,
|
|
|
|
|
2021-03-01 19:50:39 +00:00
|
|
|
/// State of the keyboard hints.
|
|
|
|
pub hint_state: HintState,
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
renderer: QuadRenderer,
|
|
|
|
glyph_cache: GlyphCache,
|
|
|
|
meter: Meter,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display {
|
2020-06-18 01:02:56 +00:00
|
|
|
pub fn new<E>(config: &Config, event_loop: &EventLoop<E>) -> Result<Display, Error> {
|
2021-02-01 21:25:24 +00:00
|
|
|
#[cfg(any(not(feature = "x11"), target_os = "macos", windows))]
|
|
|
|
let is_x11 = false;
|
|
|
|
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
|
|
|
|
let is_x11 = event_loop.is_x11();
|
|
|
|
|
|
|
|
// Guess DPR based on first monitor. On Wayland the initial frame always renders at a DPR
|
|
|
|
// of 1.
|
|
|
|
let estimated_dpr = if cfg!(any(target_os = "macos", windows)) || is_x11 {
|
|
|
|
event_loop.available_monitors().next().map(|m| m.scale_factor()).unwrap_or(1.)
|
|
|
|
} else {
|
|
|
|
1.
|
|
|
|
};
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Guess the target window dimensions.
|
2020-07-11 17:03:09 +00:00
|
|
|
let metrics = GlyphCache::static_metrics(config.ui_config.font.clone(), estimated_dpr)?;
|
2019-10-05 00:29:26 +00:00
|
|
|
let (cell_width, cell_height) = compute_cell_size(config, &metrics);
|
2020-07-11 17:03:09 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
// Guess the target window size if the user has specified the number of lines/columns.
|
|
|
|
let dimensions = config.ui_config.window.dimensions();
|
|
|
|
let estimated_size = dimensions.map(|dimensions| {
|
|
|
|
window_size(config, dimensions, cell_width, cell_height, estimated_dpr)
|
|
|
|
});
|
2019-10-05 00:29:26 +00:00
|
|
|
|
|
|
|
debug!("Estimated DPR: {}", estimated_dpr);
|
2020-09-27 22:36:08 +00:00
|
|
|
debug!("Estimated window size: {:?}", estimated_size);
|
|
|
|
debug!("Estimated cell size: {} x {}", cell_width, cell_height);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
let mut wayland_event_queue = None;
|
|
|
|
|
|
|
|
// Initialize Wayland event queue, to handle Wayland callbacks.
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-07-28 10:00:55 +00:00
|
|
|
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());
|
2020-05-03 23:29:11 +00:00
|
|
|
}
|
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
// Spawn the Alacritty window.
|
2020-05-03 23:29:11 +00:00
|
|
|
let mut window = Window::new(
|
|
|
|
event_loop,
|
|
|
|
&config,
|
2020-09-27 22:36:08 +00:00
|
|
|
estimated_size,
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
wayland_event_queue.as_ref(),
|
|
|
|
)?;
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
info!("Device pixel ratio: {}", window.dpr);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Create renderer.
|
2019-10-05 00:29:26 +00:00
|
|
|
let mut renderer = QuadRenderer::new()?;
|
|
|
|
|
|
|
|
let (glyph_cache, cell_width, cell_height) =
|
2020-09-27 22:36:08 +00:00
|
|
|
Self::new_glyph_cache(window.dpr, &mut renderer, config)?;
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
if let Some(dimensions) = dimensions {
|
|
|
|
if (estimated_dpr - window.dpr).abs() < f64::EPSILON {
|
2019-10-05 00:29:26 +00:00
|
|
|
info!("Estimated DPR correctly, skipping resize");
|
|
|
|
} else {
|
2020-09-27 22:36:08 +00:00
|
|
|
// Resize the window again if the DPR was not estimated correctly.
|
|
|
|
let size = window_size(config, dimensions, cell_width, cell_height, window.dpr);
|
|
|
|
window.set_inner_size(size);
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
let padding = config.ui_config.window.padding(window.dpr);
|
2020-10-21 21:16:12 +00:00
|
|
|
let viewport_size = window.inner_size();
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Create new size with at least one column and row.
|
2020-09-27 22:36:08 +00:00
|
|
|
let size_info = SizeInfo::new(
|
|
|
|
viewport_size.width as f32,
|
|
|
|
viewport_size.height as f32,
|
2019-11-13 23:36:54 +00:00
|
|
|
cell_width,
|
|
|
|
cell_height,
|
2020-09-27 22:36:08 +00:00
|
|
|
padding.0,
|
|
|
|
padding.1,
|
|
|
|
config.ui_config.window.dynamic_padding && dimensions.is_none(),
|
|
|
|
);
|
|
|
|
|
|
|
|
info!("Cell size: {} x {}", cell_width, cell_height);
|
|
|
|
info!("Padding: {} x {}", size_info.padding_x(), size_info.padding_y());
|
2020-10-21 21:16:12 +00:00
|
|
|
info!("Width: {}, Height: {}", size_info.width(), size_info.height());
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Update OpenGL projection.
|
2019-10-05 00:29:26 +00:00
|
|
|
renderer.resize(&size_info);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Clear screen.
|
2021-01-24 21:45:36 +00:00
|
|
|
let background_color = config.ui_config.colors.primary.background;
|
2020-12-28 09:45:39 +00:00
|
|
|
renderer.with_api(&config.ui_config, &size_info, |api| {
|
2019-10-05 00:29:26 +00:00
|
|
|
api.clear(background_color);
|
|
|
|
});
|
|
|
|
|
2020-05-17 21:14:13 +00:00
|
|
|
// Set subpixel anti-aliasing.
|
|
|
|
#[cfg(target_os = "macos")]
|
2020-12-21 02:44:38 +00:00
|
|
|
crossfont::set_font_smoothing(config.ui_config.font.use_thin_strokes);
|
2020-05-17 21:14:13 +00:00
|
|
|
|
2020-12-22 04:25:43 +00:00
|
|
|
// Disable shadows for transparent windows on macOS.
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
window.set_has_shadow(config.ui_config.background_opacity() >= 1.0);
|
|
|
|
|
2020-07-28 10:00:55 +00:00
|
|
|
// 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.
|
2019-12-31 16:53:27 +00:00
|
|
|
#[cfg(not(any(target_os = "macos", windows)))]
|
2020-07-28 10:00:55 +00:00
|
|
|
if is_x11 {
|
|
|
|
window.swap_buffers();
|
2020-12-28 09:45:39 +00:00
|
|
|
renderer.with_api(&config.ui_config, &size_info, |api| {
|
2020-07-28 10:00:55 +00:00
|
|
|
api.finish();
|
|
|
|
});
|
2019-12-31 16:53:27 +00:00
|
|
|
}
|
2019-10-05 00:29:26 +00:00
|
|
|
|
|
|
|
window.set_visible(true);
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Set window position.
|
2019-10-05 00:29:26 +00:00
|
|
|
//
|
2020-05-05 22:50:23 +00:00
|
|
|
// TODO: replace `set_position` with `with_position` once available.
|
2020-05-03 23:29:11 +00:00
|
|
|
// Upstream issue: https://github.com/rust-windowing/winit/issues/806.
|
2020-07-11 17:03:09 +00:00
|
|
|
if let Some(position) = config.ui_config.window.position {
|
2020-01-10 01:51:37 +00:00
|
|
|
window.set_outer_position(PhysicalPosition::from((position.x, position.y)));
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::single_match)]
|
2020-10-07 14:40:50 +00:00
|
|
|
#[cfg(not(windows))]
|
2020-07-11 17:03:09 +00:00
|
|
|
match config.ui_config.window.startup_mode {
|
2019-10-05 00:29:26 +00:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
StartupMode::SimpleFullscreen => window.set_simple_fullscreen(true),
|
2020-10-07 14:40:50 +00:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
StartupMode::Maximized if is_x11 => window.set_maximized(true),
|
2019-10-05 00:29:26 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
2021-03-01 19:50:39 +00:00
|
|
|
let hint_state = HintState::new(config.ui_config.hints.alphabet());
|
|
|
|
|
2020-01-03 00:17:22 +00:00
|
|
|
Ok(Self {
|
2019-11-03 20:59:28 +00:00
|
|
|
window,
|
|
|
|
renderer,
|
|
|
|
glyph_cache,
|
2021-03-01 19:50:39 +00:00
|
|
|
hint_state,
|
2019-11-03 20:59:28 +00:00
|
|
|
meter: Meter::new(),
|
|
|
|
size_info,
|
|
|
|
urls: Urls::new(),
|
|
|
|
highlighted_url: None,
|
2020-05-01 20:57:25 +00:00
|
|
|
#[cfg(not(any(target_os = "macos", windows)))]
|
|
|
|
is_x11,
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
wayland_event_queue,
|
2020-11-23 23:11:03 +00:00
|
|
|
cursor_hidden: false,
|
2021-01-24 21:45:36 +00:00
|
|
|
visual_bell: VisualBell::from(&config.ui_config.bell),
|
|
|
|
colors: List::from(&config.ui_config.colors),
|
2019-11-03 20:59:28 +00:00
|
|
|
})
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn new_glyph_cache(
|
|
|
|
dpr: f64,
|
|
|
|
renderer: &mut QuadRenderer,
|
|
|
|
config: &Config,
|
|
|
|
) -> Result<(GlyphCache, f32, f32), Error> {
|
2020-07-11 17:03:09 +00:00
|
|
|
let font = config.ui_config.font.clone();
|
2020-12-21 02:44:38 +00:00
|
|
|
let rasterizer = Rasterizer::new(dpr as f32, config.ui_config.font.use_thin_strokes)?;
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Initialize glyph cache.
|
2019-10-05 00:29:26 +00:00
|
|
|
let glyph_cache = {
|
|
|
|
info!("Initializing glyph cache...");
|
|
|
|
let init_start = Instant::now();
|
|
|
|
|
|
|
|
let cache =
|
|
|
|
renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?;
|
|
|
|
|
|
|
|
let stop = init_start.elapsed();
|
|
|
|
let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
|
|
|
|
info!("... finished initializing glyph cache in {}s", stop_f);
|
|
|
|
|
|
|
|
cache
|
|
|
|
};
|
|
|
|
|
|
|
|
// Need font metrics to resize the window properly. This suggests to me the
|
|
|
|
// font metrics should be computed before creating the window in the first
|
|
|
|
// place so that a resize is not needed.
|
|
|
|
let (cw, ch) = compute_cell_size(config, &glyph_cache.font_metrics());
|
|
|
|
|
|
|
|
Ok((glyph_cache, cw, ch))
|
|
|
|
}
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
/// Update font size and cell dimensions.
|
2020-09-27 22:36:08 +00:00
|
|
|
///
|
|
|
|
/// This will return a tuple of the cell width and height.
|
|
|
|
fn update_glyph_cache(&mut self, config: &Config, font: &Font) -> (f32, f32) {
|
2019-10-05 00:29:26 +00:00
|
|
|
let cache = &mut self.glyph_cache;
|
2020-09-27 22:36:08 +00:00
|
|
|
let dpr = self.window.dpr;
|
2019-10-05 00:29:26 +00:00
|
|
|
|
|
|
|
self.renderer.with_loader(|mut api| {
|
2020-09-27 22:36:08 +00:00
|
|
|
let _ = cache.update_font_size(font, dpr, &mut api);
|
2019-10-05 00:29:26 +00:00
|
|
|
});
|
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
// Compute new cell sizes.
|
|
|
|
compute_cell_size(config, &self.glyph_cache.font_metrics())
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
2020-04-15 03:50:34 +00:00
|
|
|
/// Clear glyph cache.
|
|
|
|
fn clear_glyph_cache(&mut self) {
|
|
|
|
let cache = &mut self.glyph_cache;
|
|
|
|
self.renderer.with_loader(|mut api| {
|
|
|
|
cache.clear_glyph_cache(&mut api);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Process update events.
|
2019-11-03 19:02:26 +00:00
|
|
|
pub fn handle_update<T>(
|
2019-10-05 00:29:26 +00:00
|
|
|
&mut self,
|
|
|
|
terminal: &mut Term<T>,
|
|
|
|
pty_resize_handle: &mut dyn OnResize,
|
|
|
|
message_buffer: &MessageBuffer,
|
2020-07-09 21:45:22 +00:00
|
|
|
search_active: bool,
|
2019-10-05 00:29:26 +00:00
|
|
|
config: &Config,
|
2019-11-03 19:02:26 +00:00
|
|
|
update_pending: DisplayUpdate,
|
2020-07-09 21:45:22 +00:00
|
|
|
) where
|
|
|
|
T: EventListener,
|
|
|
|
{
|
2020-09-27 22:36:08 +00:00
|
|
|
let (mut cell_width, mut cell_height) =
|
|
|
|
(self.size_info.cell_width(), self.size_info.cell_height());
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Update font size and cell dimensions.
|
2020-07-09 21:45:22 +00:00
|
|
|
if let Some(font) = update_pending.font() {
|
2020-09-27 22:36:08 +00:00
|
|
|
let cell_dimensions = self.update_glyph_cache(config, font);
|
|
|
|
cell_width = cell_dimensions.0;
|
|
|
|
cell_height = cell_dimensions.1;
|
|
|
|
|
|
|
|
info!("Cell size: {} x {}", cell_width, cell_height);
|
2020-07-09 21:45:22 +00:00
|
|
|
} else if update_pending.cursor_dirty() {
|
2020-04-15 03:50:34 +00:00
|
|
|
self.clear_glyph_cache();
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
let (mut width, mut height) = (self.size_info.width(), self.size_info.height());
|
|
|
|
if let Some(dimensions) = update_pending.dimensions() {
|
|
|
|
width = dimensions.width as f32;
|
|
|
|
height = dimensions.height as f32;
|
2019-11-13 23:36:54 +00:00
|
|
|
}
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
let padding = config.ui_config.window.padding(self.window.dpr);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
self.size_info = SizeInfo::new(
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
cell_width,
|
|
|
|
cell_height,
|
|
|
|
padding.0,
|
|
|
|
padding.1,
|
|
|
|
config.ui_config.window.dynamic_padding,
|
|
|
|
);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
// Update number of column/lines in the viewport.
|
|
|
|
let message_bar_lines =
|
|
|
|
message_buffer.message().map(|m| m.text(&self.size_info).len()).unwrap_or(0);
|
|
|
|
let search_lines = if search_active { 1 } else { 0 };
|
|
|
|
self.size_info.reserve_lines(message_bar_lines + search_lines);
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Resize PTY.
|
2020-09-27 22:36:08 +00:00
|
|
|
pty_resize_handle.on_resize(&self.size_info);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Resize terminal.
|
2020-09-27 22:36:08 +00:00
|
|
|
terminal.resize(self.size_info);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Resize renderer.
|
2020-09-27 22:36:08 +00:00
|
|
|
let physical =
|
|
|
|
PhysicalSize::new(self.size_info.width() as u32, self.size_info.height() as u32);
|
2019-10-05 00:29:26 +00:00
|
|
|
self.window.resize(physical);
|
2020-01-04 00:19:06 +00:00
|
|
|
self.renderer.resize(&self.size_info);
|
2020-09-24 00:08:26 +00:00
|
|
|
|
2020-09-27 22:36:08 +00:00
|
|
|
info!("Padding: {} x {}", self.size_info.padding_x(), self.size_info.padding_y());
|
|
|
|
info!("Width: {}, Height: {}", self.size_info.width(), self.size_info.height());
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
/// Draw the screen.
|
2019-10-05 00:29:26 +00:00
|
|
|
///
|
|
|
|
/// A reference to Term whose state is being drawn must be provided.
|
|
|
|
///
|
2020-05-03 23:29:11 +00:00
|
|
|
/// This call may block if vsync is enabled.
|
2021-01-24 21:45:36 +00:00
|
|
|
pub fn draw<T: EventListener>(
|
2019-10-05 00:29:26 +00:00
|
|
|
&mut self,
|
|
|
|
terminal: MutexGuard<'_, Term<T>>,
|
|
|
|
message_buffer: &MessageBuffer,
|
|
|
|
config: &Config,
|
2019-11-03 20:59:28 +00:00
|
|
|
mouse: &Mouse,
|
|
|
|
mods: ModifiersState,
|
2020-07-17 01:26:53 +00:00
|
|
|
search_state: &SearchState,
|
2019-10-05 00:29:26 +00:00
|
|
|
) {
|
2020-11-13 05:40:09 +00:00
|
|
|
// Convert search match from viewport to absolute indexing.
|
|
|
|
let search_active = search_state.regex().is_some();
|
|
|
|
let viewport_match = search_state
|
|
|
|
.focused_match()
|
|
|
|
.and_then(|focused_match| terminal.grid().clamp_buffer_range_to_visible(focused_match));
|
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
// Collect renderable content before the terminal is dropped.
|
2021-03-06 03:18:48 +00:00
|
|
|
let mut content = RenderableContent::new(config, self, &terminal, search_state);
|
2020-12-28 09:45:39 +00:00
|
|
|
let mut grid_cells = Vec::new();
|
|
|
|
while let Some(cell) = content.next() {
|
|
|
|
grid_cells.push(cell);
|
|
|
|
}
|
2021-01-24 21:45:36 +00:00
|
|
|
let background_color = content.color(NamedColor::Background as usize);
|
|
|
|
let display_offset = content.display_offset();
|
2020-12-28 09:45:39 +00:00
|
|
|
let cursor = content.cursor();
|
|
|
|
|
2020-07-17 01:26:53 +00:00
|
|
|
let cursor_point = terminal.grid().cursor.point;
|
2021-01-01 05:19:03 +00:00
|
|
|
let total_lines = terminal.grid().total_lines();
|
2019-10-05 00:29:26 +00:00
|
|
|
let metrics = self.glyph_cache.font_metrics();
|
|
|
|
let size_info = self.size_info;
|
|
|
|
|
2020-05-30 20:45:44 +00:00
|
|
|
let selection = !terminal.selection.as_ref().map(Selection::is_empty).unwrap_or(true);
|
2020-04-10 18:23:50 +00:00
|
|
|
let mouse_mode = terminal.mode().intersects(TermMode::MOUSE_MODE)
|
|
|
|
&& !terminal.mode().contains(TermMode::VI);
|
2019-11-03 20:59:28 +00:00
|
|
|
|
2021-01-01 05:19:03 +00:00
|
|
|
let vi_mode = terminal.mode().contains(TermMode::VI);
|
|
|
|
let vi_mode_cursor = if vi_mode { Some(terminal.vi_mode_cursor) } else { None };
|
2020-03-18 02:35:08 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Drop terminal as early as possible to free lock.
|
2019-10-05 00:29:26 +00:00
|
|
|
drop(terminal);
|
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
self.renderer.with_api(&config.ui_config, &size_info, |api| {
|
2019-10-05 00:29:26 +00:00
|
|
|
api.clear(background_color);
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut lines = RenderLines::new();
|
2019-11-03 20:59:28 +00:00
|
|
|
let mut urls = Urls::new();
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Draw grid.
|
2019-10-05 00:29:26 +00:00
|
|
|
{
|
|
|
|
let _sampler = self.meter.sampler();
|
|
|
|
|
2021-01-01 05:19:03 +00:00
|
|
|
let glyph_cache = &mut self.glyph_cache;
|
2020-12-28 09:45:39 +00:00
|
|
|
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
2020-05-03 23:29:11 +00:00
|
|
|
// Iterate over all non-empty cells in the grid.
|
2020-11-13 05:40:09 +00:00
|
|
|
for mut cell in grid_cells {
|
2021-01-07 05:19:37 +00:00
|
|
|
// Invert the active match during search.
|
2020-11-13 05:40:09 +00:00
|
|
|
if cell.is_match
|
|
|
|
&& viewport_match
|
|
|
|
.as_ref()
|
2021-01-24 21:45:36 +00:00
|
|
|
.map_or(false, |viewport_match| viewport_match.contains(&cell.point))
|
2020-11-13 05:40:09 +00:00
|
|
|
{
|
2021-01-24 21:45:36 +00:00
|
|
|
let colors = config.ui_config.colors.search.focused_match;
|
2020-12-21 02:44:38 +00:00
|
|
|
let match_fg = colors.foreground.color(cell.fg, cell.bg);
|
|
|
|
cell.bg = colors.background.color(cell.fg, cell.bg);
|
2020-11-13 05:40:09 +00:00
|
|
|
cell.fg = match_fg;
|
|
|
|
cell.bg_alpha = 1.0;
|
|
|
|
}
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Update URL underlines.
|
2020-11-05 04:45:14 +00:00
|
|
|
urls.update(size_info.cols(), &cell);
|
2019-11-03 20:59:28 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Update underline/strikeout.
|
2020-11-05 04:45:14 +00:00
|
|
|
lines.update(&cell);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Draw the cell.
|
2019-10-05 00:29:26 +00:00
|
|
|
api.render_cell(cell, glyph_cache);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-11-03 20:59:28 +00:00
|
|
|
let mut rects = lines.rects(&metrics, &size_info);
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Update visible URLs.
|
2019-11-03 20:59:28 +00:00
|
|
|
self.urls = urls;
|
|
|
|
if let Some(url) = self.urls.highlighted(config, mouse, mods, mouse_mode, selection) {
|
|
|
|
rects.append(&mut url.rects(&metrics, &size_info));
|
|
|
|
|
|
|
|
self.window.set_mouse_cursor(CursorIcon::Hand);
|
|
|
|
|
|
|
|
self.highlighted_url = Some(url);
|
|
|
|
} else if self.highlighted_url.is_some() {
|
|
|
|
self.highlighted_url = None;
|
|
|
|
|
|
|
|
if mouse_mode {
|
|
|
|
self.window.set_mouse_cursor(CursorIcon::Default);
|
|
|
|
} else {
|
|
|
|
self.window.set_mouse_cursor(CursorIcon::Text);
|
|
|
|
}
|
|
|
|
}
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-03-18 02:35:08 +00:00
|
|
|
if let Some(vi_mode_cursor) = vi_mode_cursor {
|
2021-01-01 05:19:03 +00:00
|
|
|
// Highlight URLs at the vi mode cursor position.
|
|
|
|
let vi_mode_point = vi_mode_cursor.point;
|
|
|
|
if let Some(url) = self.urls.find_at(vi_mode_point) {
|
2020-03-18 02:35:08 +00:00
|
|
|
rects.append(&mut url.rects(&metrics, &size_info));
|
|
|
|
}
|
2021-01-01 05:19:03 +00:00
|
|
|
|
|
|
|
// Indicate vi mode by showing the cursor's position in the top right corner.
|
|
|
|
let line = size_info.screen_lines() + display_offset - vi_mode_point.line - 1;
|
|
|
|
self.draw_line_indicator(config, &size_info, total_lines, Some(vi_mode_point), line.0);
|
|
|
|
} else if search_active {
|
|
|
|
// Show current display offset in vi-less search to indicate match position.
|
|
|
|
self.draw_line_indicator(config, &size_info, total_lines, None, display_offset);
|
2020-03-18 02:35:08 +00:00
|
|
|
}
|
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
// Push the cursor rects for rendering.
|
|
|
|
if let Some(cursor) = cursor {
|
|
|
|
for rect in cursor.rects(&size_info, config.cursor.thickness()) {
|
|
|
|
rects.push(rect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Push visual bell after url/underline/strikeout rects.
|
2021-01-24 21:45:36 +00:00
|
|
|
let visual_bell_intensity = self.visual_bell.intensity();
|
2019-10-26 19:45:47 +00:00
|
|
|
if visual_bell_intensity != 0. {
|
|
|
|
let visual_bell_rect = RenderRect::new(
|
|
|
|
0.,
|
|
|
|
0.,
|
2020-09-27 22:36:08 +00:00
|
|
|
size_info.width(),
|
|
|
|
size_info.height(),
|
2021-01-24 21:45:36 +00:00
|
|
|
config.ui_config.bell.color,
|
2019-10-26 19:45:47 +00:00
|
|
|
visual_bell_intensity as f32,
|
|
|
|
);
|
|
|
|
rects.push(visual_bell_rect);
|
|
|
|
}
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
if let Some(message) = message_buffer.message() {
|
2020-11-13 05:40:09 +00:00
|
|
|
let search_offset = if search_active { 1 } else { 0 };
|
2019-10-05 00:29:26 +00:00
|
|
|
let text = message.text(&size_info);
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Create a new rectangle for the background.
|
2020-09-27 22:36:08 +00:00
|
|
|
let start_line = size_info.screen_lines() + search_offset;
|
|
|
|
let y = size_info.cell_height().mul_add(start_line.0 as f32, size_info.padding_y());
|
2020-08-13 11:59:35 +00:00
|
|
|
|
2021-01-01 05:19:03 +00:00
|
|
|
let bg = match message.ty() {
|
2021-01-24 21:45:36 +00:00
|
|
|
MessageType::Error => config.ui_config.colors.normal.red,
|
|
|
|
MessageType::Warning => config.ui_config.colors.normal.yellow,
|
2020-08-13 11:59:35 +00:00
|
|
|
};
|
|
|
|
|
2019-10-26 19:45:47 +00:00
|
|
|
let message_bar_rect =
|
2021-01-01 05:19:03 +00:00
|
|
|
RenderRect::new(0., y, size_info.width(), size_info.height() - y, bg, 1.);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Push message_bar in the end, so it'll be above all other content.
|
2019-10-26 19:45:47 +00:00
|
|
|
rects.push(message_bar_rect);
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Draw rectangles.
|
2019-10-26 19:45:47 +00:00
|
|
|
self.renderer.draw_rects(&size_info, rects);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Relay messages to the user.
|
2021-01-01 05:19:03 +00:00
|
|
|
let glyph_cache = &mut self.glyph_cache;
|
2021-01-24 21:45:36 +00:00
|
|
|
let fg = config.ui_config.colors.primary.background;
|
2020-09-27 22:36:08 +00:00
|
|
|
for (i, message_text) in text.iter().enumerate() {
|
2021-01-01 05:19:03 +00:00
|
|
|
let point = Point::new(start_line + i, Column(0));
|
2020-12-28 09:45:39 +00:00
|
|
|
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
2021-01-01 05:19:03 +00:00
|
|
|
api.render_string(glyph_cache, point, fg, bg, &message_text);
|
2019-10-05 00:29:26 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
2020-05-03 23:29:11 +00:00
|
|
|
// Draw rectangles.
|
2019-10-26 19:45:47 +00:00
|
|
|
self.renderer.draw_rects(&size_info, rects);
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
self.draw_render_timer(config, &size_info);
|
2019-10-05 00:29:26 +00:00
|
|
|
|
2020-07-17 01:26:53 +00:00
|
|
|
// Handle search and IME positioning.
|
|
|
|
let ime_position = match search_state.regex() {
|
|
|
|
Some(regex) => {
|
|
|
|
let search_label = match search_state.direction() {
|
|
|
|
Direction::Right => FORWARD_SEARCH_LABEL,
|
|
|
|
Direction::Left => BACKWARD_SEARCH_LABEL,
|
|
|
|
};
|
|
|
|
|
|
|
|
let search_text = Self::format_search(&size_info, regex, search_label);
|
|
|
|
|
|
|
|
// Render the search bar.
|
2020-09-27 22:36:08 +00:00
|
|
|
self.draw_search(config, &size_info, &search_text);
|
2020-07-17 01:26:53 +00:00
|
|
|
|
|
|
|
// Compute IME position.
|
2020-09-27 22:36:08 +00:00
|
|
|
Point::new(size_info.screen_lines() + 1, Column(search_text.chars().count() - 1))
|
2020-07-17 01:26:53 +00:00
|
|
|
},
|
|
|
|
None => cursor_point,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Update IME position.
|
|
|
|
self.window.update_ime_position(ime_position, &self.size_info);
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
// Frame event should be requested before swaping buffers, since it requires surface
|
|
|
|
// `commit`, which is done by swap buffers under the hood.
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
self.request_frame(&self.window);
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
self.window.swap_buffers();
|
2020-05-01 20:57:25 +00:00
|
|
|
|
2020-10-12 22:13:38 +00:00
|
|
|
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
|
2020-07-28 10:00:55 +00:00
|
|
|
if self.is_x11 {
|
|
|
|
// 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
|
|
|
|
// permanent one frame delay.
|
2020-12-28 09:45:39 +00:00
|
|
|
self.renderer.with_api(&config.ui_config, &size_info, |api| {
|
2020-07-28 10:00:55 +00:00
|
|
|
api.finish();
|
|
|
|
});
|
2020-05-01 20:57:25 +00:00
|
|
|
}
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
2020-05-03 23:29:11 +00:00
|
|
|
|
2021-01-24 21:45:36 +00:00
|
|
|
/// Update to a new configuration.
|
|
|
|
pub fn update_config(&mut self, config: &Config) {
|
|
|
|
self.visual_bell.update_config(&config.ui_config.bell);
|
|
|
|
self.colors = List::from(&config.ui_config.colors);
|
|
|
|
}
|
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
/// Format search regex to account for the cursor and fullwidth characters.
|
2020-07-18 01:27:41 +00:00
|
|
|
fn format_search(size_info: &SizeInfo, search_regex: &str, search_label: &str) -> String {
|
2020-07-09 21:45:22 +00:00
|
|
|
// Add spacers for wide chars.
|
2020-07-17 21:12:11 +00:00
|
|
|
let mut formatted_regex = String::with_capacity(search_regex.len());
|
2020-07-09 21:45:22 +00:00
|
|
|
for c in search_regex.chars() {
|
2020-07-17 21:12:11 +00:00
|
|
|
formatted_regex.push(c);
|
2020-07-09 21:45:22 +00:00
|
|
|
if c.width() == Some(2) {
|
2020-07-17 21:12:11 +00:00
|
|
|
formatted_regex.push(' ');
|
2020-07-09 21:45:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add cursor to show whitespace.
|
2020-07-17 21:12:11 +00:00
|
|
|
formatted_regex.push('_');
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2020-07-17 21:12:11 +00:00
|
|
|
// Truncate beginning of the search regex if it exceeds the viewport width.
|
2020-07-17 01:26:53 +00:00
|
|
|
let num_cols = size_info.cols().0;
|
2020-09-25 00:21:21 +00:00
|
|
|
let label_len = search_label.chars().count();
|
|
|
|
let regex_len = formatted_regex.chars().count();
|
2020-07-17 21:12:11 +00:00
|
|
|
let truncate_len = min((regex_len + label_len).saturating_sub(num_cols), regex_len);
|
2020-09-25 00:21:21 +00:00
|
|
|
let index = formatted_regex.char_indices().nth(truncate_len).map(|(i, _c)| i).unwrap_or(0);
|
|
|
|
let truncated_regex = &formatted_regex[index..];
|
2020-07-17 01:26:53 +00:00
|
|
|
|
2020-07-17 21:12:11 +00:00
|
|
|
// Add search label to the beginning of the search regex.
|
|
|
|
let mut bar_text = format!("{}{}", search_label, truncated_regex);
|
|
|
|
|
|
|
|
// Make sure the label alone doesn't exceed the viewport width.
|
|
|
|
bar_text.truncate(num_cols);
|
|
|
|
|
|
|
|
bar_text
|
2020-07-09 21:45:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Draw current search regex.
|
2020-09-27 22:36:08 +00:00
|
|
|
fn draw_search(&mut self, config: &Config, size_info: &SizeInfo, text: &str) {
|
2020-07-09 21:45:22 +00:00
|
|
|
let glyph_cache = &mut self.glyph_cache;
|
|
|
|
let num_cols = size_info.cols().0;
|
|
|
|
|
|
|
|
// Assure text length is at least num_cols.
|
2020-07-17 01:26:53 +00:00
|
|
|
let text = format!("{:<1$}", text, num_cols);
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2021-01-01 05:19:03 +00:00
|
|
|
let point = Point::new(size_info.screen_lines(), Column(0));
|
2021-01-24 21:45:36 +00:00
|
|
|
let fg = config.ui_config.colors.search_bar_foreground();
|
|
|
|
let bg = config.ui_config.colors.search_bar_background();
|
2021-01-01 05:19:03 +00:00
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
2021-01-01 05:19:03 +00:00
|
|
|
api.render_string(glyph_cache, point, fg, bg, &text);
|
2020-07-09 21:45:22 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Draw render timer.
|
|
|
|
fn draw_render_timer(&mut self, config: &Config, size_info: &SizeInfo) {
|
2020-07-11 17:03:09 +00:00
|
|
|
if !config.ui_config.debug.render_timer {
|
2020-07-09 21:45:22 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-01-01 05:19:03 +00:00
|
|
|
|
2020-07-09 21:45:22 +00:00
|
|
|
let glyph_cache = &mut self.glyph_cache;
|
|
|
|
|
|
|
|
let timing = format!("{:.3} usec", self.meter.average());
|
2021-01-01 05:19:03 +00:00
|
|
|
let point = Point::new(size_info.screen_lines() - 2, Column(0));
|
2021-01-24 21:45:36 +00:00
|
|
|
let fg = config.ui_config.colors.primary.background;
|
|
|
|
let bg = config.ui_config.colors.normal.red;
|
2020-07-09 21:45:22 +00:00
|
|
|
|
2020-12-28 09:45:39 +00:00
|
|
|
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
2021-01-01 05:19:03 +00:00
|
|
|
api.render_string(glyph_cache, point, fg, bg, &timing);
|
2020-07-09 21:45:22 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-01-01 05:19:03 +00:00
|
|
|
/// Draw an indicator for the position of a line in history.
|
|
|
|
fn draw_line_indicator(
|
|
|
|
&mut self,
|
|
|
|
config: &Config,
|
|
|
|
size_info: &SizeInfo,
|
|
|
|
total_lines: usize,
|
|
|
|
vi_mode_point: Option<Point>,
|
|
|
|
line: usize,
|
|
|
|
) {
|
|
|
|
let text = format!("[{}/{}]", line, total_lines - 1);
|
|
|
|
let column = Column(size_info.cols().0.saturating_sub(text.len()));
|
2021-01-24 21:45:36 +00:00
|
|
|
let colors = &config.ui_config.colors;
|
2021-01-01 05:19:03 +00:00
|
|
|
let fg = colors.line_indicator.foreground.unwrap_or(colors.primary.background);
|
|
|
|
let bg = colors.line_indicator.background.unwrap_or(colors.primary.foreground);
|
|
|
|
|
|
|
|
// Do not render anything if it would obscure the vi mode cursor.
|
2021-01-24 21:45:36 +00:00
|
|
|
if vi_mode_point.map_or(true, |point| point.line.0 != 0 || point.column < column) {
|
2021-01-01 05:19:03 +00:00
|
|
|
let glyph_cache = &mut self.glyph_cache;
|
|
|
|
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
|
|
|
|
api.render_string(glyph_cache, Point::new(Line(0), column), fg, bg, &text);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-03 23:29:11 +00:00
|
|
|
/// Requst a new frame for a window on Wayland.
|
|
|
|
#[inline]
|
2020-10-12 09:22:36 +00:00
|
|
|
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
2020-05-03 23:29:11 +00:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
}
|
2019-10-05 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Calculate the cell dimensions based on font metrics.
|
2020-09-27 22:36:08 +00:00
|
|
|
///
|
|
|
|
/// This will return a tuple of the cell width and height.
|
2019-10-05 00:29:26 +00:00
|
|
|
#[inline]
|
2020-07-18 01:27:41 +00:00
|
|
|
fn compute_cell_size(config: &Config, metrics: &crossfont::Metrics) -> (f32, f32) {
|
2020-07-11 17:03:09 +00:00
|
|
|
let offset_x = f64::from(config.ui_config.font.offset.x);
|
|
|
|
let offset_y = f64::from(config.ui_config.font.offset.y);
|
2019-10-05 00:29:26 +00:00
|
|
|
(
|
2020-10-29 22:14:43 +00:00
|
|
|
(metrics.average_advance + offset_x).floor().max(1.) as f32,
|
|
|
|
(metrics.line_height + offset_y).floor().max(1.) as f32,
|
2019-10-05 00:29:26 +00:00
|
|
|
)
|
|
|
|
}
|
2020-09-27 22:36:08 +00:00
|
|
|
|
|
|
|
/// Calculate the size of the window given padding, terminal dimensions and cell size.
|
|
|
|
fn window_size(
|
|
|
|
config: &Config,
|
|
|
|
dimensions: Dimensions,
|
|
|
|
cell_width: f32,
|
|
|
|
cell_height: f32,
|
|
|
|
dpr: f64,
|
|
|
|
) -> PhysicalSize<u32> {
|
|
|
|
let padding = config.ui_config.window.padding(dpr);
|
|
|
|
|
|
|
|
let grid_width = cell_width * dimensions.columns.0.max(MIN_COLS) as f32;
|
|
|
|
let grid_height = cell_height * dimensions.lines.0.max(MIN_SCREEN_LINES) as f32;
|
|
|
|
|
|
|
|
let width = (padding.0).mul_add(2., grid_width).floor();
|
|
|
|
let height = (padding.1).mul_add(2., grid_height).floor();
|
|
|
|
|
|
|
|
PhysicalSize::new(width as u32, height as u32)
|
|
|
|
}
|