Add `cursor.blink_timeout` config option

This option should prevent extensive power usage due to cursor blinking
when there's no user activity being performed.

Fixes #5992.
This commit is contained in:
Kirill Chibisov 2022-07-01 11:40:27 +03:00 committed by GitHub
parent 28e3fc7c64
commit ebc6922eaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 13 deletions

View File

@ -21,12 +21,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Escape sequence to reset underline color (`CSI 59 m`)
- Vi mode keybinding (z) to center view around vi mode cursor
- Accept hexadecimal values starting with `0x` for `--embed`
- Config option `cursor.blink_timeout` to timeout cursor blinking after inactivity
### Changed
- The `--help` output was reworked with a new colorful syntax
- OSC 52 is now disabled on unfocused windows
- `SpawnNewInstance` no longer inherits initial `--command`
- Blinking cursor will timeout after `5` seconds by default
### Fixed

View File

@ -412,6 +412,11 @@
# Cursor blinking interval in milliseconds.
#blink_interval: 750
# Time after which cursor stops blinking, in seconds.
#
# Specifying '0' will disable timeout for blinking.
#blink_timeout: 5
# If this is `true`, the cursor will be rendered as a hollow box when the
# window is not focused.
#unfocused_hollow: true

View File

@ -90,6 +90,7 @@ pub enum EventType {
Scroll(Scroll),
CreateWindow(WindowOptions),
BlinkCursor,
BlinkCursorTimeout,
SearchNext,
}
@ -180,6 +181,7 @@ pub struct ActionContext<'a, N, T> {
pub display: &'a mut Display,
pub message_buffer: &'a mut MessageBuffer,
pub config: &'a mut UiConfig,
pub cursor_blink_timed_out: &'a mut bool,
pub event_loop: &'a EventLoopWindowTarget<Event>,
pub event_proxy: &'a EventLoopProxy<Event>,
pub scheduler: &'a mut Scheduler,
@ -647,14 +649,15 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
fn on_typing_start(&mut self) {
// Disable cursor blinking.
let timer_id = TimerId::new(Topic::BlinkCursor, self.display.window.id());
if let Some(timer) = self.scheduler.unschedule(timer_id) {
let interval =
Duration::from_millis(self.config.terminal_config.cursor.blink_interval());
self.scheduler.schedule(timer.event, interval, true, timer.id);
if self.scheduler.unschedule(timer_id).is_some() {
self.schedule_blinking();
self.display.cursor_hidden = false;
*self.dirty = true;
} else if *self.cursor_blink_timed_out {
self.update_cursor_blinking();
}
*self.dirty = true;
// Hide mouse cursor.
if self.config.mouse.hide_when_typing {
self.display.window.set_mouse_visible(false);
@ -945,18 +948,44 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
blinking &= vi_mode || self.terminal().mode().contains(TermMode::SHOW_CURSOR);
// Update cursor blinking state.
let timer_id = TimerId::new(Topic::BlinkCursor, self.display.window.id());
self.scheduler.unschedule(timer_id);
let window_id = self.display.window.id();
self.scheduler.unschedule(TimerId::new(Topic::BlinkCursor, window_id));
self.scheduler.unschedule(TimerId::new(Topic::BlinkTimeout, window_id));
// Reset blinkinig timeout.
*self.cursor_blink_timed_out = false;
if blinking && self.terminal.is_focused {
let event = Event::new(EventType::BlinkCursor, self.display.window.id());
let interval =
Duration::from_millis(self.config.terminal_config.cursor.blink_interval());
self.scheduler.schedule(event, interval, true, timer_id);
self.schedule_blinking();
self.schedule_blinking_timeout();
} else {
self.display.cursor_hidden = false;
*self.dirty = true;
}
}
fn schedule_blinking(&mut self) {
let window_id = self.display.window.id();
let timer_id = TimerId::new(Topic::BlinkCursor, window_id);
let event = Event::new(EventType::BlinkCursor, window_id);
let blinking_interval =
Duration::from_millis(self.config.terminal_config.cursor.blink_interval());
self.scheduler.schedule(event, blinking_interval, true, timer_id);
}
fn schedule_blinking_timeout(&mut self) {
let blinking_timeout = self.config.terminal_config.cursor.blink_timeout();
if blinking_timeout == 0 {
return;
}
let window_id = self.display.window.id();
let blinking_timeout_interval = Duration::from_secs(blinking_timeout);
let event = Event::new(EventType::BlinkCursorTimeout, window_id);
let timer_id = TimerId::new(Topic::BlinkTimeout, window_id);
self.scheduler.schedule(event, blinking_timeout_interval, false, timer_id);
}
}
#[derive(Debug, Eq, PartialEq)]
@ -1048,6 +1077,14 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
self.ctx.display.cursor_hidden ^= true;
*self.ctx.dirty = true;
},
EventType::BlinkCursorTimeout => {
// Disable blinking after timeout reached.
let timer_id = TimerId::new(Topic::BlinkCursor, self.ctx.display.window.id());
self.ctx.scheduler.unschedule(timer_id);
self.ctx.display.cursor_hidden = false;
*self.ctx.cursor_blink_timed_out = true;
*self.ctx.dirty = true;
},
EventType::Message(message) => {
self.ctx.message_buffer.push(message);
self.ctx.display.pending_update.dirty = true;

View File

@ -27,6 +27,7 @@ pub enum Topic {
SelectionScrolling,
DelayedSearch,
BlinkCursor,
BlinkTimeout,
}
/// Event scheduled to be emitted at a specific time.

View File

@ -43,6 +43,7 @@ pub struct WindowContext {
pub display: Display,
event_queue: Vec<GlutinEvent<'static, Event>>,
terminal: Arc<FairMutex<Term<EventProxy>>>,
cursor_blink_timed_out: bool,
modifiers: ModifiersState,
search_state: SearchState,
received_count: usize,
@ -151,6 +152,7 @@ impl WindowContext {
master_fd,
#[cfg(not(windows))]
shell_pid,
cursor_blink_timed_out: Default::default(),
suppress_chars: Default::default(),
message_buffer: Default::default(),
received_count: Default::default(),
@ -267,6 +269,7 @@ impl WindowContext {
let old_is_searching = self.search_state.history_index.is_some();
let context = ActionContext {
cursor_blink_timed_out: &mut self.cursor_blink_timed_out,
message_buffer: &mut self.message_buffer,
received_count: &mut self.received_count,
suppress_chars: &mut self.suppress_chars,

View File

@ -1,4 +1,4 @@
use std::cmp::max;
use std::cmp;
use std::collections::HashMap;
use std::path::PathBuf;
@ -13,6 +13,7 @@ use crate::ansi::{CursorShape, CursorStyle};
pub use crate::config::scrolling::{Scrolling, MAX_SCROLLBACK_LINES};
pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
const MIN_BLINK_INTERVAL: u64 = 10;
/// Top-level config type.
@ -75,6 +76,7 @@ pub struct Cursor {
thickness: Percentage,
blink_interval: u64,
blink_timeout: u8,
}
impl Default for Cursor {
@ -83,6 +85,7 @@ impl Default for Cursor {
thickness: Percentage(0.15),
unfocused_hollow: true,
blink_interval: 750,
blink_timeout: 5,
style: Default::default(),
vi_mode_style: Default::default(),
}
@ -107,7 +110,18 @@ impl Cursor {
#[inline]
pub fn blink_interval(self) -> u64 {
max(self.blink_interval, MIN_BLINK_INTERVAL)
cmp::max(self.blink_interval, MIN_BLINK_INTERVAL)
}
#[inline]
pub fn blink_timeout(self) -> u64 {
const MILLIS_IN_SECOND: u64 = 1000;
match self.blink_timeout {
0 => 0,
blink_timeout => {
cmp::max(self.blink_interval * 5 / MILLIS_IN_SECOND, blink_timeout as u64)
},
}
}
}