use crate::*;
use crate::options_::options_get_number_;
pub struct input_key_entry {
pub key: key_code,
pub data: *const u8,
pub entry: rb_entry<input_key_entry>,
}
pub type input_key_tree = rb_head<input_key_entry>;
impl input_key_entry {
const fn new(key: key_code, data: &'static CStr) -> Self {
Self {
key,
data: data.as_ptr().cast(),
entry: unsafe { zeroed() },
}
}
}
pub fn input_key_cmp(ike1: &input_key_entry, ike2: &input_key_entry) -> cmp::Ordering {
ike1.key.cmp(&ike2.key)
}
RB_GENERATE!(
input_key_tree,
input_key_entry,
entry,
discr_entry,
input_key_cmp
);
static mut INPUT_KEY_TREE: input_key_tree = rb_initializer();
const INPUT_KEY_DEFAULTS_LEN: usize = 85;
static mut INPUT_KEY_DEFAULTS: [input_key_entry; 85] = [
input_key_entry::new(keyc::KEYC_PASTE_START as u64, c"\x1b[200~"),
input_key_entry::new(keyc::KEYC_PASTE_START as u64 | KEYC_IMPLIED_META, c"\x1b[200~"),
input_key_entry::new(keyc::KEYC_PASTE_END as u64, c"\x1b[201~"),
input_key_entry::new(keyc::KEYC_PASTE_END as u64 | KEYC_IMPLIED_META, c"\x1b[201~"),
input_key_entry::new(keyc::KEYC_F1 as u64, c"\x1bOP"),
input_key_entry::new(keyc::KEYC_F2 as u64, c"\x1bOQ"),
input_key_entry::new(keyc::KEYC_F3 as u64, c"\x1bOR"),
input_key_entry::new(keyc::KEYC_F4 as u64, c"\x1bOS"),
input_key_entry::new(keyc::KEYC_F5 as u64, c"\x1b[15~"),
input_key_entry::new(keyc::KEYC_F6 as u64, c"\x1b[17~"),
input_key_entry::new(keyc::KEYC_F7 as u64, c"\x1b[18~"),
input_key_entry::new(keyc::KEYC_F8 as u64, c"\x1b[19~"),
input_key_entry::new(keyc::KEYC_F9 as u64, c"\x1b[20~"),
input_key_entry::new(keyc::KEYC_F10 as u64, c"\x1b[21~"),
input_key_entry::new(keyc::KEYC_F11 as u64, c"\x1b[23~"),
input_key_entry::new(keyc::KEYC_F12 as u64, c"\x1b[24~"),
input_key_entry::new(keyc::KEYC_IC as u64, c"\x1b[2~"),
input_key_entry::new(keyc::KEYC_DC as u64, c"\x1b[3~"),
input_key_entry::new(keyc::KEYC_HOME as u64, c"\x1b[1~"),
input_key_entry::new(keyc::KEYC_END as u64, c"\x1b[4~"),
input_key_entry::new(keyc::KEYC_NPAGE as u64, c"\x1b[6~"),
input_key_entry::new(keyc::KEYC_PPAGE as u64, c"\x1b[5~"),
input_key_entry::new(keyc::KEYC_BTAB as u64, c"\x1b[Z"),
input_key_entry::new(keyc::KEYC_UP as u64 | KEYC_CURSOR, c"\x1bOA"),
input_key_entry::new(keyc::KEYC_DOWN as u64 | KEYC_CURSOR, c"\x1bOB"),
input_key_entry::new(keyc::KEYC_RIGHT as u64 | KEYC_CURSOR, c"\x1bOC"),
input_key_entry::new(keyc::KEYC_LEFT as u64 | KEYC_CURSOR, c"\x1bOD"),
input_key_entry::new(keyc::KEYC_UP as u64, c"\x1b[A"),
input_key_entry::new(keyc::KEYC_DOWN as u64, c"\x1b[B"),
input_key_entry::new(keyc::KEYC_RIGHT as u64, c"\x1b[C"),
input_key_entry::new(keyc::KEYC_LEFT as u64, c"\x1b[D"),
input_key_entry::new(keyc::KEYC_KP_SLASH as u64 | KEYC_KEYPAD, c"\x1bOo"),
input_key_entry::new(keyc::KEYC_KP_STAR as u64 | KEYC_KEYPAD, c"\x1bOj"),
input_key_entry::new(keyc::KEYC_KP_MINUS as u64 | KEYC_KEYPAD, c"\x1bOm"),
input_key_entry::new(keyc::KEYC_KP_SEVEN as u64 | KEYC_KEYPAD, c"\x1bOw"),
input_key_entry::new(keyc::KEYC_KP_EIGHT as u64 | KEYC_KEYPAD, c"\x1bOx"),
input_key_entry::new(keyc::KEYC_KP_NINE as u64 | KEYC_KEYPAD, c"\x1bOy"),
input_key_entry::new(keyc::KEYC_KP_PLUS as u64 | KEYC_KEYPAD, c"\x1bOk"),
input_key_entry::new(keyc::KEYC_KP_FOUR as u64 | KEYC_KEYPAD, c"\x1bOt"),
input_key_entry::new(keyc::KEYC_KP_FIVE as u64 | KEYC_KEYPAD, c"\x1bOu"),
input_key_entry::new(keyc::KEYC_KP_SIX as u64 | KEYC_KEYPAD, c"\x1bOv"),
input_key_entry::new(keyc::KEYC_KP_ONE as u64 | KEYC_KEYPAD, c"\x1bOq"),
input_key_entry::new(keyc::KEYC_KP_TWO as u64 | KEYC_KEYPAD, c"\x1bOr"),
input_key_entry::new(keyc::KEYC_KP_THREE as u64 | KEYC_KEYPAD, c"\x1bOs"),
input_key_entry::new(keyc::KEYC_KP_ENTER as u64 | KEYC_KEYPAD, c"\x1bOM"),
input_key_entry::new(keyc::KEYC_KP_ZERO as u64 | KEYC_KEYPAD, c"\x1bOp"),
input_key_entry::new(keyc::KEYC_KP_PERIOD as u64 | KEYC_KEYPAD, c"\x1bOn"),
input_key_entry::new(keyc::KEYC_KP_SLASH as u64, c"/"),
input_key_entry::new(keyc::KEYC_KP_STAR as u64, c"*"),
input_key_entry::new(keyc::KEYC_KP_MINUS as u64, c"-"),
input_key_entry::new(keyc::KEYC_KP_SEVEN as u64, c"7"),
input_key_entry::new(keyc::KEYC_KP_EIGHT as u64, c"8"),
input_key_entry::new(keyc::KEYC_KP_NINE as u64, c"9"),
input_key_entry::new(keyc::KEYC_KP_PLUS as u64, c"+"),
input_key_entry::new(keyc::KEYC_KP_FOUR as u64, c"4"),
input_key_entry::new(keyc::KEYC_KP_FIVE as u64, c"5"),
input_key_entry::new(keyc::KEYC_KP_SIX as u64, c"6"),
input_key_entry::new(keyc::KEYC_KP_ONE as u64, c"1"),
input_key_entry::new(keyc::KEYC_KP_TWO as u64, c"2"),
input_key_entry::new(keyc::KEYC_KP_THREE as u64, c"3"),
input_key_entry::new(keyc::KEYC_KP_ENTER as u64, c"\n"),
input_key_entry::new(keyc::KEYC_KP_ZERO as u64, c"0"),
input_key_entry::new(keyc::KEYC_KP_PERIOD as u64, c"."),
input_key_entry::new(keyc::KEYC_F1 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_P"),
input_key_entry::new(keyc::KEYC_F2 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_Q"),
input_key_entry::new(keyc::KEYC_F3 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_R"),
input_key_entry::new(keyc::KEYC_F4 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_S"),
input_key_entry::new(keyc::KEYC_F5 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[15;_~"),
input_key_entry::new(keyc::KEYC_F6 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[17;_~"),
input_key_entry::new(keyc::KEYC_F7 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[18;_~"),
input_key_entry::new(keyc::KEYC_F8 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[19;_~"),
input_key_entry::new(keyc::KEYC_F9 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[20;_~"),
input_key_entry::new(keyc::KEYC_F10 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[21;_~"),
input_key_entry::new(keyc::KEYC_F11 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[23;_~"),
input_key_entry::new(keyc::KEYC_F12 as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[24;_~"),
input_key_entry::new(keyc::KEYC_UP as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_A"),
input_key_entry::new(keyc::KEYC_DOWN as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_B"),
input_key_entry::new(keyc::KEYC_RIGHT as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_C"),
input_key_entry::new(keyc::KEYC_LEFT as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_D"),
input_key_entry::new(keyc::KEYC_HOME as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_H"),
input_key_entry::new(keyc::KEYC_END as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[1;_F"),
input_key_entry::new(keyc::KEYC_PPAGE as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[5;_~"),
input_key_entry::new(keyc::KEYC_NPAGE as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[6;_~"),
input_key_entry::new(keyc::KEYC_IC as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[2;_~"),
input_key_entry::new(keyc::KEYC_DC as u64 | KEYC_BUILD_MODIFIERS, c"\x1b[3;_~"),
];
static INPUT_KEY_MODIFIERS: [key_code; 9] = [
0,
0,
KEYC_SHIFT,
KEYC_META | KEYC_IMPLIED_META,
KEYC_SHIFT | KEYC_META | KEYC_IMPLIED_META,
KEYC_CTRL,
KEYC_SHIFT | KEYC_CTRL,
KEYC_META | KEYC_IMPLIED_META | KEYC_CTRL,
KEYC_SHIFT | KEYC_META | KEYC_IMPLIED_META | KEYC_CTRL,
];
pub unsafe fn input_key_get(key: key_code) -> *mut input_key_entry {
unsafe {
let mut entry = MaybeUninit::<input_key_entry>::uninit();
(*entry.as_mut_ptr()).key = key;
rb_find(&raw mut INPUT_KEY_TREE, entry.as_mut_ptr())
}
}
pub unsafe fn input_key_split2(c: u32, dst: *mut u8) -> usize {
unsafe {
if c > 0x7f {
*dst = (c >> 6) as u8 | 0xc0;
*dst.add(1) = (c as u8 & 0x3f) | 0x80;
2
} else {
*dst = c as u8;
1
}
}
}
#[expect(clippy::needless_range_loop)]
pub unsafe extern "C-unwind" fn input_key_build() {
unsafe {
for i in 0..INPUT_KEY_DEFAULTS_LEN {
let ike = &raw mut INPUT_KEY_DEFAULTS[i];
if !(*ike).key & KEYC_BUILD_MODIFIERS != 0 {
rb_insert(&raw mut INPUT_KEY_TREE, ike);
continue;
}
for (j, input_key_modifiers_j) in
INPUT_KEY_MODIFIERS.iter().copied().enumerate().skip(2)
{
let key = (*ike).key & !KEYC_BUILD_MODIFIERS;
let data = xstrdup((*ike).data).as_ptr();
*data.add(libc::strcspn(data, c!("_"))) = b'0' + j as u8;
let new = xcalloc1::<input_key_entry>();
new.key = key | input_key_modifiers_j;
new.data = data;
rb_insert(&raw mut INPUT_KEY_TREE, new);
}
}
for ike in rb_foreach(&raw mut INPUT_KEY_TREE).map(NonNull::as_ptr) {
log_debug!(
"{}:{} : 0x{:x} ({}) is {}",
file!(),
line!(),
(*ike).key,
_s(key_string_lookup_key((*ike).key, 1)),
_s((*ike).data)
);
}
}
}
pub unsafe fn input_key_pane(wp: *mut window_pane, key: key_code, m: *mut mouse_event) -> i32 {
unsafe {
if log_get_level() != 0 {
log_debug!(
"writing key 0x{:x} ({}) to %{}",
key,
_s(key_string_lookup_key(key, 1)),
(*wp).id
);
}
if KEYC_IS_MOUSE(key) {
if !m.is_null() && (*m).wp != -1 && (*m).wp as u32 == (*wp).id {
input_key_mouse(wp, m);
}
return 0;
}
input_key((*wp).screen, (*wp).event, key)
}
}
pub unsafe fn input_key_write(
from: *const u8,
bev: *mut bufferevent,
data: *const u8,
size: usize,
) {
unsafe {
log_debug!("input_key_write {0}: {2:1$}", _s(from), size, _s(data));
bufferevent_write(bev, data.cast(), size);
}
}
pub unsafe fn input_key_extended(bev: *mut bufferevent, mut key: key_code) -> i32 {
let __func__ = c!("input_key_extended");
unsafe {
let sizeof_tmp = 64;
let mut tmp = MaybeUninit::<[u8; 64]>::uninit();
let mut wc: wchar_t = 0;
const KEYC_SHIFT_OR_META: u64 = KEYC_SHIFT | KEYC_META;
const KEYC_SHIFT_OR_CTRL: u64 = KEYC_SHIFT | KEYC_CTRL;
const KEYC_META_OR_CTRL: u64 = KEYC_META | KEYC_CTRL;
const KEYC_SHIFT_OR_META_OR_CTRL: u64 = KEYC_SHIFT | KEYC_META | KEYC_CTRL;
let modifier = match key & KEYC_MASK_MODIFIERS {
KEYC_SHIFT => b'2',
KEYC_META => b'3',
KEYC_SHIFT_OR_META => b'4',
KEYC_CTRL => b'5',
KEYC_SHIFT_OR_CTRL => b'6',
KEYC_META_OR_CTRL => b'7',
KEYC_SHIFT_OR_META_OR_CTRL => b'8',
_ => return -1,
};
if KEYC_IS_UNICODE(key) {
let ud = utf8_to_data((key & KEYC_MASK_KEY) as u32);
if utf8_towc(&ud, &raw mut wc) == utf8_state::UTF8_DONE {
key = wc as u64;
} else {
return -1;
}
} else {
key &= KEYC_MASK_KEY;
}
if options_get_number_(GLOBAL_OPTIONS, "extended-keys-format") == 1 {
_ = xsnprintf_!(
tmp.as_mut_ptr().cast(),
sizeof_tmp,
"\x1b[27;{};{}~",
modifier as char,
key,
);
} else {
_ = xsnprintf_!(
tmp.as_mut_ptr().cast(),
sizeof_tmp,
"\x1b[{};{}u",
key,
modifier as char,
);
}
input_key_write(
__func__,
bev,
tmp.as_ptr().cast(),
strlen(tmp.as_ptr().cast()),
);
0
}
}
#[expect(
clippy::manual_c_str_literals,
reason = "false positive if c string contains NUL"
)]
static STANDARD_MAP: [SyncCharPtr; 2] = [
SyncCharPtr::from_ptr(c!("1!9(0)=+;:'\",<.>/-8? 2")),
SyncCharPtr::from_ptr(b"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0\0".as_ptr().cast()),
];
pub unsafe fn input_key_vt10x(bev: *mut bufferevent, mut key: key_code) -> i32 {
let __func__ = c!("input_key_vt10x");
unsafe {
let mut ud: utf8_data = zeroed();
log_debug!("{}: key in {}", _s(__func__), key);
if key & KEYC_META != 0 {
input_key_write(__func__, bev, c!("\x1b"), 1);
}
if KEYC_IS_UNICODE(key) {
ud = utf8_to_data(key as u32);
input_key_write(__func__, bev, ud.data.as_ptr().cast(), ud.size as usize);
return 0;
}
let onlykey: key_code = key & KEYC_MASK_KEY;
if onlykey == b'\r' as u64 || onlykey == b'\n' as u64 || onlykey == b'\t' as u64 {
key &= !KEYC_CTRL;
}
if key & KEYC_CTRL != 0 {
let p = libc::strchr(STANDARD_MAP[0].as_ptr(), onlykey as i32);
key = if !p.is_null() {
*STANDARD_MAP[1]
.as_ptr()
.add(p.addr() - STANDARD_MAP[0].as_ptr().addr()) as u64
} else if onlykey >= b'3' as u64 && onlykey <= b'7' as u64 {
onlykey - b'\x18' as u64
} else if onlykey >= b'@' as u64 && onlykey <= b'~' as u64 {
onlykey & 0x1f
} else {
return -1;
};
}
log_debug!("{}: key out {}", _s(__func__), key);
ud.data[0] = (key & 0x7f) as u8;
input_key_write(__func__, bev, ud.data.as_ptr().cast(), 1);
0
}
}
pub unsafe fn input_key_mode1(bev: *mut bufferevent, key: key_code) -> i32 {
unsafe {
log_debug!("{}: key in {}", "input_key_mode1", key);
let onlykey = key & KEYC_MASK_KEY;
if (key & KEYC_CTRL) != 0
&& (onlykey == ' ' as u64
|| onlykey == '/' as u64
|| onlykey == '@' as u64
|| onlykey == '^' as u64
|| (onlykey >= '2' as u64 && onlykey <= '8' as u64)
|| (onlykey >= '@' as u64 && onlykey <= '~' as u64))
{
return input_key_vt10x(bev, key);
}
if (key & (KEYC_CTRL | KEYC_META)) == KEYC_META {
return input_key_vt10x(bev, key);
}
}
-1
}
pub unsafe fn input_key(s: *mut screen, bev: *mut bufferevent, mut key: key_code) -> i32 {
let __func__ = c!("input_key");
unsafe {
let mut ike: *mut input_key_entry = null_mut();
let mut ud: utf8_data = zeroed();
if KEYC_IS_MOUSE(key) {
return 0;
}
if key & KEYC_LITERAL != 0 {
ud.data[0] = key as u8;
input_key_write(__func__, bev, ud.data.as_ptr().cast(), 1);
return 0;
}
if (key & KEYC_MASK_KEY) == keyc::KEYC_BSPACE as u64 {
let mut newkey = options_get_number_(GLOBAL_OPTIONS, "backspace") as key_code;
log_debug!(
"{}: key 0x{:x} is backspace -> 0x{:x}",
_s(__func__),
key,
newkey
);
if (key & KEYC_MASK_MODIFIERS) == 0 {
ud.data[0] = 255;
if (newkey & KEYC_MASK_MODIFIERS) == 0 {
ud.data[0] = newkey as u8;
} else if (newkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL {
newkey &= KEYC_MASK_KEY;
if newkey == '?' as u64 {
ud.data[0] = 0x7f;
} else if newkey >= '@' as u64 && newkey <= '_' as u64 {
ud.data[0] = (newkey - 0x40) as u8;
} else if newkey >= 'a' as u64 && newkey <= 'z' as u64 {
ud.data[0] = (newkey - 0x60) as u8;
}
}
if ud.data[0] != 255 {
input_key_write(__func__, bev, ud.data.as_ptr().cast(), 1);
}
return 0;
}
key = newkey | (key & (KEYC_MASK_FLAGS | KEYC_MASK_MODIFIERS));
}
if (key & KEYC_MASK_KEY) == keyc::KEYC_BTAB as u64 {
if (*s).mode.intersects(EXTENDED_KEY_MODES) {
key = '\x09' as u64 | (key & !KEYC_MASK_KEY) | KEYC_SHIFT;
} else {
key &= !KEYC_MASK_MODIFIERS;
}
}
if (key & !KEYC_MASK_KEY) == 0 {
if key == c0::C0_HT as u64
|| key == c0::C0_CR as u64
|| key == c0::C0_ESC as u64
|| (0x20..=0x7f).contains(&key)
{
ud.data[0] = key as u8;
input_key_write(__func__, bev, ud.data.as_ptr().cast(), 1);
return 0;
}
if KEYC_IS_UNICODE(key) {
ud = utf8_to_data(key as u32);
input_key_write(__func__, bev, ud.data.as_ptr().cast(), ud.size as usize);
return 0;
}
}
if !(*s).mode.intersects(mode_flag::MODE_KKEYPAD) {
key &= !KEYC_KEYPAD;
}
if !(*s).mode.intersects(mode_flag::MODE_KCURSOR) {
key &= !KEYC_CURSOR;
}
if ike.is_null() {
ike = input_key_get(key);
}
if ike.is_null() && (key & KEYC_META != 0) && (!key & KEYC_IMPLIED_META != 0) {
ike = input_key_get(key & !KEYC_META);
}
if ike.is_null() && (key & KEYC_CURSOR != 0) {
ike = input_key_get(key & !KEYC_CURSOR);
}
if ike.is_null() && (key & KEYC_KEYPAD != 0) {
ike = input_key_get(key & !KEYC_KEYPAD);
}
if !ike.is_null() {
log_debug!(
"{}: found key 0x{}: \"{}\"",
_s(__func__),
key,
_s((*ike).data)
);
if ((key & KEYC_MASK_KEY) == keyc::KEYC_PASTE_START as u64
|| (key & KEYC_MASK_KEY) == keyc::KEYC_PASTE_END as u64)
&& !(*s).mode.intersects(mode_flag::MODE_BRACKETPASTE)
{
return 0;
}
if (key & KEYC_META != 0) && (!key & KEYC_IMPLIED_META != 0) {
input_key_write(__func__, bev, c!("\x1b"), 1);
}
input_key_write(__func__, bev, (*ike).data, strlen((*ike).data));
return 0;
}
if (key >= KEYC_BASE && key < keyc::KEYC_BASE_END as u64)
|| (KEYC_USER..KEYC_USER_END).contains(&key)
{
log_debug!("{}: ignoring key 0x{}", _s(__func__), key);
return 0;
}
match (*s).mode & EXTENDED_KEY_MODES {
mode_flag::MODE_KEYS_EXTENDED_2 =>
{
input_key_extended(bev, key)
}
mode_flag::MODE_KEYS_EXTENDED => {
if input_key_mode1(bev, key) == -1 {
return input_key_extended(bev, key);
}
0
}
_ =>
{
input_key_vt10x(bev, key)
}
}
}
}
#[expect(static_mut_refs, reason = "FIXME")]
pub unsafe fn input_key_get_mouse(
s: *mut screen,
m: *mut mouse_event,
x: u32,
y: u32,
rbuf: *mut *const u8,
rlen: *mut usize,
) -> i32 {
use std::io::Write;
static mut BUF: [u8; 40] = [0; 40];
unsafe {
let sizeof_buf = 40;
*rbuf = null_mut();
*rlen = 0;
if MOUSE_DRAG((*m).b) && !(*s).mode.intersects(MOTION_MOUSE_MODES) {
return 0;
}
if !(*s).mode.intersects(ALL_MOUSE_MODES) {
return 0;
}
if (*m).sgr_type != b' ' as u32 {
if MOUSE_DRAG((*m).sgr_b)
&& MOUSE_RELEASE((*m).sgr_b)
&& !(*s).mode.intersects(mode_flag::MODE_MOUSE_ALL)
{
return 0;
}
} else if MOUSE_DRAG((*m).b)
&& MOUSE_RELEASE((*m).b)
&& MOUSE_RELEASE((*m).lb)
&& !(*s).mode.intersects(mode_flag::MODE_MOUSE_ALL)
{
return 0;
}
let mut len: usize;
if (*m).sgr_type != ' ' as u32 && (*s).mode.intersects(mode_flag::MODE_MOUSE_SGR) {
len = xsnprintf_!(
&raw mut BUF as *mut u8,
sizeof_buf,
"\x1b[<{};{};{}{}",
(*m).sgr_b,
x + 1,
y + 1,
(*m).sgr_type as u8 as char,
)
.unwrap() as usize;
} else if (*s).mode.intersects(mode_flag::MODE_MOUSE_UTF8) {
if (*m).b > (MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_BTN_OFF)
|| x > (MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF)
|| y > (MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF)
{
return 0;
}
len = BUF.as_mut_slice().write(b"\x1b[M").unwrap();
len += input_key_split2((*m).b + MOUSE_PARAM_BTN_OFF, &raw mut BUF[len] as _);
len += input_key_split2(x + MOUSE_PARAM_POS_OFF, &raw mut BUF[len] as _);
len += input_key_split2(y + MOUSE_PARAM_POS_OFF, &raw mut BUF[len] as _);
} else {
if (*m).b + MOUSE_PARAM_BTN_OFF > MOUSE_PARAM_MAX {
return 0;
}
len = BUF.as_mut_slice().write(b"\x1b[M").unwrap();
BUF[len] = ((*m).b + MOUSE_PARAM_BTN_OFF) as u8;
len += 1;
if x + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX {
BUF[len] = MOUSE_PARAM_MAX as u8;
len += 1;
} else {
BUF[len] = x as u8 + MOUSE_PARAM_POS_OFF as u8;
len += 1;
}
if y + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX {
BUF[len] = MOUSE_PARAM_MAX as u8;
len += 1;
} else {
BUF[len] = y as u8 + MOUSE_PARAM_POS_OFF as u8;
len += 1;
}
}
*rbuf = &raw const BUF as *const u8;
*rlen = len;
}
1
}
pub unsafe fn input_key_mouse(wp: *mut window_pane, m: *mut mouse_event) {
let __func__ = c!("input_key_mouse");
unsafe {
let s = (*wp).screen;
let mut x = 0;
let mut y = 0;
let mut buf = null();
let mut len: usize = 0;
if (*m).ignore != 0 || !(*s).mode.intersects(ALL_MOUSE_MODES) {
return;
}
if cmd_mouse_at(wp, m, &raw mut x, &raw mut y, 0) != 0 {
return;
}
if !window_pane_visible(wp) {
return;
}
if input_key_get_mouse(s, m, x, y, &raw mut buf, &raw mut len) == 0 {
return;
}
log_debug!("writing mouse {1:0$} to %{2}", len, _s(buf), (*wp).id);
input_key_write(__func__, (*wp).event, buf, len);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
use std::sync::Once;
static MOUSE_BUF_LOCK: Mutex<()> = Mutex::new(());
static BUILT: Once = Once::new();
fn ensure_built() {
BUILT.call_once(|| unsafe {
input_key_build();
});
}
unsafe fn lookup(key: key_code) -> Option<Vec<u8>> {
unsafe {
ensure_built();
let ike = input_key_get(key);
if ike.is_null() {
None
} else {
Some(CStr::from_ptr((*ike).data.cast()).to_bytes().to_vec())
}
}
}
#[test]
fn test_input_key_cmp() {
let a = input_key_entry::new(keyc::KEYC_F1 as u64, c"x");
let b = input_key_entry::new(keyc::KEYC_F2 as u64, c"y");
let a2 = input_key_entry::new(keyc::KEYC_F1 as u64, c"zzz");
assert_eq!(input_key_cmp(&a, &b), cmp::Ordering::Less);
assert_eq!(input_key_cmp(&b, &a), cmp::Ordering::Greater);
assert_eq!(input_key_cmp(&a, &a2), cmp::Ordering::Equal);
}
#[test]
fn test_input_key_split2() {
unsafe {
let mut buf = [0u8; 4];
assert_eq!(input_key_split2(b'A' as u32, buf.as_mut_ptr()), 1);
assert_eq!(buf[0], 0x41);
assert_eq!(input_key_split2(0x7f, buf.as_mut_ptr()), 1);
assert_eq!(buf[0], 0x7f);
assert_eq!(input_key_split2(0x80, buf.as_mut_ptr()), 2);
assert_eq!(buf[0], 0xc2);
assert_eq!(buf[1], 0x80);
assert_eq!(input_key_split2(0x100, buf.as_mut_ptr()), 2);
assert_eq!(buf[0], 0xc4);
assert_eq!(buf[1], 0x80);
}
}
#[test]
fn test_get_mouse_sgr_keeps_terminator() {
let _g = MOUSE_BUF_LOCK.lock().unwrap();
unsafe {
let mut s: screen = std::mem::zeroed();
s.mode = mode_flag::MODE_MOUSE_STANDARD | mode_flag::MODE_MOUSE_SGR;
let mut m: mouse_event = std::mem::zeroed();
m.sgr_type = b'M' as u32; m.sgr_b = 0; m.b = 0;
let mut buf: *const u8 = null();
let mut len: usize = 0;
let r = input_key_get_mouse(&mut s, &mut m, 4, 4, &raw mut buf, &raw mut len);
assert_eq!(r, 1);
let bytes = std::slice::from_raw_parts(buf, len);
assert_eq!(bytes, b"\x1b[<0;5;5M");
assert_eq!(*bytes.last().unwrap(), b'M', "SGR mouse terminator dropped");
m.sgr_type = b'm' as u32;
let r = input_key_get_mouse(&mut s, &mut m, 4, 4, &raw mut buf, &raw mut len);
assert_eq!(r, 1);
assert_eq!(std::slice::from_raw_parts(buf, len), b"\x1b[<0;5;5m");
}
}
#[test]
fn test_get_mouse_sgr_multidigit_and_button() {
let _g = MOUSE_BUF_LOCK.lock().unwrap();
unsafe {
let mut s: screen = std::mem::zeroed();
s.mode = mode_flag::MODE_MOUSE_STANDARD | mode_flag::MODE_MOUSE_SGR;
let mut m: mouse_event = std::mem::zeroed();
m.sgr_type = b'M' as u32;
m.sgr_b = 64; m.b = 64;
let mut buf: *const u8 = null();
let mut len: usize = 0;
let r = input_key_get_mouse(&mut s, &mut m, 209, 99, &raw mut buf, &raw mut len);
assert_eq!(r, 1);
let bytes = std::slice::from_raw_parts(buf, len);
assert_eq!(bytes, b"\x1b[<64;210;100M");
assert_eq!(len, bytes.len());
assert_eq!(*bytes.last().unwrap(), b'M');
}
}
#[test]
fn test_get_mouse_sgr_drag_keeps_terminator() {
let _g = MOUSE_BUF_LOCK.lock().unwrap();
unsafe {
let mut s: screen = std::mem::zeroed();
s.mode = mode_flag::MODE_MOUSE_ALL | mode_flag::MODE_MOUSE_SGR;
let mut m: mouse_event = std::mem::zeroed();
m.sgr_type = b'M' as u32;
m.sgr_b = 32;
m.b = 32;
let mut buf: *const u8 = null();
let mut len: usize = 0;
let r = input_key_get_mouse(&mut s, &mut m, 9, 9, &raw mut buf, &raw mut len);
assert_eq!(r, 1);
let bytes = std::slice::from_raw_parts(buf, len);
assert_eq!(bytes, b"\x1b[<32;10;10M");
assert_eq!(*bytes.last().unwrap(), b'M', "SGR drag terminator dropped");
}
}
#[test]
fn test_lookup_function_and_nav_keys() {
unsafe {
assert_eq!(lookup(keyc::KEYC_F1 as u64).as_deref(), Some(&b"\x1bOP"[..]));
assert_eq!(lookup(keyc::KEYC_F4 as u64).as_deref(), Some(&b"\x1bOS"[..]));
assert_eq!(lookup(keyc::KEYC_F5 as u64).as_deref(), Some(&b"\x1b[15~"[..]));
assert_eq!(lookup(keyc::KEYC_F12 as u64).as_deref(), Some(&b"\x1b[24~"[..]));
assert_eq!(lookup(keyc::KEYC_IC as u64).as_deref(), Some(&b"\x1b[2~"[..]));
assert_eq!(lookup(keyc::KEYC_DC as u64).as_deref(), Some(&b"\x1b[3~"[..]));
assert_eq!(lookup(keyc::KEYC_HOME as u64).as_deref(), Some(&b"\x1b[1~"[..]));
assert_eq!(lookup(keyc::KEYC_END as u64).as_deref(), Some(&b"\x1b[4~"[..]));
assert_eq!(lookup(keyc::KEYC_NPAGE as u64).as_deref(), Some(&b"\x1b[6~"[..]));
assert_eq!(lookup(keyc::KEYC_PPAGE as u64).as_deref(), Some(&b"\x1b[5~"[..]));
assert_eq!(lookup(keyc::KEYC_BTAB as u64).as_deref(), Some(&b"\x1b[Z"[..]));
}
}
#[test]
fn test_lookup_arrow_keys_cursor_mode() {
unsafe {
assert_eq!(lookup(keyc::KEYC_UP as u64 | KEYC_CURSOR).as_deref(), Some(&b"\x1bOA"[..]));
assert_eq!(lookup(keyc::KEYC_DOWN as u64 | KEYC_CURSOR).as_deref(), Some(&b"\x1bOB"[..]));
assert_eq!(lookup(keyc::KEYC_RIGHT as u64 | KEYC_CURSOR).as_deref(), Some(&b"\x1bOC"[..]));
assert_eq!(lookup(keyc::KEYC_LEFT as u64 | KEYC_CURSOR).as_deref(), Some(&b"\x1bOD"[..]));
assert_eq!(lookup(keyc::KEYC_UP as u64).as_deref(), Some(&b"\x1b[A"[..]));
assert_eq!(lookup(keyc::KEYC_DOWN as u64).as_deref(), Some(&b"\x1b[B"[..]));
assert_eq!(lookup(keyc::KEYC_RIGHT as u64).as_deref(), Some(&b"\x1b[C"[..]));
assert_eq!(lookup(keyc::KEYC_LEFT as u64).as_deref(), Some(&b"\x1b[D"[..]));
}
}
#[test]
fn test_lookup_keypad_keys() {
unsafe {
assert_eq!(lookup(keyc::KEYC_KP_SLASH as u64 | KEYC_KEYPAD).as_deref(), Some(&b"\x1bOo"[..]));
assert_eq!(lookup(keyc::KEYC_KP_STAR as u64 | KEYC_KEYPAD).as_deref(), Some(&b"\x1bOj"[..]));
assert_eq!(lookup(keyc::KEYC_KP_ENTER as u64 | KEYC_KEYPAD).as_deref(), Some(&b"\x1bOM"[..]));
assert_eq!(lookup(keyc::KEYC_KP_ZERO as u64 | KEYC_KEYPAD).as_deref(), Some(&b"\x1bOp"[..]));
assert_eq!(lookup(keyc::KEYC_KP_PERIOD as u64 | KEYC_KEYPAD).as_deref(), Some(&b"\x1bOn"[..]));
assert_eq!(lookup(keyc::KEYC_KP_SLASH as u64).as_deref(), Some(&b"/"[..]));
assert_eq!(lookup(keyc::KEYC_KP_STAR as u64).as_deref(), Some(&b"*"[..]));
assert_eq!(lookup(keyc::KEYC_KP_SEVEN as u64).as_deref(), Some(&b"7"[..]));
assert_eq!(lookup(keyc::KEYC_KP_ENTER as u64).as_deref(), Some(&b"\n"[..]));
assert_eq!(lookup(keyc::KEYC_KP_ZERO as u64).as_deref(), Some(&b"0"[..]));
assert_eq!(lookup(keyc::KEYC_KP_PERIOD as u64).as_deref(), Some(&b"."[..]));
}
}
#[test]
fn test_lookup_paste_keys() {
unsafe {
assert_eq!(lookup(keyc::KEYC_PASTE_START as u64).as_deref(), Some(&b"\x1b[200~"[..]));
assert_eq!(lookup(keyc::KEYC_PASTE_END as u64).as_deref(), Some(&b"\x1b[201~"[..]));
assert_eq!(
lookup(keyc::KEYC_PASTE_START as u64 | KEYC_IMPLIED_META).as_deref(),
Some(&b"\x1b[200~"[..])
);
assert_eq!(
lookup(keyc::KEYC_PASTE_END as u64 | KEYC_IMPLIED_META).as_deref(),
Some(&b"\x1b[201~"[..])
);
}
}
#[test]
fn test_lookup_modifier_folding() {
unsafe {
assert_eq!(
lookup(keyc::KEYC_UP as u64 | KEYC_SHIFT).as_deref(),
Some(&b"\x1b[1;2A"[..])
);
assert_eq!(
lookup(keyc::KEYC_HOME as u64 | KEYC_META | KEYC_IMPLIED_META).as_deref(),
Some(&b"\x1b[1;3H"[..])
);
assert_eq!(
lookup(keyc::KEYC_F1 as u64 | KEYC_CTRL).as_deref(),
Some(&b"\x1b[1;5P"[..])
);
assert_eq!(
lookup(keyc::KEYC_F12 as u64 | KEYC_CTRL).as_deref(),
Some(&b"\x1b[24;5~"[..])
);
assert_eq!(
lookup(
keyc::KEYC_UP as u64
| KEYC_SHIFT
| KEYC_META
| KEYC_IMPLIED_META
| KEYC_CTRL
)
.as_deref(),
Some(&b"\x1b[1;8A"[..])
);
assert_eq!(
lookup(keyc::KEYC_END as u64 | KEYC_CTRL).as_deref(),
Some(&b"\x1b[1;5F"[..])
);
}
}
#[test]
fn test_lookup_missing_returns_none() {
unsafe {
assert_eq!(lookup(b'a' as key_code), None);
assert_eq!(lookup(keyc::KEYC_UP as u64 | KEYC_KEYPAD), None);
assert_eq!(lookup(keyc::KEYC_F1 as u64 | KEYC_BUILD_MODIFIERS), None);
}
}
#[test]
fn test_modifier_folding_all_digits() {
unsafe {
let up = keyc::KEYC_UP as u64;
let meta = KEYC_META | KEYC_IMPLIED_META;
assert_eq!(lookup(up | KEYC_SHIFT).as_deref(), Some(&b"\x1b[1;2A"[..]));
assert_eq!(lookup(up | meta).as_deref(), Some(&b"\x1b[1;3A"[..]));
assert_eq!(lookup(up | KEYC_SHIFT | meta).as_deref(), Some(&b"\x1b[1;4A"[..]));
assert_eq!(lookup(up | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5A"[..]));
assert_eq!(lookup(up | KEYC_SHIFT | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;6A"[..]));
assert_eq!(lookup(up | meta | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;7A"[..]));
assert_eq!(
lookup(up | KEYC_SHIFT | meta | KEYC_CTRL).as_deref(),
Some(&b"\x1b[1;8A"[..])
);
}
}
#[test]
fn test_modifier_folding_arrow_final_bytes() {
unsafe {
assert_eq!(lookup(keyc::KEYC_UP as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5A"[..]));
assert_eq!(lookup(keyc::KEYC_DOWN as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5B"[..]));
assert_eq!(lookup(keyc::KEYC_RIGHT as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5C"[..]));
assert_eq!(lookup(keyc::KEYC_LEFT as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5D"[..]));
}
}
#[test]
fn test_modifier_folding_edit_and_nav_keys() {
unsafe {
let meta = KEYC_META | KEYC_IMPLIED_META;
assert_eq!(lookup(keyc::KEYC_IC as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[2;5~"[..]));
assert_eq!(lookup(keyc::KEYC_DC as u64 | KEYC_SHIFT).as_deref(), Some(&b"\x1b[3;2~"[..]));
assert_eq!(lookup(keyc::KEYC_PPAGE as u64 | meta).as_deref(), Some(&b"\x1b[5;3~"[..]));
assert_eq!(lookup(keyc::KEYC_NPAGE as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[6;5~"[..]));
assert_eq!(
lookup(keyc::KEYC_HOME as u64 | KEYC_SHIFT | meta).as_deref(),
Some(&b"\x1b[1;4H"[..])
);
assert_eq!(lookup(keyc::KEYC_END as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5F"[..]));
}
}
#[test]
fn test_modifier_folding_ss3_function_keys() {
unsafe {
assert_eq!(lookup(keyc::KEYC_F1 as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5P"[..]));
assert_eq!(lookup(keyc::KEYC_F2 as u64 | KEYC_SHIFT).as_deref(), Some(&b"\x1b[1;2Q"[..]));
assert_eq!(lookup(keyc::KEYC_F3 as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5R"[..]));
assert_eq!(lookup(keyc::KEYC_F4 as u64 | KEYC_CTRL).as_deref(), Some(&b"\x1b[1;5S"[..]));
}
}
#[test]
fn test_input_key_split2_more_boundaries() {
unsafe {
let mut buf = [0u8; 4];
assert_eq!(input_key_split2(0, buf.as_mut_ptr()), 1);
assert_eq!(buf[0], 0);
assert_eq!(input_key_split2(0x7ff, buf.as_mut_ptr()), 2);
assert_eq!(buf[0], 0xdf);
assert_eq!(buf[1], 0xbf);
assert_eq!(input_key_split2(0xa0, buf.as_mut_ptr()), 2);
assert_eq!(buf[0], 0xc2);
assert_eq!(buf[1], 0xa0);
}
}
#[test]
fn test_input_key_cmp_full_width() {
let lo = input_key_entry::new(1u64, c"a");
let hi = input_key_entry::new(0x0100_0000_0000_0001u64, c"b");
assert_eq!(input_key_cmp(&lo, &hi), cmp::Ordering::Less);
assert_eq!(input_key_cmp(&hi, &lo), cmp::Ordering::Greater);
}
}