forked from NotAShelf/beer
config: default cursor style/blink and visual bell
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ibd512084374fe4723ee267a916187af56a6a6964
This commit is contained in:
parent
2d319b7e73
commit
0738ce3b6f
4 changed files with 90 additions and 1 deletions
|
|
@ -13,7 +13,27 @@ use serde::Deserialize;
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub main: Main,
|
pub main: Main,
|
||||||
pub colors: Colors,
|
pub colors: Colors,
|
||||||
|
pub cursor: Cursor,
|
||||||
pub scrollback: Scrollback,
|
pub scrollback: Scrollback,
|
||||||
|
pub bell: Bell,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `[cursor]`: the default cursor presentation (DECSCUSR may override at runtime).
|
||||||
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
|
#[serde(default, rename_all = "kebab-case")]
|
||||||
|
pub struct Cursor {
|
||||||
|
/// `block`, `beam`/`bar`, or `underline`.
|
||||||
|
pub style: Option<String>,
|
||||||
|
/// Whether the cursor blinks by default.
|
||||||
|
pub blink: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `[bell]`: what happens on `BEL` (0x07).
|
||||||
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
|
#[serde(default, rename_all = "kebab-case")]
|
||||||
|
pub struct Bell {
|
||||||
|
/// Briefly flash the screen.
|
||||||
|
pub visual: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `[colors]`: foreground/background, the 16 base palette entries, and accents.
|
/// `[colors]`: foreground/background, the 16 base palette entries, and accents.
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,13 @@ impl Theme {
|
||||||
pub fn reset_bg(&mut self) {
|
pub fn reset_bg(&mut self) {
|
||||||
self.bg = self.default_bg;
|
self.bg = self.default_bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A copy with foreground and background swapped, for the visual bell flash.
|
||||||
|
pub fn inverted(&self) -> Self {
|
||||||
|
let mut t = self.clone();
|
||||||
|
std::mem::swap(&mut t.fg, &mut t.bg);
|
||||||
|
t
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Foreground/background used for `Color::Default`.
|
/// Foreground/background used for `Color::Default`.
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,8 @@ pub struct Term {
|
||||||
clipboard_ops: Vec<ClipboardOp>,
|
clipboard_ops: Vec<ClipboardOp>,
|
||||||
/// The active colour scheme (seeded from config, mutated by OSC escapes).
|
/// The active colour scheme (seeded from config, mutated by OSC escapes).
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
|
/// Set when the child rings the bell (`BEL`); cleared by the front-end.
|
||||||
|
bell: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Term {
|
impl Term {
|
||||||
|
|
@ -115,9 +117,15 @@ impl Term {
|
||||||
xtgettcap: None,
|
xtgettcap: None,
|
||||||
clipboard_ops: Vec::new(),
|
clipboard_ops: Vec::new(),
|
||||||
theme: Theme::default(),
|
theme: Theme::default(),
|
||||||
|
bell: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take and clear the pending bell flag.
|
||||||
|
pub fn take_bell(&mut self) -> bool {
|
||||||
|
std::mem::take(&mut self.bell)
|
||||||
|
}
|
||||||
|
|
||||||
/// Drain the OSC 52 clipboard requests accumulated since the last call.
|
/// Drain the OSC 52 clipboard requests accumulated since the last call.
|
||||||
pub fn take_clipboard_ops(&mut self) -> Vec<ClipboardOp> {
|
pub fn take_clipboard_ops(&mut self) -> Vec<ClipboardOp> {
|
||||||
std::mem::take(&mut self.clipboard_ops)
|
std::mem::take(&mut self.clipboard_ops)
|
||||||
|
|
@ -535,6 +543,7 @@ impl Perform for Term {
|
||||||
|
|
||||||
fn execute(&mut self, byte: u8) {
|
fn execute(&mut self, byte: u8) {
|
||||||
match byte {
|
match byte {
|
||||||
|
0x07 => self.bell = true,
|
||||||
0x08 => self.grid.backspace(),
|
0x08 => self.grid.backspace(),
|
||||||
0x09 => self.grid.tab(),
|
0x09 => self.grid.tab(),
|
||||||
0x0A..=0x0C => self.grid.line_feed(),
|
0x0A..=0x0C => self.grid.line_feed(),
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,9 @@ const SYNC_TIMEOUT_MS: u64 = 150;
|
||||||
/// Interval between autoscroll steps while a drag selection runs off an edge.
|
/// Interval between autoscroll steps while a drag selection runs off an edge.
|
||||||
const AUTOSCROLL_MS: u64 = 40;
|
const AUTOSCROLL_MS: u64 = 40;
|
||||||
|
|
||||||
|
/// How long the visual bell inverts the screen.
|
||||||
|
const FLASH_MS: u64 = 80;
|
||||||
|
|
||||||
/// What determines one rendered row's pixels: its cells, the cursor on it, the
|
/// What determines one rendered row's pixels: its cells, the cursor on it, the
|
||||||
/// selection span over it, and the blink phase. Two equal `RowSnap`s render
|
/// selection span over it, and the blink phase. Two equal `RowSnap`s render
|
||||||
/// identically, so a buffer holding an equal snapshot needs no repaint.
|
/// identically, so a buffer holding an equal snapshot needs no repaint.
|
||||||
|
|
@ -255,6 +258,8 @@ pub fn run(config: Config) -> anyhow::Result<ExitCode> {
|
||||||
buf_dims: (0, 0),
|
buf_dims: (0, 0),
|
||||||
blink_on: true,
|
blink_on: true,
|
||||||
sync_timeout: None,
|
sync_timeout: None,
|
||||||
|
flashing: false,
|
||||||
|
flash_timer: None,
|
||||||
searching: false,
|
searching: false,
|
||||||
focused: true,
|
focused: true,
|
||||||
exit: false,
|
exit: false,
|
||||||
|
|
@ -382,6 +387,10 @@ struct App {
|
||||||
blink_on: bool,
|
blink_on: bool,
|
||||||
/// Armed while synchronized output holds the screen, to force it open.
|
/// Armed while synchronized output holds the screen, to force it open.
|
||||||
sync_timeout: Option<RegistrationToken>,
|
sync_timeout: Option<RegistrationToken>,
|
||||||
|
/// The visual bell is inverting the screen.
|
||||||
|
flashing: bool,
|
||||||
|
/// Timer that ends the visual-bell flash.
|
||||||
|
flash_timer: Option<RegistrationToken>,
|
||||||
/// Whether incremental search mode is active (the query lives in the grid).
|
/// Whether incremental search mode is active (the query lives in the grid).
|
||||||
searching: bool,
|
searching: bool,
|
||||||
/// Whether the toplevel currently has keyboard focus (drives the cursor).
|
/// Whether the toplevel currently has keyboard focus (drives the cursor).
|
||||||
|
|
@ -454,6 +463,10 @@ impl App {
|
||||||
let grid = term.grid_mut();
|
let grid = term.grid_mut();
|
||||||
grid.set_word_delimiters(self.config.main.word_delimiters.clone());
|
grid.set_word_delimiters(self.config.main.word_delimiters.clone());
|
||||||
grid.set_scrollback_cap(self.config.scrollback.lines);
|
grid.set_scrollback_cap(self.config.scrollback.lines);
|
||||||
|
if let Some(shape) = cursor_shape_from(self.config.cursor.style.as_deref()) {
|
||||||
|
grid.set_cursor_shape(shape);
|
||||||
|
}
|
||||||
|
grid.set_cursor_blink(self.config.cursor.blink);
|
||||||
self.session = Some(Session { pty, term });
|
self.session = Some(Session { pty, term });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -982,13 +995,38 @@ impl App {
|
||||||
self.window
|
self.window
|
||||||
.set_title(self.title.clone().unwrap_or_default());
|
.set_title(self.title.clone().unwrap_or_default());
|
||||||
}
|
}
|
||||||
|
let rang = session.term.take_bell();
|
||||||
let ops = session.term.take_clipboard_ops();
|
let ops = session.term.take_clipboard_ops();
|
||||||
if !ops.is_empty() {
|
if !ops.is_empty() {
|
||||||
self.handle_clipboard_ops(ops);
|
self.handle_clipboard_ops(ops);
|
||||||
}
|
}
|
||||||
|
if rang && self.config.bell.visual {
|
||||||
|
self.start_flash();
|
||||||
|
}
|
||||||
self.needs_draw = true;
|
self.needs_draw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Begin a visual-bell flash: invert the screen for a moment. Clearing the
|
||||||
|
/// buffer ring forces a full repaint with the inverted theme.
|
||||||
|
fn start_flash(&mut self) {
|
||||||
|
self.flashing = true;
|
||||||
|
self.frames.clear();
|
||||||
|
self.needs_draw = true;
|
||||||
|
if self.flash_timer.is_none() {
|
||||||
|
let timer = Timer::from_duration(Duration::from_millis(FLASH_MS));
|
||||||
|
self.flash_timer = self
|
||||||
|
.loop_handle
|
||||||
|
.insert_source(timer, |_, _, app: &mut App| {
|
||||||
|
app.flashing = false;
|
||||||
|
app.frames.clear();
|
||||||
|
app.needs_draw = true;
|
||||||
|
app.flash_timer = None;
|
||||||
|
TimeoutAction::Drop
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Recompute the grid size for the current window and tell the grid and the
|
/// Recompute the grid size for the current window and tell the grid and the
|
||||||
/// PTY about it if it changed.
|
/// PTY about it if it changed.
|
||||||
fn resize_grid(&mut self) {
|
fn resize_grid(&mut self) {
|
||||||
|
|
@ -1084,7 +1122,9 @@ impl App {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let grid = session.term.grid();
|
let grid = session.term.grid();
|
||||||
let theme = session.term.theme();
|
// The visual bell inverts fg/bg for the duration of the flash.
|
||||||
|
let flashed = self.flashing.then(|| session.term.theme().inverted());
|
||||||
|
let theme = flashed.as_ref().unwrap_or(session.term.theme());
|
||||||
let rows = grid.rows();
|
let rows = grid.rows();
|
||||||
let mut cur: Vec<RowSnap> = (0..rows)
|
let mut cur: Vec<RowSnap> = (0..rows)
|
||||||
.map(|y| row_snap(grid, y, focused, blink_on))
|
.map(|y| row_snap(grid, y, focused, blink_on))
|
||||||
|
|
@ -1440,6 +1480,19 @@ impl KeyboardHandler for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a configured cursor-style name into a [`CursorShape`].
|
||||||
|
fn cursor_shape_from(style: Option<&str>) -> Option<CursorShape> {
|
||||||
|
match style? {
|
||||||
|
"block" => Some(CursorShape::Block),
|
||||||
|
"beam" | "bar" => Some(CursorShape::Beam),
|
||||||
|
"underline" => Some(CursorShape::Underline),
|
||||||
|
other => {
|
||||||
|
tracing::warn!("unknown cursor style {other:?}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Map a Wayland button code to the terminal mouse base code, if reportable.
|
/// Map a Wayland button code to the terminal mouse base code, if reportable.
|
||||||
fn button_code(button: u32) -> Option<u8> {
|
fn button_code(button: u32) -> Option<u8> {
|
||||||
match button {
|
match button {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue