mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-31 23:28:45 +00:00
fe95e4f4cd
No longer supported by keys, and these are not a thing in the real world
521 lines
22 KiB
Rust
521 lines
22 KiB
Rust
//! A wrapper around the system's curses/ncurses library, exposing some lower-level functionality
|
|
//! that's not directly exposed in any of the popular ncurses crates.
|
|
//!
|
|
//! In addition to exposing the C library ffi calls, we also shim around some functionality that's
|
|
//! only made available via the ncurses headers to C code via macro magic, such as polyfilling
|
|
//! missing capability strings to shoe-in missing support for certain terminal sequences.
|
|
//!
|
|
//! This is intentionally very bare bones and only implements the subset of curses functionality
|
|
//! used by fish
|
|
|
|
use crate::common::ToCString;
|
|
use crate::FLOGF;
|
|
use std::env;
|
|
use std::ffi::{CStr, CString};
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
use std::sync::Mutex;
|
|
|
|
/// The [`Term`] singleton, providing a façade around the system curses library. Initialized via a
|
|
/// successful call to [`setup()`] and surfaced to the outside world via [`term()`].
|
|
///
|
|
/// It isn't guaranteed that fish will ever be able to successfully call `setup()`, so this must
|
|
/// remain an `Option` instead of returning `Term` by default and just panicking if [`term()`] was
|
|
/// called before `setup()`.
|
|
///
|
|
/// We can't just use an AtomicPtr<Arc<Term>> here because there's a race condition when the old Arc
|
|
/// gets dropped - we would obtain the current (non-null) value of `TERM` in [`term()`] but there's
|
|
/// no guarantee that a simultaneous call to [`setup()`] won't result in this refcount being
|
|
/// decremented to zero and the memory being reclaimed before we can clone it, since we can only
|
|
/// atomically *read* the value of the pointer, not clone the `Arc` it points to.
|
|
pub static TERM: Mutex<Option<Arc<Term>>> = Mutex::new(None);
|
|
|
|
/// Returns a reference to the global [`Term`] singleton or `None` if not preceded by a successful
|
|
/// call to [`curses::setup()`].
|
|
pub fn term() -> Option<Arc<Term>> {
|
|
TERM.lock()
|
|
.expect("Mutex poisoned!")
|
|
.as_ref()
|
|
.map(Arc::clone)
|
|
}
|
|
|
|
/// The safe wrapper around curses functionality, initialized by a successful call to [`setup()`]
|
|
/// and obtained thereafter by calls to [`term()`].
|
|
///
|
|
/// An extant `Term` instance means the curses `TERMINAL *cur_term` pointer is non-null. Any
|
|
/// functionality that is normally performed using `cur_term` should be done via `Term` instead.
|
|
#[derive(Default)]
|
|
pub struct Term {
|
|
// String capabilities. Any Some value is confirmed non-empty.
|
|
pub enter_bold_mode: Option<CString>,
|
|
pub enter_italics_mode: Option<CString>,
|
|
pub exit_italics_mode: Option<CString>,
|
|
pub enter_dim_mode: Option<CString>,
|
|
pub enter_underline_mode: Option<CString>,
|
|
pub exit_underline_mode: Option<CString>,
|
|
pub enter_reverse_mode: Option<CString>,
|
|
pub enter_standout_mode: Option<CString>,
|
|
pub exit_standout_mode: Option<CString>,
|
|
pub enter_blink_mode: Option<CString>,
|
|
pub enter_protected_mode: Option<CString>,
|
|
pub enter_shadow_mode: Option<CString>,
|
|
pub exit_shadow_mode: Option<CString>,
|
|
pub enter_secure_mode: Option<CString>,
|
|
pub enter_alt_charset_mode: Option<CString>,
|
|
pub exit_alt_charset_mode: Option<CString>,
|
|
pub set_a_foreground: Option<CString>,
|
|
pub set_foreground: Option<CString>,
|
|
pub set_a_background: Option<CString>,
|
|
pub set_background: Option<CString>,
|
|
pub exit_attribute_mode: Option<CString>,
|
|
pub set_title: Option<CString>,
|
|
pub clear_screen: Option<CString>,
|
|
pub cursor_up: Option<CString>,
|
|
pub cursor_down: Option<CString>,
|
|
pub cursor_left: Option<CString>,
|
|
pub cursor_right: Option<CString>,
|
|
pub parm_left_cursor: Option<CString>,
|
|
pub parm_right_cursor: Option<CString>,
|
|
pub clr_eol: Option<CString>,
|
|
pub clr_eos: Option<CString>,
|
|
|
|
// Number capabilities
|
|
pub max_colors: Option<usize>,
|
|
pub init_tabs: Option<usize>,
|
|
|
|
// Flag/boolean capabilities
|
|
pub eat_newline_glitch: bool,
|
|
pub auto_right_margin: bool,
|
|
|
|
// Keys.
|
|
pub key_a1: Option<CString>,
|
|
pub key_a3: Option<CString>,
|
|
pub key_b2: Option<CString>,
|
|
pub key_backspace: Option<CString>,
|
|
pub key_beg: Option<CString>,
|
|
pub key_btab: Option<CString>,
|
|
pub key_c1: Option<CString>,
|
|
pub key_c3: Option<CString>,
|
|
pub key_cancel: Option<CString>,
|
|
pub key_catab: Option<CString>,
|
|
pub key_clear: Option<CString>,
|
|
pub key_close: Option<CString>,
|
|
pub key_command: Option<CString>,
|
|
pub key_copy: Option<CString>,
|
|
pub key_create: Option<CString>,
|
|
pub key_ctab: Option<CString>,
|
|
pub key_dc: Option<CString>,
|
|
pub key_dl: Option<CString>,
|
|
pub key_down: Option<CString>,
|
|
pub key_eic: Option<CString>,
|
|
pub key_end: Option<CString>,
|
|
pub key_enter: Option<CString>,
|
|
pub key_eol: Option<CString>,
|
|
pub key_eos: Option<CString>,
|
|
pub key_exit: Option<CString>,
|
|
pub key_f0: Option<CString>,
|
|
pub key_f1: Option<CString>,
|
|
pub key_f2: Option<CString>,
|
|
pub key_f3: Option<CString>,
|
|
pub key_f4: Option<CString>,
|
|
pub key_f5: Option<CString>,
|
|
pub key_f6: Option<CString>,
|
|
pub key_f7: Option<CString>,
|
|
pub key_f8: Option<CString>,
|
|
pub key_f9: Option<CString>,
|
|
pub key_f10: Option<CString>,
|
|
pub key_f11: Option<CString>,
|
|
pub key_f12: Option<CString>,
|
|
// Note key_f21 through key_f63 are available but no actual keyboard supports them.
|
|
// key_f13 and up are also basically unused and not supported by key.rs
|
|
pub key_find: Option<CString>,
|
|
pub key_help: Option<CString>,
|
|
pub key_home: Option<CString>,
|
|
pub key_ic: Option<CString>,
|
|
pub key_il: Option<CString>,
|
|
pub key_left: Option<CString>,
|
|
pub key_ll: Option<CString>,
|
|
pub key_mark: Option<CString>,
|
|
pub key_message: Option<CString>,
|
|
pub key_move: Option<CString>,
|
|
pub key_next: Option<CString>,
|
|
pub key_npage: Option<CString>,
|
|
pub key_open: Option<CString>,
|
|
pub key_options: Option<CString>,
|
|
pub key_ppage: Option<CString>,
|
|
pub key_previous: Option<CString>,
|
|
pub key_print: Option<CString>,
|
|
pub key_redo: Option<CString>,
|
|
pub key_reference: Option<CString>,
|
|
pub key_refresh: Option<CString>,
|
|
pub key_replace: Option<CString>,
|
|
pub key_restart: Option<CString>,
|
|
pub key_resume: Option<CString>,
|
|
pub key_right: Option<CString>,
|
|
pub key_save: Option<CString>,
|
|
pub key_sbeg: Option<CString>,
|
|
pub key_scancel: Option<CString>,
|
|
pub key_scommand: Option<CString>,
|
|
pub key_scopy: Option<CString>,
|
|
pub key_screate: Option<CString>,
|
|
pub key_sdc: Option<CString>,
|
|
pub key_sdl: Option<CString>,
|
|
pub key_select: Option<CString>,
|
|
pub key_send: Option<CString>,
|
|
pub key_seol: Option<CString>,
|
|
pub key_sexit: Option<CString>,
|
|
pub key_sf: Option<CString>,
|
|
pub key_sfind: Option<CString>,
|
|
pub key_shelp: Option<CString>,
|
|
pub key_shome: Option<CString>,
|
|
pub key_sic: Option<CString>,
|
|
pub key_sleft: Option<CString>,
|
|
pub key_smessage: Option<CString>,
|
|
pub key_smove: Option<CString>,
|
|
pub key_snext: Option<CString>,
|
|
pub key_soptions: Option<CString>,
|
|
pub key_sprevious: Option<CString>,
|
|
pub key_sprint: Option<CString>,
|
|
pub key_sr: Option<CString>,
|
|
pub key_sredo: Option<CString>,
|
|
pub key_sreplace: Option<CString>,
|
|
pub key_sright: Option<CString>,
|
|
pub key_srsume: Option<CString>,
|
|
pub key_ssave: Option<CString>,
|
|
pub key_ssuspend: Option<CString>,
|
|
pub key_stab: Option<CString>,
|
|
pub key_sundo: Option<CString>,
|
|
pub key_suspend: Option<CString>,
|
|
pub key_undo: Option<CString>,
|
|
pub key_up: Option<CString>,
|
|
}
|
|
|
|
impl Term {
|
|
/// Initialize a new `Term` instance, prepopulating the values of all the curses string
|
|
/// capabilities we care about in the process.
|
|
fn new(db: terminfo::Database) -> Self {
|
|
Term {
|
|
// String capabilities
|
|
enter_bold_mode: get_str_cap(&db, "md"),
|
|
enter_italics_mode: get_str_cap(&db, "ZH"),
|
|
exit_italics_mode: get_str_cap(&db, "ZR"),
|
|
enter_dim_mode: get_str_cap(&db, "mh"),
|
|
enter_underline_mode: get_str_cap(&db, "us"),
|
|
exit_underline_mode: get_str_cap(&db, "ue"),
|
|
enter_reverse_mode: get_str_cap(&db, "mr"),
|
|
enter_standout_mode: get_str_cap(&db, "so"),
|
|
exit_standout_mode: get_str_cap(&db, "se"),
|
|
enter_blink_mode: get_str_cap(&db, "mb"),
|
|
enter_protected_mode: get_str_cap(&db, "mp"),
|
|
enter_shadow_mode: get_str_cap(&db, "ZM"),
|
|
exit_shadow_mode: get_str_cap(&db, "ZU"),
|
|
enter_secure_mode: get_str_cap(&db, "mk"),
|
|
enter_alt_charset_mode: get_str_cap(&db, "as"),
|
|
exit_alt_charset_mode: get_str_cap(&db, "ae"),
|
|
set_a_foreground: get_str_cap(&db, "AF"),
|
|
set_foreground: get_str_cap(&db, "Sf"),
|
|
set_a_background: get_str_cap(&db, "AB"),
|
|
set_background: get_str_cap(&db, "Sb"),
|
|
exit_attribute_mode: get_str_cap(&db, "me"),
|
|
set_title: get_str_cap(&db, "ts"),
|
|
clear_screen: get_str_cap(&db, "cl"),
|
|
cursor_up: get_str_cap(&db, "up"),
|
|
cursor_down: get_str_cap(&db, "do"),
|
|
cursor_left: get_str_cap(&db, "le"),
|
|
cursor_right: get_str_cap(&db, "nd"),
|
|
parm_left_cursor: get_str_cap(&db, "LE"),
|
|
parm_right_cursor: get_str_cap(&db, "RI"),
|
|
clr_eol: get_str_cap(&db, "ce"),
|
|
clr_eos: get_str_cap(&db, "cd"),
|
|
|
|
// Number capabilities
|
|
max_colors: get_num_cap(&db, "Co"),
|
|
init_tabs: get_num_cap(&db, "it"),
|
|
|
|
// Flag/boolean capabilities
|
|
eat_newline_glitch: get_flag_cap(&db, "xn"),
|
|
auto_right_margin: get_flag_cap(&db, "am"),
|
|
|
|
// Keys. See `man terminfo` for these strings.
|
|
key_a1: get_str_cap(&db, "K1"),
|
|
key_a3: get_str_cap(&db, "K3"),
|
|
key_b2: get_str_cap(&db, "K2"),
|
|
key_backspace: get_str_cap(&db, "kb"),
|
|
key_beg: get_str_cap(&db, "@1"),
|
|
key_btab: get_str_cap(&db, "kB"),
|
|
key_c1: get_str_cap(&db, "K4"),
|
|
key_c3: get_str_cap(&db, "K5"),
|
|
key_cancel: get_str_cap(&db, "@2"),
|
|
key_catab: get_str_cap(&db, "ka"),
|
|
key_clear: get_str_cap(&db, "kC"),
|
|
key_close: get_str_cap(&db, "@3"),
|
|
key_command: get_str_cap(&db, "@4"),
|
|
key_copy: get_str_cap(&db, "@5"),
|
|
key_create: get_str_cap(&db, "@6"),
|
|
key_ctab: get_str_cap(&db, "kt"),
|
|
key_dc: get_str_cap(&db, "kD"),
|
|
key_dl: get_str_cap(&db, "kL"),
|
|
key_down: get_str_cap(&db, "kd"),
|
|
key_eic: get_str_cap(&db, "kM"),
|
|
key_end: get_str_cap(&db, "@7"),
|
|
key_enter: get_str_cap(&db, "@8"),
|
|
key_eol: get_str_cap(&db, "kE"),
|
|
key_eos: get_str_cap(&db, "kS"),
|
|
key_exit: get_str_cap(&db, "@9"),
|
|
key_f0: get_str_cap(&db, "k0"),
|
|
key_f1: get_str_cap(&db, "k1"),
|
|
key_f10: get_str_cap(&db, "k;"),
|
|
key_f11: get_str_cap(&db, "F1"),
|
|
key_f12: get_str_cap(&db, "F2"),
|
|
key_f2: get_str_cap(&db, "k2"),
|
|
key_f3: get_str_cap(&db, "k3"),
|
|
key_f4: get_str_cap(&db, "k4"),
|
|
key_f5: get_str_cap(&db, "k5"),
|
|
key_f6: get_str_cap(&db, "k6"),
|
|
key_f7: get_str_cap(&db, "k7"),
|
|
key_f8: get_str_cap(&db, "k8"),
|
|
key_f9: get_str_cap(&db, "k9"),
|
|
key_find: get_str_cap(&db, "@0"),
|
|
key_help: get_str_cap(&db, "%1"),
|
|
key_home: get_str_cap(&db, "kh"),
|
|
key_ic: get_str_cap(&db, "kI"),
|
|
key_il: get_str_cap(&db, "kA"),
|
|
key_left: get_str_cap(&db, "kl"),
|
|
key_ll: get_str_cap(&db, "kH"),
|
|
key_mark: get_str_cap(&db, "%2"),
|
|
key_message: get_str_cap(&db, "%3"),
|
|
key_move: get_str_cap(&db, "%4"),
|
|
key_next: get_str_cap(&db, "%5"),
|
|
key_npage: get_str_cap(&db, "kN"),
|
|
key_open: get_str_cap(&db, "%6"),
|
|
key_options: get_str_cap(&db, "%7"),
|
|
key_ppage: get_str_cap(&db, "kP"),
|
|
key_previous: get_str_cap(&db, "%8"),
|
|
key_print: get_str_cap(&db, "%9"),
|
|
key_redo: get_str_cap(&db, "%0"),
|
|
key_reference: get_str_cap(&db, "&1"),
|
|
key_refresh: get_str_cap(&db, "&2"),
|
|
key_replace: get_str_cap(&db, "&3"),
|
|
key_restart: get_str_cap(&db, "&4"),
|
|
key_resume: get_str_cap(&db, "&5"),
|
|
key_right: get_str_cap(&db, "kr"),
|
|
key_save: get_str_cap(&db, "&6"),
|
|
key_sbeg: get_str_cap(&db, "&9"),
|
|
key_scancel: get_str_cap(&db, "&0"),
|
|
key_scommand: get_str_cap(&db, "*1"),
|
|
key_scopy: get_str_cap(&db, "*2"),
|
|
key_screate: get_str_cap(&db, "*3"),
|
|
key_sdc: get_str_cap(&db, "*4"),
|
|
key_sdl: get_str_cap(&db, "*5"),
|
|
key_select: get_str_cap(&db, "*6"),
|
|
key_send: get_str_cap(&db, "*7"),
|
|
key_seol: get_str_cap(&db, "*8"),
|
|
key_sexit: get_str_cap(&db, "*9"),
|
|
key_sf: get_str_cap(&db, "kF"),
|
|
key_sfind: get_str_cap(&db, "*0"),
|
|
key_shelp: get_str_cap(&db, "#1"),
|
|
key_shome: get_str_cap(&db, "#2"),
|
|
key_sic: get_str_cap(&db, "#3"),
|
|
key_sleft: get_str_cap(&db, "#4"),
|
|
key_smessage: get_str_cap(&db, "%a"),
|
|
key_smove: get_str_cap(&db, "%b"),
|
|
key_snext: get_str_cap(&db, "%c"),
|
|
key_soptions: get_str_cap(&db, "%d"),
|
|
key_sprevious: get_str_cap(&db, "%e"),
|
|
key_sprint: get_str_cap(&db, "%f"),
|
|
key_sr: get_str_cap(&db, "kR"),
|
|
key_sredo: get_str_cap(&db, "%g"),
|
|
key_sreplace: get_str_cap(&db, "%h"),
|
|
key_sright: get_str_cap(&db, "%i"),
|
|
key_srsume: get_str_cap(&db, "%j"),
|
|
key_ssave: get_str_cap(&db, "!1"),
|
|
key_ssuspend: get_str_cap(&db, "!2"),
|
|
key_stab: get_str_cap(&db, "kT"),
|
|
key_sundo: get_str_cap(&db, "!3"),
|
|
key_suspend: get_str_cap(&db, "&7"),
|
|
key_undo: get_str_cap(&db, "&8"),
|
|
key_up: get_str_cap(&db, "ku"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Calls the curses `setupterm()` function with the provided `$TERM` value `term` (or a null
|
|
/// pointer in case `term` is null) for the file descriptor `fd`. Returns a reference to the newly
|
|
/// initialized [`Term`] singleton on success or `None` if this failed.
|
|
///
|
|
/// The `configure` parameter may be set to a callback that takes an `&mut Term` reference to
|
|
/// override any capabilities before the `Term` is permanently made immutable.
|
|
///
|
|
/// Note that the `errret` parameter is provided to the function, meaning curses will not write
|
|
/// error output to stderr in case of failure.
|
|
///
|
|
/// Any existing references from `curses::term()` will be invalidated by this call!
|
|
pub fn setup<F>(term: Option<&str>, configure: F) -> Option<Arc<Term>>
|
|
where
|
|
F: Fn(&mut Term),
|
|
{
|
|
// For now, use the same TERM lock when using `cur_term` to prevent any race conditions in
|
|
// curses itself. We might split this to another lock in the future.
|
|
let mut global_term = TERM.lock().expect("Mutex poisoned!");
|
|
|
|
let res = if let Some(term) = term {
|
|
terminfo::Database::from_name(term)
|
|
} else {
|
|
// For historical reasons getting "None" means to get it from the environment.
|
|
terminfo::Database::from_env()
|
|
}
|
|
.or_else(|x| {
|
|
// Try some more paths
|
|
let t = if let Some(term) = term {
|
|
term.to_string()
|
|
} else if let Ok(name) = env::var("TERM") {
|
|
name
|
|
} else {
|
|
return Err(x);
|
|
};
|
|
let first_char = t.chars().next().unwrap().to_string();
|
|
for dir in [
|
|
"/run/current-system/sw/share/terminfo", // Nix
|
|
"/usr/pkg/share/terminfo", // NetBSD
|
|
] {
|
|
let mut path = PathBuf::from(dir);
|
|
path.push(first_char.clone());
|
|
path.push(t.clone());
|
|
FLOGF!(term_support, "Trying path '%ls'", path.to_str().unwrap());
|
|
if let Ok(db) = terminfo::Database::from_path(path) {
|
|
return Ok(db);
|
|
}
|
|
}
|
|
Err(x)
|
|
});
|
|
|
|
// Safely store the new Term instance or replace the old one. We have the lock so it's safe to
|
|
// drop the old TERM value and have its refcount decremented - no one will be cloning it.
|
|
if let Ok(result) = res {
|
|
// Create a new `Term` instance, prepopulate the capabilities we care about, and allow the
|
|
// caller to override any as needed.
|
|
let mut term = Term::new(result);
|
|
(configure)(&mut term);
|
|
|
|
let term = Arc::new(term);
|
|
*global_term = Some(term.clone());
|
|
Some(term)
|
|
} else {
|
|
*global_term = None;
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn setup_fallback_term() -> Arc<Term> {
|
|
let mut global_term = TERM.lock().expect("Mutex poisoned!");
|
|
// These values extracted from xterm-256color from ncurses 6.4
|
|
let term = Term {
|
|
enter_bold_mode: Some(CString::new("\x1b[1m").unwrap()),
|
|
enter_italics_mode: Some(CString::new("\x1b[3m").unwrap()),
|
|
exit_italics_mode: Some(CString::new("\x1b[23m").unwrap()),
|
|
enter_dim_mode: Some(CString::new("\x1b[2m").unwrap()),
|
|
enter_underline_mode: Some(CString::new("\x1b[4m").unwrap()),
|
|
exit_underline_mode: Some(CString::new("\x1b[24m").unwrap()),
|
|
enter_reverse_mode: Some(CString::new("\x1b[7m").unwrap()),
|
|
enter_standout_mode: Some(CString::new("\x1b[7m").unwrap()),
|
|
exit_standout_mode: Some(CString::new("\x1b[27m").unwrap()),
|
|
enter_blink_mode: Some(CString::new("\x1b[5m").unwrap()),
|
|
enter_secure_mode: Some(CString::new("\x1b[8m").unwrap()),
|
|
enter_alt_charset_mode: Some(CString::new("\x1b(0").unwrap()),
|
|
exit_alt_charset_mode: Some(CString::new("\x1b(B").unwrap()),
|
|
set_a_foreground: Some(
|
|
CString::new("\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m")
|
|
.unwrap(),
|
|
),
|
|
set_a_background: Some(
|
|
CString::new("\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m")
|
|
.unwrap(),
|
|
),
|
|
exit_attribute_mode: Some(CString::new("\x1b(B\x1b[m").unwrap()),
|
|
clear_screen: Some(CString::new("\x1b[H\x1b[2J").unwrap()),
|
|
cursor_up: Some(CString::new("\x1b[A").unwrap()),
|
|
cursor_down: Some(CString::new("\n").unwrap()),
|
|
cursor_left: Some(CString::new("\x08").unwrap()),
|
|
cursor_right: Some(CString::new("\x1b[C").unwrap()),
|
|
parm_left_cursor: Some(CString::new("\x1b[%p1%dD").unwrap()),
|
|
parm_right_cursor: Some(CString::new("\x1b[%p1%dC").unwrap()),
|
|
clr_eol: Some(CString::new("\x1b[K").unwrap()),
|
|
clr_eos: Some(CString::new("\x1b[J").unwrap()),
|
|
max_colors: Some(256),
|
|
init_tabs: Some(8),
|
|
eat_newline_glitch: true,
|
|
auto_right_margin: true,
|
|
key_a1: Some(CString::new("\x1bOw").unwrap()),
|
|
key_a3: Some(CString::new("\x1bOy").unwrap()),
|
|
key_b2: Some(CString::new("\x1bOu").unwrap()),
|
|
key_backspace: Some(CString::new("\x7f").unwrap()),
|
|
key_btab: Some(CString::new("\x1b[Z").unwrap()),
|
|
key_c1: Some(CString::new("\x1bOq").unwrap()),
|
|
key_c3: Some(CString::new("\x1bOs").unwrap()),
|
|
key_dc: Some(CString::new("\x1b[3~").unwrap()),
|
|
key_down: Some(CString::new("\x1bOB").unwrap()),
|
|
key_f1: Some(CString::new("\x1bOP").unwrap()),
|
|
key_home: Some(CString::new("\x1bOH").unwrap()),
|
|
key_ic: Some(CString::new("\x1b[2~").unwrap()),
|
|
key_left: Some(CString::new("\x1bOD").unwrap()),
|
|
key_npage: Some(CString::new("\x1b[6~").unwrap()),
|
|
key_ppage: Some(CString::new("\x1b[5~").unwrap()),
|
|
key_right: Some(CString::new("\x1bOC").unwrap()),
|
|
key_sdc: Some(CString::new("\x1b[3;2~").unwrap()),
|
|
key_send: Some(CString::new("\x1b[1;2F").unwrap()),
|
|
key_sf: Some(CString::new("\x1b[1;2B").unwrap()),
|
|
key_shome: Some(CString::new("\x1b[1;2H").unwrap()),
|
|
key_sic: Some(CString::new("\x1b[2;2~").unwrap()),
|
|
key_sleft: Some(CString::new("\x1b[1;2D").unwrap()),
|
|
key_snext: Some(CString::new("\x1b[6;2~").unwrap()),
|
|
key_sprevious: Some(CString::new("\x1b[5;2~").unwrap()),
|
|
key_sr: Some(CString::new("\x1b[1;2A").unwrap()),
|
|
key_sright: Some(CString::new("\x1b[1;2C").unwrap()),
|
|
key_up: Some(CString::new("\x1bOA").unwrap()),
|
|
..Default::default()
|
|
};
|
|
let term = Arc::new(term);
|
|
*global_term = Some(term.clone());
|
|
term
|
|
}
|
|
|
|
/// Return a nonempty String capability from termcap, or None if missing or empty.
|
|
/// Panics if the given code string does not contain exactly two bytes.
|
|
fn get_str_cap(db: &terminfo::Database, code: &str) -> Option<CString> {
|
|
db.raw(code).map(|cap| match cap {
|
|
terminfo::Value::True => "1".to_string().as_bytes().to_cstring(),
|
|
terminfo::Value::Number(n) => n.to_string().as_bytes().to_cstring(),
|
|
terminfo::Value::String(s) => s.clone().to_cstring(),
|
|
})
|
|
}
|
|
|
|
/// Return a number capability from termcap, or None if missing.
|
|
/// Panics if the given code string does not contain exactly two bytes.
|
|
fn get_num_cap(db: &terminfo::Database, code: &str) -> Option<usize> {
|
|
match db.raw(code) {
|
|
Some(terminfo::Value::Number(n)) if *n >= 0 => Some(*n as usize),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a flag capability from termcap, or false if missing.
|
|
/// Panics if the given code string does not contain exactly two bytes.
|
|
fn get_flag_cap(db: &terminfo::Database, code: &str) -> bool {
|
|
db.raw(code)
|
|
.map(|cap| matches!(cap, terminfo::Value::True))
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
/// Covers over tparm().
|
|
pub fn tparm0(cap: &CStr) -> Option<CString> {
|
|
assert!(!cap.to_bytes().is_empty());
|
|
let cap = cap.to_bytes();
|
|
terminfo::expand!(cap).ok().map(|x| x.to_cstring())
|
|
}
|
|
|
|
/// Covers over tparm().
|
|
pub fn tparm1(cap: &CStr, param1: i32) -> Option<CString> {
|
|
assert!(!cap.to_bytes().is_empty());
|
|
let cap = cap.to_bytes();
|
|
terminfo::expand!(cap; param1).ok().map(|x| x.to_cstring())
|
|
}
|