term.v

2.7 KB · raw

1//! Terminal control and key decoding for the history view.2//!3//! The view owns the screen while it runs, so raw mode and the alternate screen4//! have to be entered and left reliably, on every exit path. Rendering is a5//! plain string of ANSI escapes written in one call per frame — no redraw6//! diffing, because the whole frame is small and the terminal does that faster7//! than we can.89module view1011#flag -I@VMODROOT/vendor/shim12#flag @VMODROOT/vendor/shim/koh_term.c13#include "koh_term.h"1415fn C.koh_term_raw(enable int) int1617fn C.koh_term_size(rows &int, cols &int) int1819fn C.koh_read_key(buf &char, cap int) int2021pub enum KeyKind {22	rune23	up24	down25	left26	right27	enter28	escape29	tab30	backspace31	delete32	home33	end34	page_up35	page_down36	interrupt37	eof38	unknown39}4041pub struct Key {42pub:43	kind KeyKind44	text string45}4647pub fn terminal_size() (int, int) {48	mut rows := 049	mut cols := 050	C.koh_term_size(&rows, &cols)51	return rows, cols52}5354pub fn raw_mode(enable bool) {55	if !enable {56		C.koh_term_raw(0)57		return58	}59	C.koh_term_raw(1)60}6162// read_key blocks for one keystroke. `eof` means input ended (stdin closed or63// Ctrl-D), which the view treats as quit.64pub fn read_key() Key {65	mut buf := []u8{len: 16}66	n := C.koh_read_key(unsafe { &char(buf.data) }, buf.len)67	if n <= 0 {68		return Key{69			kind: .eof70		}71	}72	return decode(buf[..n])73}7475fn decode(bytes []u8) Key {76	if bytes.len == 1 {77		b := bytes[0]78		match b {79			0x03 {80				return Key{81					kind: .interrupt82				}83			}84			0x04 {85				return Key{86					kind: .eof87				}88			}89			0x09 {90				return Key{91					kind: .tab92				}93			}94			0x0d, 0x0a {95				return Key{96					kind: .enter97				}98			}99			0x7f, 0x08 {100				return Key{101					kind: .backspace102				}103			}104			0x1b {105				return Key{106					kind: .escape107				}108			}109			else {110				if b >= 0x20 {111					return Key{112						kind: .rune113						text: bytes.bytestr()114					}115				}116				return Key{117					kind: .unknown118				}119			}120		}121	}122	if bytes[0] == 0x1b {123		sequence := bytes[1..].bytestr()124		return match sequence {125			'[A', 'OA' {126				Key{127					kind: .up128				}129			}130			'[B', 'OB' {131				Key{132					kind: .down133				}134			}135			'[C', 'OC' {136				Key{137					kind: .right138				}139			}140			'[D', 'OD' {141				Key{142					kind: .left143				}144			}145			'[H', 'OH', '[1~' {146				Key{147					kind: .home148				}149			}150			'[F', 'OF', '[4~' {151				Key{152					kind: .end153				}154			}155			'[3~' {156				Key{157					kind: .delete158				}159			}160			'[5~' {161				Key{162					kind: .page_up163				}164			}165			'[6~' {166				Key{167					kind: .page_down168				}169			}170			else {171				Key{172					kind: .escape173				}174			}175		}176	}177	// Anything else is typed text: a pasted run, or a multi-byte character the178	// shim gathered in one read.179	return Key{180		kind: .rune181		text: bytes.bytestr()182	}183}