├── .gitignore ├── amulet ├── amulet.rc ├── trie.rs ├── terminal.rs ├── canvas.rs ├── termios.rs ├── ll.rs └── c.rs ├── README.md ├── demos ├── howto-01-hello-world.rs ├── blessings-03-before-and-after.rs ├── howto-04-scanw.rs ├── howto-06-chgat.rs ├── howto-03-printw.rs ├── howto-02-initialization.rs ├── howto-05-attributes.rs └── howto-07-window-borders.rs └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | *.so 3 | 4 | # Dummy file for make 5 | amulet/.built 6 | 7 | # Ignore the binaries in `demos` 8 | demos/* 9 | !demos/*.rs 10 | -------------------------------------------------------------------------------- /amulet/amulet.rc: -------------------------------------------------------------------------------- 1 | #![link(name = "amulet", vers = "0.0", author = "eevee")] 2 | #![comment = "Rust ncurses and TUI library"] 3 | #![crate_type = "lib"] 4 | // TODO? :S 5 | #![feature(unsafe_destructor)] 6 | 7 | extern crate libc; 8 | 9 | pub use terminal::Terminal; 10 | 11 | pub mod c; 12 | pub mod canvas; 13 | pub mod ll; 14 | pub mod termios; 15 | pub mod terminal; 16 | mod trie; 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # amulet 2 | 3 | `amulet` is a Rust library for creating terminal UIs. 4 | 5 | Or, it will be. The intention is to create a spiritual port of [Urwid](http://excess.org/urwid/), a Python TUI library. 6 | 7 | ## Building 8 | 9 | `amulet` depends on: 10 | 11 | * The [Rust](http://www.rust-lang.org/) compiler (0.3 only) 12 | * `libncurses` 13 | 14 | And then: 15 | 16 | make 17 | -------------------------------------------------------------------------------- /demos/howto-01-hello-world.rs: -------------------------------------------------------------------------------- 1 | /** Hello, world! 2 | * 3 | * Taken from the ncurses programming howto, section 2.1: 4 | * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/helloworld.html 5 | */ 6 | 7 | extern crate amulet; 8 | 9 | fn main() { 10 | let mut term = amulet::Terminal::new(); 11 | let mut canvas = term.enter_fullscreen(); 12 | canvas.write("Hello World !!!"); 13 | canvas.repaint(); 14 | canvas.pause(); 15 | } 16 | -------------------------------------------------------------------------------- /demos/blessings-03-before-and-after.rs: -------------------------------------------------------------------------------- 1 | /** "Before and after". (This is after.) 2 | * 3 | * Taken from the blessings README: 4 | * https://github.com/erikrose/blessings/blob/master/README.rst 5 | */ 6 | 7 | extern crate amulet; 8 | 9 | fn main() { 10 | let term = amulet::Terminal::new(); 11 | 12 | term.at(0, term.height() - 1, |&:| { 13 | term.write("This is "); 14 | term.attrwrite("pretty!", amulet::ll::Style().underline()); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RUSTC := rustc 2 | 3 | .PHONY: all 4 | all: libamulet.rlib demos 5 | 6 | libamulet.rlib: amulet/amulet.rc $(wildcard amulet/*.rs) 7 | $(RUSTC) amulet/amulet.rc \ 8 | && touch amulet/.built 9 | 10 | 11 | DEMO_SOURCES := $(sort $(wildcard demos/*.rs)) 12 | DEMO_TARGETS := $(DEMO_SOURCES:.rs=) 13 | 14 | .PHONY: demos 15 | demos: $(DEMO_TARGETS) 16 | 17 | demos/%: demos/%.rs libamulet.rlib 18 | $(RUSTC) -L . --out-dir demos $@.rs 19 | 20 | 21 | .PHONY: clean 22 | clean: 23 | rm -f amulet/libamulet*.so 24 | rm -f amulet/.built 25 | rm -f $(DEMO_TARGETS) 26 | -------------------------------------------------------------------------------- /demos/howto-04-scanw.rs: -------------------------------------------------------------------------------- 1 | /** A simple scanw example 2 | * 3 | * Taken from the ncurses programming howto, section 7.4: 4 | * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/scanw.html 5 | */ 6 | 7 | extern crate amulet; 8 | 9 | fn main() { 10 | let mesg = "Enter a string: "; 11 | 12 | let mut term = amulet::Terminal::new(); 13 | let mut canvas = term.enter_fullscreen(); 14 | let (rows, cols) = canvas.size(); 15 | 16 | canvas.reposition(rows / 2, (cols - mesg.len()) / 2); 17 | canvas.write(mesg); 18 | canvas.repaint(); 19 | 20 | let buf = canvas.read_line(); 21 | 22 | canvas.reposition(rows - 2, 0); 23 | canvas.write(format!("You entered: {}", buf).as_slice()); 24 | canvas.repaint(); 25 | 26 | canvas.pause(); 27 | } 28 | -------------------------------------------------------------------------------- /demos/howto-06-chgat.rs: -------------------------------------------------------------------------------- 1 | /** chgat() usage example 2 | * 3 | * Taken from the ncurses programming howto, section 8.6: 4 | * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html 5 | */ 6 | 7 | extern crate amulet; 8 | 9 | use amulet::ll::Style; 10 | 11 | fn main() { 12 | let mut term = amulet::Terminal::new(); 13 | let mut canvas = term.enter_fullscreen(); 14 | 15 | canvas.write("A big string which I didn't care to type fully"); 16 | 17 | canvas.reposition(0, 0); 18 | // TODO the original curses function also takes an argument for how many 19 | // characters to change, with -1 meaning "to end of line" (which i am not 20 | // in love with) 21 | canvas.restyle(Style().fg(13)); 22 | 23 | canvas.repaint(); 24 | canvas.pause(); 25 | } 26 | -------------------------------------------------------------------------------- /demos/howto-03-printw.rs: -------------------------------------------------------------------------------- 1 | /** A simple printw example 2 | * 3 | * Taken from the ncurses programming howto, section 6.3: 4 | * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/printw.html 5 | */ 6 | 7 | extern crate amulet; 8 | 9 | fn main() { 10 | let mesg = "Just a string"; 11 | 12 | let mut term = amulet::Terminal::new(); 13 | let mut canvas = term.enter_fullscreen(); 14 | let (rows, cols) = canvas.size(); 15 | 16 | // TODO seems like centering should be easier? 17 | //mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg); 18 | //mvprintw(row-2,0,"This screen has %d rows and %d columns\n",row,col); 19 | 20 | // TODO do i want the combined mvprint et al.? 21 | canvas.reposition(rows / 2, (cols - mesg.len())/2); 22 | canvas.write(mesg); 23 | canvas.reposition(rows - 2, 0); 24 | canvas.write(format!("This screen has {} rows and {} columns\n", rows, cols).as_slice()); 25 | 26 | canvas.write("Try resizing your window (if possible) and then run this program again"); 27 | canvas.repaint(); 28 | canvas.pause(); 29 | } 30 | -------------------------------------------------------------------------------- /demos/howto-02-initialization.rs: -------------------------------------------------------------------------------- 1 | /** Initialization function usage 2 | * 3 | * Taken from the ncurses programming howto, section 4.7: 4 | * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/init.html 5 | */ 6 | 7 | extern crate amulet; 8 | 9 | fn main() { 10 | let bold = amulet::ll::Style().bold(); 11 | 12 | let mut term = amulet::Terminal::new(); 13 | let mut canvas = term.enter_fullscreen(); 14 | // TODO implement me -- right now there's NEVER echo, so 15 | // TODO also i don't like this curses-style api; maybe a "set_options" 16 | // or something 17 | // term.noecho(); 18 | 19 | canvas.write("Type any character to see it in bold\n"); 20 | canvas.repaint(); 21 | 22 | let ch = canvas.read_key(); 23 | match ch { 24 | amulet::ll::Key::FunctionKey(n) if n == 1 => { 25 | canvas.write("F1 key pressed"); 26 | } 27 | _ => { 28 | canvas.write("The pressed key is "); 29 | canvas.attrwrite(format!("{:?}", ch).as_slice(), bold); 30 | } 31 | } 32 | 33 | canvas.repaint(); 34 | canvas.pause(); 35 | } 36 | -------------------------------------------------------------------------------- /demos/howto-05-attributes.rs: -------------------------------------------------------------------------------- 1 | /** A simple attributes example 2 | * 3 | * Taken from the ncurses programming howto, section 8: 4 | * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html 5 | */ 6 | 7 | extern crate amulet; 8 | extern crate libc; 9 | 10 | use std::io; 11 | use std::os; 12 | use std::path::Path; 13 | use std::result; 14 | 15 | /* pager functionality by Joseph Spainhou */ 16 | 17 | #[fixed_stack_segment] 18 | fn main() { 19 | let mut prev = '0' as u8; 20 | 21 | let args = os::args(); 22 | if args.len() != 2 { 23 | io::println(format!("Usage: {} ", args[0])); 24 | unsafe { libc::exit(1) }; 25 | } 26 | 27 | let fh; 28 | match io::file_reader(&Path(args[1])) { 29 | result::Ok(res) => { fh = res; } 30 | result::Err(msg) => { 31 | io::println(msg); 32 | unsafe { libc::exit(1) }; 33 | } 34 | } 35 | 36 | // cannot open input file... 37 | 38 | let mut term = amulet::Terminal::new(); 39 | let mut canvas = term.enter_fullscreen(); 40 | let mut ch : u8; 41 | let (rows, _cols) = canvas.size(); 42 | 43 | let plain = amulet::ll::Style(); 44 | let bold = plain.bold(); 45 | let mut cur_style = &plain; 46 | 47 | loop { 48 | ch = fh.read_byte() as u8; 49 | if fh.eof() { 50 | break; 51 | } 52 | 53 | let (row, col) = canvas.position(); 54 | 55 | if row == rows - 1 { 56 | canvas.write("<-Press Any Key->"); 57 | canvas.repaint(); 58 | canvas.pause(); 59 | canvas.clear(); 60 | canvas.move(0, 0); 61 | } 62 | 63 | if prev as char == '/' && ch as char == '*' { 64 | cur_style = &bold; 65 | 66 | canvas.move(row, col - 1); 67 | canvas.attrwrite(format!("{}{}", prev as char, ch as char), *cur_style); 68 | } 69 | else { 70 | canvas.attrwrite(format!("{}", ch as char), *cur_style); 71 | } 72 | 73 | canvas.repaint(); 74 | 75 | if prev as char == '*' && ch as char == '/' { 76 | cur_style = &plain; 77 | } 78 | 79 | prev = ch; 80 | } 81 | 82 | canvas.pause(); 83 | } 84 | -------------------------------------------------------------------------------- /amulet/trie.rs: -------------------------------------------------------------------------------- 1 | /** Trie implementation. 2 | * 3 | * Useful for figuring out when an entire terminal escape sequence has been 4 | * received, and what it is. 5 | */ 6 | 7 | // TODO way too much @ and copying in here but i get mut/ali errors otherwise. 8 | // :( revisit with 0.5 perhaps 9 | 10 | use std::cmp::Eq; 11 | use std::hash::Hash; 12 | use std::collections::HashMap; 13 | use std::collections::hash_map::{Hasher,Entry}; 14 | use std::vec; 15 | use std::io; 16 | use std::fmt::Show; 17 | 18 | pub struct Trie { 19 | value: Option, 20 | children: HashMap>, 21 | } 22 | 23 | /** Construct an empty trie. */ 24 | pub fn Trie + Clone + Show + 'static, U: Clone + Show + 'static>() -> Trie { 25 | return Trie::new(); 26 | } 27 | 28 | impl + Clone + Show + 'static, U: Clone + Show + 'static> Trie { 29 | pub fn new() -> Trie { 30 | return Trie{ value: None::, children: HashMap::new() }; 31 | } 32 | 33 | pub fn insert(&mut self, keys: &[T], value: U) { 34 | if keys.is_empty() { 35 | self.value = Some(value); 36 | return; 37 | } 38 | 39 | let key = keys[0].clone(); 40 | let child_node = match self.children.entry(key) { 41 | Entry::Occupied(child_node) => child_node.into_mut(), 42 | Entry::Vacant(child_node) => child_node.insert(Trie::new()), 43 | }; 44 | child_node.insert(&keys[1..], value); 45 | } 46 | 47 | fn find(&self, keys: &[T]) -> Option { 48 | if keys.is_empty() { 49 | panic!("Trie cannot have an empty key"); 50 | } 51 | 52 | let mut node = self; 53 | for k in range(0, keys.len()) { 54 | match node.children.get(&keys[k]) { 55 | Some(child_node) => node = child_node, 56 | None => return None, 57 | } 58 | } 59 | 60 | return node.value.clone(); 61 | } 62 | 63 | pub fn find_prefix(&self, keys: &[T]) -> (Option, Vec) { 64 | let mut node = self; 65 | for k in range(0, keys.len()) { 66 | match node.children.get(&keys[k]) { 67 | Some(child_node) => node = child_node, 68 | None => return (node.value.clone(), keys.slice(k, keys.len()).to_vec()), 69 | } 70 | } 71 | 72 | return (node.value.clone(), vec![]); 73 | } 74 | 75 | fn _print_all(&self) { 76 | self._print_all_impl(&mut vec![]); 77 | } 78 | fn _print_all_impl(&self, prefix: &mut Vec) { 79 | for value in self.value.iter() { 80 | io::stderr().write_line(format!("{:?} => {:?}", prefix, value).as_slice()); 81 | } 82 | 83 | for (key, node) in self.children.iter() { 84 | prefix.push(key.clone()); 85 | node._print_all_impl(prefix); 86 | prefix.pop(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /demos/howto-07-window-borders.rs: -------------------------------------------------------------------------------- 1 | /** Window border example 2 | * 3 | * Taken from the ncurses programming howto, section 9.1: 4 | * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/windows.html 5 | */ 6 | 7 | extern crate amulet; 8 | 9 | use amulet::canvas::Canvas; 10 | 11 | fn main() { 12 | let mut term = amulet::Terminal::new(); 13 | let mut canvas = term.enter_fullscreen(); 14 | 15 | let height = 3; 16 | let width = 10; 17 | // TODO this used to be (term.height(), term.width()), but that was disallowed for "immutable 18 | // borrow" when the canvas itself is already a mutable borrow. this seems... all suboptimal. 19 | let (rows, columns) = canvas.size(); 20 | // Calculation for a center placement of the window 21 | let mut starty = (rows - height) / 2; 22 | let mut startx = (columns - width) / 2; 23 | 24 | canvas.write("Press F1 to exit"); 25 | canvas.repaint(); 26 | 27 | let mut my_win = create_newwin(&canvas, height, width, starty, startx); 28 | 29 | loop { 30 | match canvas.read_key() { 31 | amulet::ll::Key::FunctionKey(1) => { 32 | break; 33 | } 34 | amulet::ll::Key::SpecialKey(amulet::ll::SpecialKeyCode::LEFT) => { 35 | destroy_win(&mut my_win); 36 | startx -= 1; 37 | my_win = create_newwin(&canvas, height, width, starty, startx); 38 | } 39 | amulet::ll::Key::SpecialKey(amulet::ll::SpecialKeyCode::RIGHT) => { 40 | destroy_win(&mut my_win); 41 | startx += 1; 42 | my_win = create_newwin(&canvas, height, width, starty, startx); 43 | } 44 | amulet::ll::Key::SpecialKey(amulet::ll::SpecialKeyCode::UP) => { 45 | destroy_win(&mut my_win); 46 | starty -= 1; 47 | my_win = create_newwin(&canvas, height, width, starty, startx); 48 | } 49 | amulet::ll::Key::SpecialKey(amulet::ll::SpecialKeyCode::DOWN) => { 50 | destroy_win(&mut my_win); 51 | starty += 1; 52 | my_win = create_newwin(&canvas, height, width, starty, startx); 53 | } 54 | _ => (), 55 | } 56 | } 57 | } 58 | 59 | fn create_newwin<'a, 'b>(canvas: &Canvas<'a, 'b>, height: usize, width: usize, starty: usize, startx: usize) -> Canvas<'a, 'b> { 60 | let mut local_win = canvas.spawn(startx, starty, width, height); 61 | // 0,0 gives default chars for the vertical and horizontal lines 62 | //local_win.set_box(0 as char, 0 as char); 63 | // TODO: box borders don't belong on Canvas since they are more a UI thing. 64 | // probably a really good first widget though. 65 | for _n in range(0, width) { 66 | local_win.write("box...\n"); 67 | } 68 | 69 | // Show that box 70 | local_win.repaint(); 71 | 72 | return local_win; 73 | } 74 | 75 | fn destroy_win(local_win: &mut Canvas) { 76 | // 'box' won't erase the window; it'll leave the corners behind. 77 | 78 | // border's params are L, R, T, B, TL, TR, BL, BR 79 | //local_win.set_border(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); 80 | 81 | local_win.repaint(); 82 | } 83 | -------------------------------------------------------------------------------- /amulet/terminal.rs: -------------------------------------------------------------------------------- 1 | use canvas::Canvas; 2 | use ll::Style; 3 | use ll::TerminalInfo; 4 | use termios; 5 | 6 | pub struct Terminal<'a> { 7 | info: TerminalInfo<'a>, 8 | } 9 | 10 | impl<'a> Terminal<'a> { 11 | pub fn new() -> Terminal<'a> { 12 | let info = TerminalInfo::new(); 13 | 14 | return Terminal{ 15 | info: info, 16 | }; 17 | } 18 | 19 | 20 | // ------------------------------------------------------------------------ 21 | // Inspection 22 | #[inline] 23 | pub fn height(&self) -> usize { 24 | return self.info.height(); 25 | } 26 | 27 | #[inline] 28 | pub fn width(&self) -> usize { 29 | return self.info.width(); 30 | } 31 | 32 | 33 | pub fn at ()>(&self, x: usize, y: usize, cb: F) { 34 | self.info.write_cap("sc"); // save cursor 35 | // TODO check for existence of cup 36 | self.info.write_cap2("cup", y as isize, x as isize); 37 | 38 | cb(); 39 | 40 | self.info.write_cap("rc"); // restore cursor 41 | } 42 | 43 | // Output 44 | 45 | #[inline] 46 | pub fn write(&self, s: &str) { 47 | self.info.write(s); 48 | } 49 | 50 | pub fn attrwrite(&self, s: &str, style: Style) { 51 | // TODO try to cut down on the amount of back-and-forth between c 52 | // strings and rust strings all up in here 53 | if style.is_underline { 54 | self.info.write_cap("smul"); 55 | } 56 | 57 | // TODO this may need some escaping or whatever -- or maybe that 58 | // belongs in write() 59 | self.write(s); 60 | 61 | // Clean up after ourselves: reset style to default 62 | // TODO this is ripe for some optimizing 63 | self.info.write_cap("sgr0"); 64 | } 65 | 66 | // Full-screen 67 | 68 | pub fn fullscreen_canvas(&'a self, cb: &fn(&mut Canvas)) { 69 | // Enter fullscreen 70 | let _tidy_cup = self.info.write_tidy_cap("smcup", "rmcup"); 71 | 72 | // Enable keypad mode 73 | let _tidy_kx = self.info.write_tidy_cap("smkx", "rmkx"); 74 | 75 | // And clear the screen first 76 | self.info.write_cap("clear"); 77 | 78 | // TODO intrflush, or is that a curses thing? 79 | 80 | // TODO so, we need to switch to raw mode *some*where. is this an 81 | // appropriate place? i assume if you have a fullscreen app then you 82 | // want to get keypresses. 83 | // TODO seems weird to create a second one of these. stick a 84 | // .checkpoint() on the one attached to the terminal? 85 | let mut tidy_termstate = termios::TidyTerminalState(self.info.in_fd); 86 | tidy_termstate.cbreak(); 87 | 88 | let mut canv = Canvas(&self.info, 0, 0, self.height(), self.width()); 89 | cb(&mut canv); 90 | } 91 | 92 | // Enter fullscreen manually. Cleaning up with exit_fullscreen is YOUR 93 | // responsibility! If you don't do it in a drop, you risk leaving the 94 | // terminal in a fucked-up state on early exit! 95 | pub fn enter_fullscreen(&'a mut self) -> Canvas { 96 | // Same stuff as above. Enter fullscreen; enter keypad mode; clear the 97 | // screen. 98 | let tidy_cup = self.info.write_tidy_cap("smcup", "rmcup"); 99 | let tidy_kx = self.info.write_tidy_cap("smkx", "rmkx"); 100 | self.info.write_cap("clear"); 101 | 102 | // TODO intrflush, as above...? 103 | 104 | let mut tidy_termstate = termios::TidyTerminalState(self.info.in_fd); 105 | tidy_termstate.cbreak(); 106 | 107 | let mut canv = Canvas(&self.info, 0, 0, self.height(), self.width()); 108 | // TODO since this isn't really a "scope" guard any more, maybe this should just push some 109 | // closures to run when the canvas goes away 110 | canv.guards.push(Box::new(tidy_kx)); 111 | canv.guards.push(Box::new(tidy_cup)); 112 | canv.guards.push(Box::new(tidy_termstate)); 113 | return canv; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /amulet/canvas.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | use std::vec; 3 | use std::rc::Rc; 4 | 5 | use ll::{Key,Style}; // TODO move these somewhere dealing with keys and text and terminal properties 6 | use ll::TerminalInfo; 7 | 8 | struct CanvasCell { 9 | dirty: bool, 10 | glyph: char, 11 | style: Style, 12 | } 13 | 14 | struct CanvasRow { 15 | is_dirty: bool, 16 | last_dirty: usize, 17 | first_dirty: usize, 18 | cells: Vec, 19 | } 20 | 21 | pub struct Canvas<'a, 'b> { 22 | terminfo: &'b TerminalInfo<'b>, 23 | start_row: usize, 24 | start_col: usize, 25 | cur_row: usize, 26 | cur_col: usize, 27 | height: usize, 28 | width: usize, 29 | 30 | rows: Vec, 31 | pub guards: Vec>, 32 | } 33 | 34 | pub fn Canvas<'a, 'b>(terminfo: &'b TerminalInfo<'b>, start_row: usize, start_col: usize, height: usize, width: usize) -> Canvas<'a, 'b> { 35 | let rows = range(0, height).map(|_row| { 36 | CanvasRow{ 37 | is_dirty: false, 38 | last_dirty: 0, 39 | first_dirty: 0, 40 | cells: range(0, width).map(|_col| { 41 | CanvasCell{ 42 | dirty: false, 43 | glyph: ' ', 44 | style: Style(), 45 | } 46 | }).collect(), 47 | } 48 | }).collect(); 49 | return Canvas{ 50 | terminfo: terminfo, 51 | 52 | start_row: start_row, 53 | start_col: start_col, 54 | cur_row: 0, 55 | cur_col: 0, 56 | height: height, 57 | width: width, 58 | 59 | rows: rows, 60 | guards: vec![], 61 | }; 62 | } 63 | 64 | impl<'a, 'b> Canvas<'a, 'b> { 65 | // ------------------------------------------------------------------------- 66 | // Creation 67 | 68 | pub fn spawn(&self, start_row: usize, start_col: usize, height: usize, width: usize) -> Canvas<'a, 'b> { 69 | // TODO verify new height/width will fit? or don't? at least verify 70 | // h/w aren't negative or zero 71 | let real_height; 72 | if height == 0 { 73 | real_height = self.height - start_row; 74 | } 75 | else { 76 | real_height = height; 77 | } 78 | 79 | let real_width; 80 | if width == 0 { 81 | real_width = self.width - start_col; 82 | } 83 | else { 84 | real_width = width; 85 | } 86 | 87 | panic!("todo not done yet"); 88 | } 89 | 90 | // ------------------------------------------------------------------------- 91 | // Accessors 92 | 93 | /** Returns the size of the canvas as (rows, columns). */ 94 | pub fn size(&self) -> (usize, usize) { 95 | return (self.height, self.width); 96 | } 97 | 98 | pub fn position(&self) -> (usize, usize) { 99 | return (self.cur_row, self.cur_col); 100 | } 101 | 102 | pub fn reposition(&mut self, row: usize, col: usize) { 103 | self.cur_row = row; 104 | self.cur_col = col; 105 | } 106 | 107 | 108 | // ------------------------------------------------------------------------- 109 | // Output 110 | 111 | pub fn clear(&mut self) { 112 | // TODO clearing the screen can be done with a single termcap, but how 113 | // do i remember that 114 | for row in self.rows.iter_mut() { 115 | row.is_dirty = true; 116 | row.last_dirty = self.width - 1; 117 | row.first_dirty = 0; 118 | for cell in row.cells.iter_mut() { 119 | *cell = CanvasCell{ 120 | dirty: true, 121 | glyph: ' ', 122 | style: Style(), 123 | }; 124 | } 125 | } 126 | } 127 | 128 | pub fn attrwrite(&mut self, s: &str, style: Style) { 129 | for glyph in s.chars() { 130 | if glyph == '\n' { 131 | // TODO this probably needs (a) more cases, (b) termcap 132 | // influence 133 | self.cur_row += 1; 134 | self.cur_col = 0; 135 | 136 | if self.cur_row >= self.height { 137 | panic!("TODO"); 138 | } 139 | 140 | // TODO is there ever anything to write here? 141 | return; 142 | } 143 | 144 | { 145 | let row = &mut self.rows[self.cur_row]; 146 | row.cells[self.cur_col] = CanvasCell{ 147 | dirty: true, 148 | glyph: glyph, 149 | style: style.clone(), 150 | }; 151 | row.is_dirty = true; 152 | if self.cur_col > row.last_dirty { 153 | row.last_dirty = self.cur_col; 154 | } 155 | if self.cur_col < row.first_dirty { 156 | row.first_dirty = self.cur_col; 157 | } 158 | } 159 | 160 | self.cur_col += 1; 161 | if self.cur_col >= self.width { 162 | self.cur_row += 1; 163 | self.cur_col = 0; 164 | } 165 | if self.cur_row >= self.height { 166 | panic!("TODO"); 167 | } 168 | } 169 | } 170 | 171 | pub fn restyle(&mut self, style: Style) { 172 | let row = &mut self.rows[self.cur_row]; 173 | row.cells[self.cur_col].style = style; 174 | 175 | // TODO this is basically duplicated from above 176 | row.is_dirty = true; 177 | if self.cur_col > row.last_dirty { 178 | row.last_dirty = self.cur_col; 179 | } 180 | if self.cur_col < row.first_dirty { 181 | row.first_dirty = self.cur_col; 182 | } 183 | } 184 | 185 | pub fn write(&mut self, s: &str) { 186 | self.attrwrite(s, Style()); 187 | } 188 | 189 | pub fn repaint(&mut self) { 190 | // TODO wrap this 191 | // TODO check for existence of cup? fallback? 192 | //self.terminfo.write_cap2("cup", self.start_col as int, self.start_row as int); 193 | 194 | let mut is_bold = false; 195 | let mut fg = 0; 196 | 197 | for row_i in range(0, self.height) { 198 | let row = &mut self.rows[row_i]; 199 | if ! row.is_dirty { 200 | continue; 201 | } 202 | 203 | // TODO the terminal could track its cursor position and optimize this move away 204 | self.terminfo.reposition(self.start_col + row.first_dirty, self.start_row + row_i); 205 | // TODO with this level of optimization, imo, there should also be a method for forcibly redrawing the entire screen from (presumed) scratch 206 | for col in range(row.first_dirty, row.last_dirty + 1) { 207 | let cell = &mut row.cells[col]; 208 | 209 | // Deal with formatting 210 | if cell.style.is_bold && ! is_bold { 211 | self.terminfo.write_cap("bold"); 212 | is_bold = true; 213 | } 214 | else if is_bold && ! cell.style.is_bold { 215 | // TODO this resets formatting entirely -- there's no way 216 | // to turn off bold/underline individually :| 217 | self.terminfo.write_cap("sgr0"); 218 | is_bold = false; 219 | } 220 | 221 | if cell.style.fg_color != fg { 222 | fg = cell.style.fg_color; 223 | 224 | // Replace -1 with an arbitrarily large number, which 225 | // apparently functions as resetting to the default color 226 | // with setaf. Maybe. 227 | // TODO i am not sure how reliable this is 228 | let actual_fg = match fg { 229 | -1 => 65535, 230 | _ => fg, 231 | }; 232 | // TODO what if setaf doesn't exist? fall back to setf i 233 | // guess, but what's the difference? 234 | self.terminfo.write_cap1("setaf", actual_fg); 235 | } 236 | 237 | self.terminfo.write(cell.glyph.to_string().as_slice()); 238 | cell.dirty = false; 239 | } 240 | 241 | row.is_dirty = false; 242 | row.first_dirty = self.width; 243 | row.last_dirty = 0; 244 | } 245 | 246 | // Clean up attribute settings when done 247 | // TODO optimization possibilities here if we remember the current cursor style -- which we may need to do anyway once we're tracking more than bold 248 | if is_bold { 249 | self.terminfo.write_cap("sgr0"); 250 | } 251 | 252 | // TODO move the cursor to its original position if that's not where it is now 253 | } 254 | 255 | // ------------------------------------------------------------------------- 256 | // Input 257 | 258 | // TODO should this auto-repaint? seems to make sense and i think curses 259 | // does 260 | pub fn read_key(&mut self) -> Key { 261 | // Thanks to urwid for already doing much of this work in a readable 262 | // manner! 263 | // TODO this doesn't time out, doesn't check for key sequences, etc 264 | // etc. it's hilariously sad. 265 | // TODO should have a timeout after Esc... et al.? 266 | // TODO this could probably stand to be broken out a bit 267 | let byte = match self.terminfo.in_file.borrow_mut().read_byte() { 268 | Ok(byte) => byte, 269 | // TODO how can this actually happen? 270 | Err(err) => panic!("couldn't read a byte?! {:?}", err), 271 | }; 272 | 273 | let mut bytes = vec![byte]; 274 | 275 | if 32 <= byte && byte <= 126 { 276 | // ASCII character 277 | return Key::Character(byte as char); 278 | } 279 | 280 | // XXX urwid here checks for some specific keys: tab, enter, backspace 281 | 282 | // XXX is this cross-terminal? 283 | if 0 < byte && byte < 27 { 284 | // Ctrl-x 285 | // TODO no nice way to return this though, so just do the character 286 | return Key::Character(byte as char); 287 | } 288 | if 27 < byte && byte < 32 { 289 | // Ctrl-X 290 | // TODO no nice way to return this though, so just do the character 291 | return Key::Character(byte as char); 292 | } 293 | 294 | // TODO supporting other encodings would be... nice... but hard. 295 | // what does curses do here; is this where it uses the locale? 296 | let encoding = "utf8"; 297 | if encoding == "utf8" && byte > 127 && byte < 256 { 298 | let mut utf8buf: [u8; 6] = [byte, 0, 0, 0, 0, 0]; 299 | 300 | let need_more; 301 | if byte & 0xe0 == 0xc0 { 302 | // two-byte form 303 | need_more = 1; 304 | } 305 | else if byte & 0xf0 == 0xe0 { 306 | // three-byte form 307 | need_more = 2; 308 | } 309 | else if byte & 0xf8 == 0xf0 { 310 | // four-byte form 311 | need_more = 3; 312 | } 313 | else { 314 | panic!(format!("junk byte {:?}", byte)); 315 | } 316 | 317 | // TODO this returns IoResult; should catch, convert to error if amount read is less 318 | // than need_more, and then do... something. 319 | self.terminfo.in_file.borrow_mut().read(&mut utf8buf[1..need_more]); 320 | // TODO umm this all only works for utf8 321 | // TODO and what if it's bogus utf8? 322 | let decoded = str::from_utf8(bytes.as_slice()).unwrap(); 323 | if decoded.len() != 1 { 324 | panic!("unexpected decoded string length!"); 325 | } 326 | 327 | return Key::Character(decoded.char_at(0)); 328 | } 329 | 330 | // XXX urwid has if byte > 127 && byte < 256... but that's covered 331 | // above because we are always utf8. 332 | 333 | 334 | // OK, check for cute terminal escapes 335 | loop { 336 | let (maybe_key, _remaining_bytes) = self.terminfo.keypress_trie.find_prefix(bytes.as_slice()); 337 | match maybe_key { 338 | Some(key) => { 339 | return key; 340 | } 341 | None => (), 342 | } 343 | // XXX right now we are discarding any leftover bytes -- but we also only read one at a time so there are never leftovers 344 | // bytes = remaining_bytes; 345 | // XXX i don't know a better way to decide when to give up reading a sequence? i guess it should be time-based 346 | if bytes.len() > 8 { 347 | break; 348 | } 349 | match self.terminfo.in_file.borrow_mut().read_byte() { 350 | Ok(byte) => bytes.push(byte), 351 | Err(_) => break, 352 | } 353 | } 354 | 355 | // TODO uhh lol this doesn't seem like a useful fallback? now we just have a pile of bytes 356 | // and throw them all away whoops 357 | return Key::Character(byte as char); 358 | } 359 | 360 | // TODO unclear whether the trailing \n should be included 361 | // TODO this doesn't handle backspace etc; it's a pretty piss-poor readline 362 | // implementation really :) 363 | pub fn read_line(&mut self) -> String { 364 | let mut chars: Vec = vec![]; 365 | loop { 366 | match self.read_key() { 367 | Key::Character(ch) => { 368 | // TODO should \r become \n? my raw() impl disables that 369 | // TODO ...actually maybe this is a termcap?? 370 | if ch == '\r' { 371 | chars.push('\n'); 372 | } 373 | else { 374 | chars.push(ch); 375 | } 376 | if ch == '\n' || ch == '\r' { 377 | break; 378 | } 379 | }, 380 | _ => (), 381 | } 382 | } 383 | 384 | return chars.into_iter().collect(); 385 | } 386 | 387 | /** Blocks until a key is pressed. 388 | * 389 | * This is identical to `read_key()`, except it returns nothing and reads 390 | * a little better if you don't care which key was pressed. 391 | */ 392 | pub fn pause(&mut self) { 393 | self.read_key(); 394 | } 395 | 396 | 397 | } 398 | -------------------------------------------------------------------------------- /amulet/termios.rs: -------------------------------------------------------------------------------- 1 | use std::clone::Clone; 2 | use libc::c_int; 3 | use std::rc::Rc; 4 | 5 | // ----------------------------------------------------------------------------- 6 | // Platform-specific implementations 7 | 8 | // So, termios, this cool POSIX standard, relies on (a) a struct definition 9 | // that lives in a C header and varies by platform and (b) a bunch of #define'd 10 | // constants that live in a C header file and vary by platform. 11 | // This is what I've got on my platform. Feel free to add yours! 12 | // Hopefully someday rustc will be able to parse this stuff out itself. 13 | 14 | #[cfg(target_os="linux")] 15 | pub mod imp { 16 | use libc::{c_int,c_uint,c_ushort,c_void}; 17 | 18 | static NCCS: c_int = 32; 19 | pub type cc_t = c_int; 20 | pub type tcflag_t = c_uint; 21 | pub type speed_t = c_uint; 22 | 23 | 24 | // Constants. 25 | /* c_cc characters */ 26 | pub static VINTR: c_uint = 0; 27 | pub static VQUIT: c_uint = 1; 28 | pub static VERASE: c_uint = 2; 29 | pub static VKILL: c_uint = 3; 30 | pub static VEOF: c_uint = 4; 31 | pub static VTIME: c_uint = 5; 32 | pub static VMIN: c_uint = 6; 33 | pub static VSWTC: c_uint = 7; 34 | pub static VSTART: c_uint = 8; 35 | pub static VSTOP: c_uint = 9; 36 | pub static VSUSP: c_uint = 10; 37 | pub static VEOL: c_uint = 11; 38 | pub static VREPRINT: c_uint = 12; 39 | pub static VDISCARD: c_uint = 13; 40 | pub static VWERASE: c_uint = 14; 41 | pub static VLNEXT: c_uint = 15; 42 | pub static VEOL2: c_uint = 16; 43 | 44 | /* c_iflag bits */ 45 | pub static IGNBRK: c_uint = 0x00001; 46 | pub static BRKINT: c_uint = 0x00002; 47 | pub static IGNPAR: c_uint = 0x00004; 48 | pub static PARMRK: c_uint = 0x00008; 49 | pub static INPCK: c_uint = 0x00010; 50 | pub static ISTRIP: c_uint = 0x00020; 51 | pub static INLCR: c_uint = 0x00040; 52 | pub static IGNCR: c_uint = 0x00080; 53 | pub static ICRNL: c_uint = 0x00100; 54 | pub static IUCLC: c_uint = 0x00200; 55 | pub static IXON: c_uint = 0x00400; 56 | pub static IXANY: c_uint = 0x00800; 57 | pub static IXOFF: c_uint = 0x01000; 58 | pub static IMAXBEL: c_uint = 0x02000; 59 | pub static IUTF8: c_uint = 0x04000; 60 | 61 | /* c_oflag bits */ 62 | pub static OPOST: c_uint = 0x00001; 63 | pub static OLCUC: c_uint = 0x00002; 64 | pub static ONLCR: c_uint = 0x00004; 65 | pub static OCRNL: c_uint = 0x00008; 66 | pub static ONOCR: c_uint = 0x00010; 67 | pub static ONLRET: c_uint = 0x00020; 68 | pub static OFILL: c_uint = 0x00040; 69 | pub static OFDEL: c_uint = 0x00080; 70 | 71 | /* c_cflag bit meaning */ 72 | pub static B0: c_uint = 0x00000; // hang up 73 | pub static B50: c_uint = 0x00001; 74 | pub static B75: c_uint = 0x00002; 75 | pub static B110: c_uint = 0x00003; 76 | pub static B134: c_uint = 0x00004; 77 | pub static B150: c_uint = 0x00005; 78 | pub static B200: c_uint = 0x00006; 79 | pub static B300: c_uint = 0x00007; 80 | pub static B600: c_uint = 0x00008; 81 | pub static B1200: c_uint = 0x00009; 82 | pub static B1800: c_uint = 0x0000a; 83 | pub static B2400: c_uint = 0x0000b; 84 | pub static B4800: c_uint = 0x0000c; 85 | pub static B9600: c_uint = 0x0000d; 86 | pub static B19200: c_uint = 0x0000e; 87 | pub static B38400: c_uint = 0x0000f; 88 | pub static CSIZE: c_uint = 0x00030; 89 | pub static CS5: c_uint = 0x00000; 90 | pub static CS6: c_uint = 0x00010; 91 | pub static CS7: c_uint = 0x00020; 92 | pub static CS8: c_uint = 0x00030; 93 | pub static CSTOPB: c_uint = 0x00040; 94 | pub static CREAD: c_uint = 0x00080; 95 | pub static PARENB: c_uint = 0x00100; 96 | pub static PARODD: c_uint = 0x00200; 97 | pub static HUPCL: c_uint = 0x00400; 98 | pub static CLOCAL: c_uint = 0x00800; 99 | pub static B57600: c_uint = 0x01001; 100 | pub static B115200: c_uint = 0x01002; 101 | pub static B230400: c_uint = 0x01003; 102 | pub static B460800: c_uint = 0x01004; 103 | pub static B500000: c_uint = 0x01005; 104 | pub static B576000: c_uint = 0x01006; 105 | pub static B921600: c_uint = 0x01007; 106 | pub static B1000000: c_uint = 0x01008; 107 | pub static B1152000: c_uint = 0x01009; 108 | pub static B1500000: c_uint = 0x0100a; 109 | pub static B2000000: c_uint = 0x0100b; 110 | pub static B2500000: c_uint = 0x0100c; 111 | pub static B3000000: c_uint = 0x0100d; 112 | pub static B3500000: c_uint = 0x0100e; 113 | pub static B4000000: c_uint = 0x0100f; 114 | 115 | /* c_lflag bits */ 116 | pub static ISIG: c_uint = 0x00001; 117 | pub static ICANON: c_uint = 0x00002; 118 | pub static ECHO: c_uint = 0x00008; 119 | pub static ECHOE: c_uint = 0x00010; 120 | pub static ECHOK: c_uint = 0x00020; 121 | pub static ECHONL: c_uint = 0x00040; 122 | pub static NOFLSH: c_uint = 0x00080; 123 | pub static TOSTOP: c_uint = 0x00100; 124 | pub static IEXTEN: c_uint = 0x08000; 125 | 126 | /* tcsetattr uses these */ 127 | pub static TCSANOW: c_int = 0; 128 | pub static TCSADRAIN: c_int = 1; 129 | pub static TCSAFLUSH: c_int = 2; 130 | 131 | /* ioctls */ 132 | pub static TIOCGWINSZ: c_int = 0x5413; 133 | 134 | 135 | pub struct termios { 136 | pub c_iflag: tcflag_t, // input modes 137 | pub c_oflag: tcflag_t, // output modes 138 | pub c_cflag: tcflag_t, // control modes 139 | pub c_lflag: tcflag_t, // local modes 140 | // why is this here? what is going on? who knows 141 | c_line: cc_t, // "line discipline" 142 | // NOTE: 32 is the value of NCCS 143 | c_cc: [cc_t; 32], // control characters 144 | c_ispeed: speed_t, // input speed 145 | c_ospeed: speed_t, // output speed 146 | } 147 | 148 | // deriving(Clone) doesn't work for fixed-size vectors (#7622) 149 | impl Clone for termios { 150 | fn clone(&self) -> termios { 151 | return termios{ 152 | // ...also it's a syntax error to not have at least one pair 153 | c_iflag: self.c_iflag, 154 | ..*self 155 | }; 156 | } 157 | } 158 | 159 | // Need this to be able to create blank structs on the stack 160 | pub fn blank_termios() -> termios { 161 | return termios{ 162 | c_iflag: 0, 163 | c_oflag: 0, 164 | c_cflag: 0, 165 | c_lflag: 0, 166 | c_line: 0, 167 | c_cc: [0; 32], 168 | c_ispeed: 0, 169 | c_ospeed: 0, 170 | }; 171 | } 172 | 173 | 174 | 175 | struct winsize { 176 | ws_row: c_ushort, 177 | ws_col: c_ushort, 178 | ws_xpixel: c_ushort, // unused 179 | ws_ypixel: c_ushort, // unused 180 | } 181 | 182 | extern { 183 | #[link_name = "ioctl"] 184 | fn ioctl_p(fd: c_int, request: c_int, arg1: *mut c_void) -> c_int; 185 | } 186 | 187 | #[fixed_stack_segment] 188 | pub fn request_terminal_size(fd: c_int) -> (usize, usize) { 189 | let mut size = winsize{ ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 }; 190 | 191 | let res = unsafe { ioctl_p(fd, TIOCGWINSZ, &mut size as *mut _ as *mut c_void) }; 192 | 193 | // XXX return value is -1 on failure 194 | // returns width, height 195 | return (size.ws_col as usize, size.ws_row as usize); 196 | } 197 | } 198 | 199 | // End of platform-specific implementations. 200 | // ----------------------------------------------------------------------------- 201 | 202 | extern { 203 | fn tcgetattr(fd: c_int, termios_p: *mut imp::termios) -> c_int; 204 | fn tcsetattr(fd: c_int, optional_actions: c_int, termios_p: *const imp::termios) -> c_int; 205 | } 206 | 207 | /** Self-reverting access to termios state changes. 208 | * 209 | * When this object goes out of scope, it will restore the tty to whatever 210 | * settings it had when this object was created. It's like RAII, except 211 | * without the C++ braindamage. 212 | */ 213 | pub struct TidyTerminalState { 214 | c_fd: c_int, 215 | c_termios_orig: imp::termios, 216 | c_termios_cur: imp::termios, 217 | } 218 | 219 | impl Drop for TidyTerminalState { 220 | fn drop(&mut self) { 221 | self.restore_term(); 222 | } 223 | } 224 | 225 | #[fixed_stack_segment] 226 | pub fn TidyTerminalState(fd: c_int) -> TidyTerminalState { 227 | let mut c_termios = imp::blank_termios(); 228 | 229 | // TODO this has a retval, but... eh... 230 | unsafe { 231 | tcgetattr(fd as c_int, &mut c_termios); 232 | } 233 | 234 | return TidyTerminalState{ 235 | c_fd: fd as c_int, 236 | c_termios_cur: c_termios.clone(), 237 | c_termios_orig: c_termios, 238 | }; 239 | } 240 | 241 | // TODO: i want this impl only for ~T but that makes the drop not work 242 | impl TidyTerminalState { 243 | #[fixed_stack_segment] 244 | fn restore_term (&mut self) { 245 | unsafe { 246 | tcsetattr(self.c_fd, imp::TCSAFLUSH, &mut self.c_termios_orig); 247 | } 248 | } 249 | 250 | /** Explicitly restore the terminal to its pristine state. */ 251 | pub fn restore(&mut self) { 252 | self.restore_term(); 253 | self.c_termios_cur = self.c_termios_orig.clone(); 254 | } 255 | 256 | 257 | // -------------------------------------------------------------------------- 258 | // Raw and cbreak. (There's no "cooked" because that is, presumably, the 259 | // default, and you can just use .restore().) 260 | // There are a lot of bits to be twiddled here, and not really any fixed 261 | // definition of which ones "should" be. The following is based on a 262 | // combination of ncurses, Python's tty module, and Linux's termios(3). 263 | 264 | /** Switch an fd to "raw" mode. 265 | * 266 | * In raw mode, absolutely every keypress is passed along to the application 267 | * untouched. This means, for example, that ^C doesn't send a SIGINT. 268 | */ 269 | pub fn raw(&mut self) { 270 | // Disable SIGINT 271 | self.c_termios_cur.c_iflag &= !imp::BRKINT; 272 | // Ignore usual signal-generating keys 273 | self.c_termios_cur.c_lflag &= !imp::ISIG; 274 | 275 | // Everything else is covered by cbreak 276 | self.cbreak(); 277 | } 278 | 279 | /** Switch an fd to "cbreak" mode. 280 | * 281 | * This is identical to raw mode, except that ^C and other signal keys 282 | * work as normal instead of going to the application. 283 | */ 284 | #[fixed_stack_segment] 285 | pub fn cbreak(&mut self) { 286 | self.c_termios_cur.c_iflag &= !( 287 | imp::IXON // ignore XON/XOFF, i.e. ^S ^Q 288 | | imp::ISTRIP // don't strip the 8th bit (?!) 289 | | imp::INPCK // don't check for parity errors 290 | | imp::ICRNL // don't convert cr to nl 291 | | imp::INLCR // don't convert nl to cr 292 | | imp::IGNCR // don't drop cr 293 | | imp::PARMRK // don't prepend \377 \0 to error nuls 294 | ); 295 | 296 | self.c_termios_cur.c_oflag &= !( 297 | // TODO turning these off make \n act as a literal newline with no 298 | // cursor return -- not what anyone expects from \n. should i 299 | // convert \n manually, or disable every other possible output flag 300 | // here? 301 | //imp::OPOST // turn off "impl-specific processing" -- this 302 | // // includes, e.g., converting tabs to spaces 303 | //| imp::ONLCR // don't convert nl to cr 304 | 0 305 | ); 306 | // TODO in the meantime, make sure \n works right 307 | self.c_termios_cur.c_oflag |= imp::OPOST | imp::ONLCR; 308 | 309 | self.c_termios_cur.c_cflag &= !( 310 | imp::PARENB // turn off parity generation/checking 311 | ); 312 | // Set 8 bits per character. 313 | // NOTE: it's unclear why this is part of "raw" mode and not just 314 | // something any modern terminal would want, but this is done in every 315 | // raw impl I've found *except* curses... 316 | self.c_termios_cur.c_cflag = 317 | (self.c_termios_cur.c_cflag & !imp::CSIZE) | imp::CS8; 318 | 319 | self.c_termios_cur.c_lflag &= !( 320 | imp::ICANON // turn off "canonical" mode -- this is the primary 321 | // char-at-a-time flag 322 | | imp::IEXTEN // turn off "impl-specific processing" 323 | | imp::ECHO // turn off local echo 324 | ); 325 | 326 | unsafe { 327 | // TCSAFLUSH: make the changes once all output thusfar has been 328 | // sent, and clear the input buffer 329 | // TODO this returns something, but even success is hokey, so what 330 | // is there to do about it 331 | // TODO do i want this in a separate 'commit()' method? for 332 | // chaining etc? 333 | tcsetattr(self.c_fd, imp::TCSAFLUSH, &self.c_termios_cur); 334 | } 335 | } 336 | } 337 | 338 | 339 | /* 340 | For reference, as the interpretation of "raw" differs greatly. This will 341 | go away once I'm confident about the correctness of everything above. 342 | 343 | p is Python; c is ncurses; i is ncurses's initscr(); L is the Linux 344 | ttyraw() function; ✓ is this module. 345 | 346 | RAW: 347 | i - BRKINT p c L ✓ 348 | i - ICRNL p i L ✓ 349 | i - INLCR i L ✓ 350 | i - IGNCR i L ✓ 351 | i - INPCK p ✓ 352 | i - ISTRIP p L ✓ 353 | i - IXON p c L ✓ 354 | i - PARMRK c L ✓ 355 | i - IGNBRK L 356 | o - OPOST p L ✓ 357 | o - ONLCR i ✓ 358 | c - CSIZE p L ✓ 359 | c - PARENB p L ✓ 360 | c + CS8 p L ✓ 361 | l - ECHO p i L ✓ 362 | l - ECHONL i L (has no effect without ICANON) 363 | l - ICANON p c L ✓ 364 | l - IEXTEN p c L ✓ 365 | l - ISIG p c L ✓ 366 | 367 | CBREAK: 368 | i - ICRNL c 369 | l - ECHO p 370 | l - ICANON p c 371 | l + ISIG c 372 | 373 | 374 | NORAW: 375 | i + IXON 376 | i + BRKINT 377 | i + PARMRK 378 | l + ISIG 379 | l + ICANON 380 | l . IEXTEN 381 | l - everything else 382 | 383 | NOCBREAK: 384 | i + ICRNL 385 | l + ICANON 386 | */ 387 | -------------------------------------------------------------------------------- /amulet/ll.rs: -------------------------------------------------------------------------------- 1 | /** Low-level ncurses wrapper, for simple or heavily customized applications. */ 2 | 3 | use libc::{c_char,c_int,c_long,c_short}; 4 | use std::ffi::CString; 5 | use std::ffi::c_str_to_bytes; 6 | use std::ptr; 7 | use std::str; 8 | use std::str::from_c_str; 9 | use libc; 10 | use std::io; 11 | use std::mem::transmute; 12 | use std::vec; 13 | use std::rc::Rc; 14 | use std::cell::RefCell; 15 | 16 | use c; 17 | use termios; 18 | use trie::Trie; 19 | 20 | extern { 21 | fn setlocale(category: c_int, locale: *mut c_char) -> *mut c_char; 22 | 23 | // XXX why the fuck is this not available 24 | static stdout: *mut libc::FILE; 25 | } 26 | 27 | 28 | /** Prints a given termcap sequence when it goes out of scope. */ 29 | pub struct TidyTermcap<'a> { 30 | terminfo: &'a TerminalInfo<'a>, 31 | cap: &'static str, 32 | } 33 | #[unsafe_destructor] 34 | impl<'a> Drop for TidyTermcap<'a> { 35 | fn drop(&mut self) { 36 | self.terminfo.write_cap(self.cap); 37 | } 38 | } 39 | 40 | 41 | pub struct TerminalInfo<'a> { 42 | pub in_fd: c_int, 43 | pub in_file: RefCell>, 44 | pub out_fd: c_int, 45 | out_file: RefCell>, 46 | 47 | pub keypress_trie: Trie, 48 | 49 | c_terminfo: *mut c::TERMINAL, 50 | tidy_termstate: termios::TidyTerminalState, 51 | 52 | //term_type: &str, 53 | } 54 | 55 | #[unsafe_destructor] 56 | impl<'a> Drop for TerminalInfo<'a> { 57 | fn drop(&mut self) { 58 | self.tidy_termstate.restore(); 59 | } 60 | } 61 | impl<'a> TerminalInfo<'a> { 62 | #[fixed_stack_segment] 63 | pub fn new() -> TerminalInfo<'a> { 64 | let mut error_code: c_int = 0; 65 | // NULL first arg means read TERM from env (TODO). 66 | // second arg is a fd to spew to on error, but it's not used when there's 67 | // an error pointer. 68 | // third arg is a var to stick the error code in. 69 | // TODO allegedly setupterm doesn't work on BSD? 70 | unsafe { 71 | let res = c::setupterm(ptr::null(), -1, &mut error_code); 72 | 73 | if res != c::OK { 74 | if error_code == -1 { 75 | panic!("Couldn't find terminfo database"); 76 | } 77 | else if error_code == 0 { 78 | panic!("Couldn't identify terminal"); 79 | } 80 | else if error_code == 1 { 81 | // The manual puts this as "terminal is hard-copy" but come on. 82 | panic!("Terminal appears to be made of paper"); 83 | } 84 | else { 85 | panic!("Something is totally fucked"); 86 | } 87 | } 88 | } 89 | 90 | // Okay; now terminfo is sitting in a magical global somewhere. Snag a 91 | // pointer to it. 92 | let terminfo = c::cur_term; 93 | 94 | let mut keypress_trie = Trie::new(); 95 | let mut p: *const *const c_char = &c::strnames[0]; 96 | unsafe { 97 | while *p != ptr::null() { 98 | let capname = from_c_str(*p); 99 | 100 | if capname.char_at(0) == 'k' { 101 | let cap = c::tigetstr(*p); 102 | if cap != ptr::null() { 103 | let cap_key = c_str_to_bytes(&cap); 104 | keypress_trie.insert(cap_key, cap_to_key(capname)); 105 | } 106 | } 107 | 108 | p = p.offset(1); 109 | } 110 | } 111 | 112 | return TerminalInfo{ 113 | // TODO would be nice to parametrize these, but Reader and Writer do 114 | // not yet expose a way to get the underlying fd, which makes the API 115 | // sucky 116 | in_fd: 0, 117 | in_file: RefCell::new(Box::new(io::stdin()) as Box), 118 | out_fd: 1, 119 | out_file: RefCell::new(Box::new(io::stdout()) as Box), 120 | 121 | keypress_trie: keypress_trie, 122 | 123 | c_terminfo: terminfo, 124 | tidy_termstate: termios::TidyTerminalState(0), 125 | }; 126 | } 127 | 128 | 129 | // ------------------------------------------------------------------------ 130 | // Capability inspection 131 | // TODO ideally, ultimately, every useful cap will be covered here... 132 | 133 | // TODO these should: 134 | // - only do the ioctl once and cache it 135 | // - handle failure in ioctl 136 | // - handle TIOCGSIZE instead of that other thing 137 | // - handle SIGWINCH 138 | // - fall back to environment 139 | // - THEN fall back to termcap 140 | pub fn height(&self) -> usize { 141 | // TODO rather not dip into `imp`, but `pub use` isn't working right 142 | let (_, height) = termios::imp::request_terminal_size(self.out_fd); 143 | return height; 144 | //return self.numeric_cap("lines"); 145 | } 146 | pub fn width(&self) -> usize { 147 | let (width, _) = termios::imp::request_terminal_size(self.out_fd); 148 | return width; 149 | //return self.numeric_cap("cols"); 150 | } 151 | 152 | // ------------------------------------------------------------------------ 153 | // Very-low-level capability inspection 154 | 155 | #[fixed_stack_segment] 156 | fn flag_cap(&self, name: &str) -> bool { 157 | unsafe { 158 | c::set_curterm(self.c_terminfo); 159 | 160 | let mut value = 0; 161 | let c_name = CString::from_slice(name.as_bytes()); 162 | value = c::tigetflag(c_name.as_ptr()); 163 | 164 | if value == -1 { 165 | // wrong type 166 | panic!("wrong type"); 167 | } 168 | 169 | // Otherwise, is 0 or 1 170 | return value != 0; 171 | } 172 | } 173 | 174 | #[fixed_stack_segment] 175 | fn numeric_cap(&self, name: &str) -> u32 { 176 | unsafe { 177 | c::set_curterm(self.c_terminfo); 178 | 179 | let mut value = -1; 180 | let c_name = CString::from_slice(name.as_bytes()); 181 | value = c::tigetnum(c_name.as_ptr()); 182 | 183 | if value == -2 { 184 | // wrong type 185 | panic!("wrong type"); 186 | } 187 | else if value == -1 { 188 | // missing; should be None 189 | panic!("missing; should be None"); 190 | } 191 | 192 | return value as u32; 193 | } 194 | } 195 | 196 | #[fixed_stack_segment] 197 | fn _string_cap_cstr(&self, name: &str) -> *const c_char { 198 | unsafe { 199 | c::set_curterm(self.c_terminfo); 200 | 201 | let c_name = CString::from_slice(name.as_bytes()); 202 | let value = c::tigetstr(c_name.as_ptr()); 203 | 204 | if value == ptr::null() { 205 | // missing; should be None really 206 | panic!("missing; should be None really"); 207 | } 208 | else if transmute::<_, isize>(value) == -1 { 209 | // wrong type 210 | panic!("wrong type"); 211 | } 212 | 213 | return value; 214 | } 215 | } 216 | 217 | fn string_cap(&self, name: &str) -> &str { 218 | let value = self._string_cap_cstr(name); 219 | 220 | unsafe { 221 | return from_c_str(value); 222 | } 223 | } 224 | 225 | // TODO i am not really liking the string capability handling anywhere in 226 | // here. 227 | // the variable arguments thing sucks ass. we should at LEAST check for 228 | // how many arguments are expected, and ideally enforce it at compile time 229 | // somehow -- perhaps with a method for every string cap. (yikes. having 230 | // keys be a separate thing would help, though.) 231 | 232 | /** Returns a string capability, formatted with the passed vector of 233 | * arguments. 234 | * 235 | * Passing the correct number of arguments is your problem, though any 236 | * missing arguments become zero. No capability requires more than 9 237 | * arguments. 238 | */ 239 | #[fixed_stack_segment] 240 | fn format_cap(&self, name: &str, args: Vec) -> &str { 241 | unsafe { 242 | c::set_curterm(self.c_terminfo); 243 | 244 | let template = self._string_cap_cstr(name); 245 | let padded_args = args + &[0, 0, 0, 0, 0, 0, 0, 0]; 246 | let formatted = c::tparm( 247 | template, 248 | padded_args[0] as c_long, 249 | padded_args[1] as c_long, 250 | padded_args[2] as c_long, 251 | padded_args[3] as c_long, 252 | padded_args[4] as c_long, 253 | padded_args[5] as c_long, 254 | padded_args[6] as c_long, 255 | padded_args[7] as c_long, 256 | padded_args[8] as c_long 257 | ); 258 | 259 | return from_c_str(formatted); 260 | } 261 | } 262 | 263 | #[fixed_stack_segment] 264 | fn _write_capx(&self, name: &str, 265 | arg1: c_long, arg2: c_long, arg3: c_long, 266 | arg4: c_long, arg5: c_long, arg6: c_long, 267 | arg7: c_long, arg8: c_long, arg9: c_long) 268 | { 269 | unsafe { 270 | c::set_curterm(self.c_terminfo); 271 | 272 | let template = self._string_cap_cstr(name); 273 | 274 | let formatted = c::tparm( 275 | template, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); 276 | 277 | //unsafe { io::stderr().write_str(fmt!("%s\t%s\n", name, from_c_str(formatted))); } 278 | 279 | // TODO we are supposed to use curses's tputs(3) to print formatted 280 | // capabilities, because they sometimes contain "padding" of the 281 | // form $<5>. alas, tputs always prints to stdout! but we don't 282 | // yet allow passing in a custom fd so i guess that's okay. would 283 | // be nice to reimplement this in Rust code someday though. 284 | c::putp(formatted); 285 | 286 | // TODO another reason dipping into C sucks: we have to manually 287 | // flush its buffer every time we do so. 288 | libc::fflush(stdout); 289 | } 290 | } 291 | 292 | // TODO seems it would make sense to cache non-formatted capabilities (as 293 | // well as numeric/flag ones), which i think blessings does 294 | pub fn write_cap(&self, cap_name: &str) { 295 | // If we're calling this function then this capability really shouldn't 296 | // take any arguments, but someone might have screwed up, or it may 297 | // have an escaped % or something. Best do the whole formatting thing. 298 | self._write_capx(cap_name, 0, 0, 0, 0, 0, 0, 0, 0, 0); 299 | } 300 | pub fn write_cap1(&self, cap_name: &str, arg1: isize) { 301 | self._write_capx(cap_name, arg1 as c_long, 0, 0, 0, 0, 0, 0, 0, 0); 302 | } 303 | pub fn write_cap2(&self, cap_name: &str, arg1: isize, arg2: isize) { 304 | self._write_capx(cap_name, arg1 as c_long, arg2 as c_long, 0, 0, 0, 0, 0, 0, 0); 305 | } 306 | 307 | pub fn write_tidy_cap(&'a self, do_cap: &str, undo_cap: &'static str) -> TidyTermcap<'a> { 308 | self.write_cap(do_cap); 309 | 310 | return TidyTermcap{ terminfo: self, cap: undo_cap }; 311 | } 312 | 313 | // TODO should capabilities just have a method apiece, like blessings? 314 | 315 | // Output 316 | 317 | pub fn write(&self, s: &str) { 318 | let mut out_file = self.out_file.borrow_mut(); 319 | out_file.flush(); 320 | // TODO well. should be a bit more flexible, i guess. 321 | out_file.write_str(s); 322 | out_file.flush(); 323 | } 324 | 325 | 326 | 327 | // Some stuff 328 | pub fn reposition(&self, x: usize, y: usize) { 329 | // TODO check for existence of cup 330 | self.write_cap2("cup", y as isize, x as isize); 331 | } 332 | } 333 | 334 | 335 | //////////////////////////////////////////////////////////////////////////////// 336 | // Attributes 337 | 338 | #[derive(Clone)] 339 | pub struct Style { 340 | // TODO i guess these could be compacted into a bitstring, but eh. 341 | pub is_bold: bool, 342 | pub is_underline: bool, 343 | 344 | // TODO strictly speaking these should refer to entire colors, not just 345 | // color numbers, for compatability with a truckload of other kinds of 346 | // terminals. but, you know. 347 | // TODO -1 for the default is super hokey and only for curses compat 348 | pub fg_color: isize, 349 | pub bg_color: isize, 350 | } 351 | pub fn Style() -> Style { 352 | return Style{ ..NORMAL }; 353 | } 354 | impl Style { 355 | pub fn bold(&self) -> Style { 356 | return Style{ is_bold: true, ..*self }; 357 | } 358 | 359 | pub fn underline(&self) -> Style { 360 | return Style{ is_underline: true, ..*self }; 361 | } 362 | 363 | // TODO this pretty much blows; color pairs are super archaic and i am 364 | // trying to hack around them until i just give up and bail on the curses 365 | // dependency. works on my machine... 366 | // TODO this only works for the first 16 colors anyway 367 | // TODO calling start_color() resets all color pairs, so you can't use this 368 | // before capturing the window... :| 369 | // TODO this doesn't handle default colors correctly, because those are 370 | // color index -1. 371 | pub fn fg(&self, color: isize) -> Style { 372 | return Style{ fg_color: color, ..*self }; 373 | } 374 | pub fn bg(&self, color: isize) -> Style { 375 | return Style{ bg_color: color, ..*self }; 376 | } 377 | 378 | #[fixed_stack_segment] 379 | fn c_value(&self) -> c_int { 380 | let mut rv: c_int = 0; 381 | 382 | if self.is_bold { 383 | rv |= c::A_BOLD; 384 | } 385 | if self.is_underline { 386 | rv |= c::A_UNDERLINE; 387 | } 388 | 389 | // Calculate a pair number to consume. It's a signed short, so use the 390 | // lower 8 bits for fg and upper 7 for bg 391 | // TODO without ext_colors, this gets all fucked up if the pair number 392 | // is anything over 255 393 | let fg = match self.fg_color { 394 | -1 => 14, 395 | _ => self.fg_color, 396 | }; 397 | let bg = match self.bg_color { 398 | -1 => 14, 399 | _ => self.bg_color, 400 | }; 401 | //let pair = ((bg << 4) | fg) as c_short; 402 | let pair = fg as c_short; 403 | unsafe { 404 | c::init_pair(pair, self.fg_color as c_short, self.bg_color as c_short); 405 | rv |= c::COLOR_PAIR(pair as c_int); 406 | } 407 | 408 | return rv; 409 | } 410 | } 411 | 412 | pub static NORMAL: Style = Style{ is_bold: false, is_underline: false, fg_color: -1, bg_color: -1 }; 413 | 414 | 415 | //////////////////////////////////////////////////////////////////////////////// 416 | // Key handling 417 | 418 | // TODO: i don't know how to handle ctrl-/alt- sequences. 419 | // 1. i don't know how to represent them type-wise 420 | // 2. i don't know how to parse them! they aren't in termcap. 421 | // TODO why does this even exist? just make these all parts of the Key enum?? 422 | #[derive(Clone, Show)] 423 | pub enum SpecialKeyCode { 424 | LEFT, 425 | RIGHT, 426 | UP, 427 | DOWN, 428 | ESC, 429 | 430 | // XXX temp kludge until i have all yonder keys 431 | UNKNOWN, 432 | } 433 | 434 | // TODO "Show" doesn't really cut it; this should implement something debuggable 435 | #[derive(Clone, Show)] 436 | pub enum Key { 437 | Character(char), 438 | SpecialKey(SpecialKeyCode), 439 | FunctionKey(u32), 440 | } 441 | 442 | fn cap_to_key(cap: &str) -> Key { 443 | // TODO this matching would be much more efficient if it used, hurr, a 444 | // trie. but seems silly to build one only to use it a few times. 445 | // TODO uh maybe this should use the happy C names 446 | return match cap { 447 | "kcuf1" => Key::SpecialKey(SpecialKeyCode::RIGHT), 448 | "kcub1" => Key::SpecialKey(SpecialKeyCode::LEFT), 449 | "kcuu1" => Key::SpecialKey(SpecialKeyCode::UP), 450 | "kcud1" => Key::SpecialKey(SpecialKeyCode::DOWN), 451 | "kf1" => Key::FunctionKey(1), 452 | "kf2" => Key::FunctionKey(2), 453 | "kf3" => Key::FunctionKey(3), 454 | "kf4" => Key::FunctionKey(4), 455 | "kf5" => Key::FunctionKey(5), 456 | "kf6" => Key::FunctionKey(6), 457 | "kf7" => Key::FunctionKey(7), 458 | "kf8" => Key::FunctionKey(8), 459 | "kf9" => Key::FunctionKey(9), 460 | "kf10" => Key::FunctionKey(10), 461 | "kf11" => Key::FunctionKey(11), 462 | "kf12" => Key::FunctionKey(12), 463 | _ => Key::SpecialKey(SpecialKeyCode::UNKNOWN), 464 | }; 465 | } 466 | -------------------------------------------------------------------------------- /amulet/c.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen */ 2 | 3 | use libc::*; 4 | 5 | // Manually generated from term.h 6 | // TODO less manual would be appreciated 7 | pub type TERMINAL = c_void; 8 | #[link(name = "ncursesw")] 9 | extern { 10 | pub static cur_term: *mut TERMINAL; 11 | 12 | // These are static arrays of unknown length, which is hard to express in 13 | // Rust; fudge it here and do pointer mangling in Rust-land 14 | pub static boolnames: [*const c_char; 1]; 15 | pub static numnames: [*const c_char; 1]; 16 | pub static strnames: [*const c_char; 1]; 17 | pub static boolcodes: [*const c_char; 1]; 18 | 19 | pub fn setupterm(term: *const c_char, arg2: c_int, arg3: *mut c_int) -> c_int; 20 | pub fn set_curterm(arg1: *mut TERMINAL) -> *mut TERMINAL; 21 | pub fn tparm(format: *const c_char, arg1: c_long, arg2: c_long, arg3: c_long, arg4: c_long, arg5: c_long, arg6: c_long, arg7: c_long, arg8: c_long, arg9: c_long) -> *const c_char; 22 | } 23 | 24 | 25 | 26 | // This is not necessarily true, but it's *probably* true. Defined in wchar.h 27 | // but missing from Rust's libc definitions. 28 | pub type wint_t = c_uint; 29 | pub static WEOF: c_uint = 0xffffffff; 30 | 31 | pub type chtype = c_uint; 32 | pub type mmask_t = c_ulong; 33 | pub type NCURSES_BOOL = c_uchar; 34 | pub type Struct_screen = c_void; 35 | pub type SCREEN = Struct_screen; 36 | pub type WINDOW = Struct__win_st; 37 | pub type attr_t = chtype; 38 | pub struct cchar_t { 39 | attr: attr_t, 40 | chars: [wchar_t; 5], 41 | } 42 | pub type Struct_ldat = c_void; 43 | pub struct Struct__win_st { 44 | _cury: c_short, 45 | _curx: c_short, 46 | _maxy: c_short, 47 | _maxx: c_short, 48 | _begy: c_short, 49 | _begx: c_short, 50 | _flags: c_short, 51 | _attrs: attr_t, 52 | _bkgd: chtype, 53 | _notimeout: c_int, 54 | _clear: c_int, 55 | _leaveok: c_int, 56 | _scroll: c_int, 57 | _idlok: c_int, 58 | _idcok: c_int, 59 | _immed: c_int, 60 | _sync: c_int, 61 | _use_keypad: c_int, 62 | _delay: c_int, 63 | _line: *mut Struct_ldat, 64 | _regtop: c_short, 65 | _regbottom: c_short, 66 | _parx: c_int, 67 | _pary: c_int, 68 | _parent: *mut WINDOW, 69 | _pad: Struct_pdat, 70 | _yoffset: c_short, 71 | _bkgrnd: cchar_t, 72 | } 73 | pub struct Struct_pdat { 74 | _pad_y: c_short, 75 | _pad_x: c_short, 76 | _pad_top: c_short, 77 | _pad_left: c_short, 78 | _pad_bottom: c_short, 79 | _pad_right: c_short, 80 | } 81 | pub type NCURSES_OUTC = *mut u8; 82 | pub type NCURSES_WINDOW_CB = *mut u8; 83 | pub type NCURSES_SCREEN_CB = *mut u8; 84 | pub struct MEVENT { 85 | id: c_short, 86 | x: c_int, 87 | y: c_int, 88 | z: c_int, 89 | bstate: mmask_t, 90 | } 91 | #[link(name = "ncursesw")] 92 | extern { 93 | pub static acs_map: c_void; 94 | pub static curscr: *mut WINDOW; 95 | pub static newscr: *mut WINDOW; 96 | pub static stdscr: *mut WINDOW; 97 | pub static ttytype: c_void; 98 | pub static COLORS: c_int; 99 | pub static COLOR_PAIRS: c_int; 100 | pub static COLS: c_int; 101 | pub static ESCDELAY: c_int; 102 | pub static LINES: c_int; 103 | pub static TABSIZE: c_int; 104 | pub static _nc_wacs: *mut cchar_t; 105 | //wide: fn addch(arg1: chtype) -> c_int; 106 | //wide: fn addchnstr(arg1: *mut chtype, arg2: c_int) -> c_int; 107 | //wide: fn addchstr(arg1: *mut chtype) -> c_int; 108 | //wide: fn addnstr(arg1: *mut c_schar, arg2: c_int) -> c_int; 109 | //wide: fn addstr(arg1: *mut c_schar) -> c_int; 110 | pub fn attroff(arg1: c_int) -> c_int; 111 | pub fn attron(arg1: c_int) -> c_int; 112 | pub fn attrset(arg1: c_int) -> c_int; 113 | pub fn attr_get(arg1: *mut attr_t, arg2: *mut c_short, arg3: *mut c_void) -> c_int; 114 | pub fn attr_off(arg1: attr_t, arg2: *mut c_void) -> c_int; 115 | pub fn attr_on(arg1: attr_t, arg2: *mut c_void) -> c_int; 116 | pub fn attr_set(arg1: attr_t, arg2: c_short, arg3: *mut c_void) -> c_int; 117 | pub fn baudrate() -> c_int; 118 | pub fn beep() -> c_int; 119 | //wide: fn bkgd(arg1: chtype) -> c_int; 120 | //wide: fn bkgdset(arg1: chtype); 121 | //wide: fn border(arg1: chtype, arg2: chtype, arg3: chtype, arg4: chtype, arg5: chtype, arg6: chtype, arg7: chtype, arg8: chtype) -> c_int; 122 | //wide: fn box(arg1: *mut WINDOW, arg2: chtype, arg3: chtype) -> c_int; 123 | pub fn can_change_color() -> c_int; 124 | pub fn cbreak() -> c_int; 125 | pub fn chgat(arg1: c_int, arg2: attr_t, arg3: c_short, arg4: *mut c_void) -> c_int; 126 | pub fn clear() -> c_int; 127 | pub fn clearok(arg1: *mut WINDOW, arg2: c_int) -> c_int; 128 | pub fn clrtobot() -> c_int; 129 | pub fn clrtoeol() -> c_int; 130 | pub fn color_content(arg1: c_short, arg2: *mut c_short, arg3: *mut c_short, arg4: *mut c_short) -> c_int; 131 | pub fn color_set(arg1: c_short, arg2: *mut c_void) -> c_int; 132 | pub fn COLOR_PAIR(arg1: c_int) -> c_int; 133 | pub fn copywin(arg1: *mut WINDOW, arg2: *mut WINDOW, arg3: c_int, arg4: c_int, arg5: c_int, arg6: c_int, arg7: c_int, arg8: c_int, arg9: c_int) -> c_int; 134 | pub fn curs_set(arg1: c_int) -> c_int; 135 | pub fn def_prog_mode() -> c_int; 136 | pub fn def_shell_mode() -> c_int; 137 | pub fn delay_output(arg1: c_int) -> c_int; 138 | pub fn delch() -> c_int; 139 | pub fn delscreen(arg1: *mut SCREEN); 140 | pub fn delwin(arg1: *mut WINDOW) -> c_int; 141 | pub fn deleteln() -> c_int; 142 | pub fn derwin(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: c_int, arg5: c_int) -> *mut WINDOW; 143 | pub fn doupdate() -> c_int; 144 | pub fn dupwin(arg1: *mut WINDOW) -> *mut WINDOW; 145 | pub fn echo() -> c_int; 146 | //wide: fn echochar(arg1: chtype) -> c_int; 147 | pub fn erase() -> c_int; 148 | pub fn endwin() -> c_int; 149 | //wide: fn erasechar() -> c_schar; 150 | pub fn filter(); 151 | pub fn flash() -> c_int; 152 | pub fn flushinp() -> c_int; 153 | //wide: fn getbkgd(arg1: *mut WINDOW) -> chtype; 154 | //wide: fn getch() -> c_int; 155 | //wide: fn getnstr(arg1: *mut c_schar, arg2: c_int) -> c_int; 156 | //wide: fn getstr(arg1: *mut c_schar) -> c_int; 157 | pub fn getwin(arg1: *mut FILE) -> *mut WINDOW; 158 | pub fn halfdelay(arg1: c_int) -> c_int; 159 | pub fn has_colors() -> c_int; 160 | pub fn has_ic() -> c_int; 161 | pub fn has_il() -> c_int; 162 | //wide: fn hline(arg1: chtype, arg2: c_int) -> c_int; 163 | pub fn idcok(arg1: *mut WINDOW, arg2: c_int); 164 | pub fn idlok(arg1: *mut WINDOW, arg2: c_int) -> c_int; 165 | pub fn immedok(arg1: *mut WINDOW, arg2: c_int); 166 | //wide: fn inch() -> chtype; 167 | //wide: fn inchnstr(arg1: *mut chtype, arg2: c_int) -> c_int; 168 | //wide: fn inchstr(arg1: *mut chtype) -> c_int; 169 | pub fn initscr() -> *mut WINDOW; 170 | pub fn init_color(arg1: c_short, arg2: c_short, arg3: c_short, arg4: c_short) -> c_int; 171 | pub fn init_pair(arg1: c_short, arg2: c_short, arg3: c_short) -> c_int; 172 | //wide: fn innstr(arg1: *mut c_schar, arg2: c_int) -> c_int; 173 | //wide: fn insch(arg1: chtype) -> c_int; 174 | pub fn insdelln(arg1: c_int) -> c_int; 175 | pub fn insertln() -> c_int; 176 | //wide: fn insnstr(arg1: *mut c_schar, arg2: c_int) -> c_int; 177 | //wide: fn insstr(arg1: *mut c_schar) -> c_int; 178 | //wide: fn instr(arg1: *mut c_schar) -> c_int; 179 | pub fn intrflush(arg1: *mut WINDOW, arg2: c_int) -> c_int; 180 | pub fn isendwin() -> c_int; 181 | pub fn is_linetouched(arg1: *mut WINDOW, arg2: c_int) -> c_int; 182 | pub fn is_wintouched(arg1: *mut WINDOW) -> c_int; 183 | //wide: fn keyname(arg1: c_int) -> *mut c_schar; 184 | pub fn keypad(arg1: *mut WINDOW, arg2: c_int) -> c_int; 185 | //wide: fn killchar() -> c_schar; 186 | pub fn leaveok(arg1: *mut WINDOW, arg2: c_int) -> c_int; 187 | pub fn longname() -> *mut c_schar; 188 | pub fn meta(arg1: *mut WINDOW, arg2: c_int) -> c_int; 189 | //wide: fn mvaddch(arg1: c_int, arg2: c_int, arg3: chtype) -> c_int; 190 | //wide: fn mvaddchnstr(arg1: c_int, arg2: c_int, arg3: *mut chtype, arg4: c_int) -> c_int; 191 | //wide: fn mvaddchstr(arg1: c_int, arg2: c_int, arg3: *mut chtype) -> c_int; 192 | //wide: fn mvaddnstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar, arg4: c_int) -> c_int; 193 | //wide: fn mvaddstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar) -> c_int; 194 | pub fn mvchgat(arg1: c_int, arg2: c_int, arg3: c_int, arg4: attr_t, arg5: c_short, arg6: *mut c_void) -> c_int; 195 | pub fn mvcur(arg1: c_int, arg2: c_int, arg3: c_int, arg4: c_int) -> c_int; 196 | pub fn mvdelch(arg1: c_int, arg2: c_int) -> c_int; 197 | pub fn mvderwin(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 198 | //wide: fn mvgetch(arg1: c_int, arg2: c_int) -> c_int; 199 | //wide: fn mvgetnstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar, arg4: c_int) -> c_int; 200 | //wide: fn mvgetstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar) -> c_int; 201 | //wide: fn mvhline(arg1: c_int, arg2: c_int, arg3: chtype, arg4: c_int) -> c_int; 202 | //wide: fn mvinch(arg1: c_int, arg2: c_int) -> chtype; 203 | //wide: fn mvinchnstr(arg1: c_int, arg2: c_int, arg3: *mut chtype, arg4: c_int) -> c_int; 204 | //wide: fn mvinchstr(arg1: c_int, arg2: c_int, arg3: *mut chtype) -> c_int; 205 | //wide: fn mvinnstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar, arg4: c_int) -> c_int; 206 | //wide: fn mvinsch(arg1: c_int, arg2: c_int, arg3: chtype) -> c_int; 207 | //wide: fn mvinsnstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar, arg4: c_int) -> c_int; 208 | //wide: fn mvinsstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar) -> c_int; 209 | //wide: fn mvinstr(arg1: c_int, arg2: c_int, arg3: *mut c_schar) -> c_int; 210 | pub fn mvprintw(arg1: c_int, arg2: c_int, arg3: *mut c_schar) -> c_int; 211 | pub fn mvscanw(arg1: c_int, arg2: c_int, arg3: *mut c_schar) -> c_int; 212 | //wide: fn mvvline(arg1: c_int, arg2: c_int, arg3: chtype, arg4: c_int) -> c_int; 213 | //wide: fn mvwaddch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: chtype) -> c_int; 214 | //wide: fn mvwaddchnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut chtype, arg5: c_int) -> c_int; 215 | //wide: fn mvwaddchstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut chtype) -> c_int; 216 | //wide: fn mvwaddnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar, arg5: c_int) -> c_int; 217 | //wide: fn mvwaddstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar) -> c_int; 218 | pub fn mvwchgat(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: c_int, arg5: attr_t, arg6: c_short, arg7: *mut c_void) -> c_int; 219 | pub fn mvwdelch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 220 | //wide: fn mvwgetch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 221 | //wide: fn mvwgetnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar, arg5: c_int) -> c_int; 222 | //wide: fn mvwgetstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar) -> c_int; 223 | //wide: fn mvwhline(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: chtype, arg5: c_int) -> c_int; 224 | //wide: fn mvwin(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 225 | //wide: fn mvwinch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> chtype; 226 | //wide: fn mvwinchnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut chtype, arg5: c_int) -> c_int; 227 | //wide: fn mvwinchstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut chtype) -> c_int; 228 | //wide: fn mvwinnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar, arg5: c_int) -> c_int; 229 | //wide: fn mvwinsch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: chtype) -> c_int; 230 | //wide: fn mvwinsnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar, arg5: c_int) -> c_int; 231 | //wide: fn mvwinsstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar) -> c_int; 232 | //wide: fn mvwinstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar) -> c_int; 233 | pub fn mvwprintw(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar) -> c_int; 234 | pub fn mvwscanw(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut c_schar) -> c_int; 235 | //wide: fn mvwvline(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: chtype, arg5: c_int) -> c_int; 236 | pub fn napms(arg1: c_int) -> c_int; 237 | pub fn newpad(arg1: c_int, arg2: c_int) -> *mut WINDOW; 238 | pub fn newterm(arg1: *mut c_schar, arg2: *mut FILE, arg3: *mut FILE) -> *mut SCREEN; 239 | pub fn newwin(arg1: c_int, arg2: c_int, arg3: c_int, arg4: c_int) -> *mut WINDOW; 240 | pub fn nl() -> c_int; 241 | pub fn nocbreak() -> c_int; 242 | pub fn nodelay(arg1: *mut WINDOW, arg2: c_int) -> c_int; 243 | pub fn noecho() -> c_int; 244 | pub fn nonl() -> c_int; 245 | pub fn noqiflush(); 246 | pub fn noraw() -> c_int; 247 | pub fn notimeout(arg1: *mut WINDOW, arg2: c_int) -> c_int; 248 | pub fn overlay(arg1: *mut WINDOW, arg2: *mut WINDOW) -> c_int; 249 | pub fn overwrite(arg1: *mut WINDOW, arg2: *mut WINDOW) -> c_int; 250 | pub fn pair_content(arg1: c_short, arg2: *mut c_short, arg3: *mut c_short) -> c_int; 251 | pub fn PAIR_NUMBER(arg1: c_int) -> c_int; 252 | //wide: fn pechochar(arg1: *mut WINDOW, arg2: chtype) -> c_int; 253 | pub fn pnoutrefresh(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: c_int, arg5: c_int, arg6: c_int, arg7: c_int) -> c_int; 254 | pub fn prefresh(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: c_int, arg5: c_int, arg6: c_int, arg7: c_int) -> c_int; 255 | pub fn printw(arg1: *mut c_schar) -> c_int; 256 | pub fn putwin(arg1: *mut WINDOW, arg2: *mut FILE) -> c_int; 257 | pub fn qiflush(); 258 | pub fn raw() -> c_int; 259 | pub fn redrawwin(arg1: *mut WINDOW) -> c_int; 260 | pub fn refresh() -> c_int; 261 | pub fn resetty() -> c_int; 262 | pub fn reset_prog_mode() -> c_int; 263 | pub fn reset_shell_mode() -> c_int; 264 | pub fn ripoffline(arg1: c_int, arg2: *mut u8) -> c_int; 265 | pub fn savetty() -> c_int; 266 | pub fn scanw(arg1: *mut c_schar) -> c_int; 267 | pub fn scr_dump(arg1: *mut c_schar) -> c_int; 268 | pub fn scr_init(arg1: *mut c_schar) -> c_int; 269 | pub fn scrl(arg1: c_int) -> c_int; 270 | pub fn scroll(arg1: *mut WINDOW) -> c_int; 271 | pub fn scrollok(arg1: *mut WINDOW, arg2: c_int) -> c_int; 272 | pub fn scr_restore(arg1: *mut c_schar) -> c_int; 273 | pub fn scr_set(arg1: *mut c_schar) -> c_int; 274 | pub fn setscrreg(arg1: c_int, arg2: c_int) -> c_int; 275 | pub fn set_term(arg1: *mut SCREEN) -> *mut SCREEN; 276 | pub fn slk_attroff(arg1: chtype) -> c_int; 277 | pub fn slk_attr_off(arg1: attr_t, arg2: *mut c_void) -> c_int; 278 | pub fn slk_attron(arg1: chtype) -> c_int; 279 | pub fn slk_attr_on(arg1: attr_t, arg2: *mut c_void) -> c_int; 280 | pub fn slk_attrset(arg1: chtype) -> c_int; 281 | pub fn slk_attr() -> attr_t; 282 | pub fn slk_attr_set(arg1: attr_t, arg2: c_short, arg3: *mut c_void) -> c_int; 283 | pub fn slk_clear() -> c_int; 284 | pub fn slk_color(arg1: c_short) -> c_int; 285 | pub fn slk_init(arg1: c_int) -> c_int; 286 | pub fn slk_label(arg1: c_int) -> *mut c_schar; 287 | pub fn slk_noutrefresh() -> c_int; 288 | pub fn slk_refresh() -> c_int; 289 | pub fn slk_restore() -> c_int; 290 | //wide: fn slk_set(arg1: c_int, arg2: *mut c_schar, arg3: c_int) -> c_int; 291 | pub fn slk_touch() -> c_int; 292 | pub fn standout() -> c_int; 293 | pub fn standend() -> c_int; 294 | pub fn start_color() -> c_int; 295 | pub fn subpad(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: c_int, arg5: c_int) -> *mut WINDOW; 296 | pub fn subwin(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: c_int, arg5: c_int) -> *mut WINDOW; 297 | pub fn syncok(arg1: *mut WINDOW, arg2: c_int) -> c_int; 298 | //wide: fn termattrs() -> chtype; 299 | pub fn termname() -> *mut c_schar; 300 | pub fn timeout(arg1: c_int); 301 | pub fn touchline(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 302 | pub fn touchwin(arg1: *mut WINDOW) -> c_int; 303 | pub fn typeahead(arg1: c_int) -> c_int; 304 | //wide: fn ungetch(arg1: c_int) -> c_int; 305 | pub fn untouchwin(arg1: *mut WINDOW) -> c_int; 306 | pub fn use_env(arg1: c_int); 307 | //wide: fn vidattr(arg1: chtype) -> c_int; 308 | //wide: fn vidputs(arg1: chtype, arg2: NCURSES_OUTC) -> c_int; 309 | //wide: fn vline(arg1: chtype, arg2: c_int) -> c_int; 310 | // varargs aren't supported by Rust, but these are covered by the fmt! 311 | // macro anyway 312 | /* 313 | fn vwprintw(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: *mut __va_list_tag) -> c_int; 314 | fn vw_printw(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: *mut __va_list_tag) -> c_int; 315 | fn vwscanw(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: *mut __va_list_tag) -> c_int; 316 | fn vw_scanw(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: *mut __va_list_tag) -> c_int; 317 | */ 318 | //wide: fn waddch(arg1: *mut WINDOW, arg2: chtype) -> c_int; 319 | //wide: fn waddchnstr(arg1: *mut WINDOW, arg2: *mut chtype, arg3: c_int) -> c_int; 320 | //wide: fn waddchstr(arg1: *mut WINDOW, arg2: *mut chtype) -> c_int; 321 | //wide: fn waddnstr(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: c_int) -> c_int; 322 | //wide: fn waddstr(arg1: *mut WINDOW, arg2: *mut c_schar) -> c_int; 323 | pub fn wattron(arg1: *mut WINDOW, arg2: c_int) -> c_int; 324 | pub fn wattroff(arg1: *mut WINDOW, arg2: c_int) -> c_int; 325 | pub fn wattrset(arg1: *mut WINDOW, arg2: c_int) -> c_int; 326 | pub fn wattr_get(arg1: *mut WINDOW, arg2: *mut attr_t, arg3: *mut c_short, arg4: *mut c_void) -> c_int; 327 | pub fn wattr_on(arg1: *mut WINDOW, arg2: attr_t, arg3: *mut c_void) -> c_int; 328 | pub fn wattr_off(arg1: *mut WINDOW, arg2: attr_t, arg3: *mut c_void) -> c_int; 329 | pub fn wattr_set(arg1: *mut WINDOW, arg2: attr_t, arg3: c_short, arg4: *mut c_void) -> c_int; 330 | //wide: fn wbkgd(arg1: *mut WINDOW, arg2: chtype) -> c_int; 331 | //wide: fn wbkgdset(arg1: *mut WINDOW, arg2: chtype); 332 | //wide: fn wborder(arg1: *mut WINDOW, arg2: chtype, arg3: chtype, arg4: chtype, arg5: chtype, arg6: chtype, arg7: chtype, arg8: chtype, arg9: chtype) -> c_int; 333 | pub fn wchgat(arg1: *mut WINDOW, arg2: c_int, arg3: attr_t, arg4: c_short, arg5: *mut c_void) -> c_int; 334 | pub fn wclear(arg1: *mut WINDOW) -> c_int; 335 | pub fn wclrtobot(arg1: *mut WINDOW) -> c_int; 336 | pub fn wclrtoeol(arg1: *mut WINDOW) -> c_int; 337 | pub fn wcolor_set(arg1: *mut WINDOW, arg2: c_short, arg3: *mut c_void) -> c_int; 338 | pub fn wcursyncup(arg1: *mut WINDOW); 339 | pub fn wdelch(arg1: *mut WINDOW) -> c_int; 340 | pub fn wdeleteln(arg1: *mut WINDOW) -> c_int; 341 | //wide: fn wechochar(arg1: *mut WINDOW, arg2: chtype) -> c_int; 342 | pub fn werase(arg1: *mut WINDOW) -> c_int; 343 | //wide: fn wgetch(arg1: *mut WINDOW) -> c_int; 344 | //wide: fn wgetnstr(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: c_int) -> c_int; 345 | //wide: fn wgetstr(arg1: *mut WINDOW, arg2: *mut c_schar) -> c_int; 346 | //wide: fn whline(arg1: *mut WINDOW, arg2: chtype, arg3: c_int) -> c_int; 347 | //wide: fn winch(arg1: *mut WINDOW) -> chtype; 348 | //wide: fn winchnstr(arg1: *mut WINDOW, arg2: *mut chtype, arg3: c_int) -> c_int; 349 | //wide: fn winchstr(arg1: *mut WINDOW, arg2: *mut chtype) -> c_int; 350 | //wide: fn winnstr(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: c_int) -> c_int; 351 | //wide: fn winsch(arg1: *mut WINDOW, arg2: chtype) -> c_int; 352 | pub fn winsdelln(arg1: *mut WINDOW, arg2: c_int) -> c_int; 353 | pub fn winsertln(arg1: *mut WINDOW) -> c_int; 354 | //wide: fn winsnstr(arg1: *mut WINDOW, arg2: *mut c_schar, arg3: c_int) -> c_int; 355 | //wide: fn winsstr(arg1: *mut WINDOW, arg2: *mut c_schar) -> c_int; 356 | //wide: fn winstr(arg1: *mut WINDOW, arg2: *mut c_schar) -> c_int; 357 | pub fn wmove(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 358 | pub fn wnoutrefresh(arg1: *mut WINDOW) -> c_int; 359 | pub fn wprintw(arg1: *mut WINDOW, arg2: *mut c_schar) -> c_int; 360 | pub fn wredrawln(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 361 | pub fn wrefresh(arg1: *mut WINDOW) -> c_int; 362 | pub fn wscanw(arg1: *mut WINDOW, arg2: *mut c_schar) -> c_int; 363 | pub fn wscrl(arg1: *mut WINDOW, arg2: c_int) -> c_int; 364 | pub fn wsetscrreg(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 365 | pub fn wstandout(arg1: *mut WINDOW) -> c_int; 366 | pub fn wstandend(arg1: *mut WINDOW) -> c_int; 367 | pub fn wsyncdown(arg1: *mut WINDOW); 368 | pub fn wsyncup(arg1: *mut WINDOW); 369 | pub fn wtimeout(arg1: *mut WINDOW, arg2: c_int); 370 | pub fn wtouchln(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: c_int) -> c_int; 371 | //wide: fn wvline(arg1: *mut WINDOW, arg2: chtype, arg3: c_int) -> c_int; 372 | pub fn tigetflag(arg1: *const c_schar) -> c_int; 373 | pub fn tigetnum(arg1: *const c_schar) -> c_int; 374 | pub fn tigetstr(arg1: *const c_schar) -> *const c_schar; 375 | pub fn putp(arg1: *const c_schar) -> c_int; 376 | //fn tparm(arg1: *mut c_schar, arg2: c_int, arg3: c_int) -> *mut c_schar; 377 | pub fn tiparm(arg1: *mut c_schar) -> *mut c_schar; 378 | pub fn getattrs(arg1: *mut WINDOW) -> c_int; 379 | pub fn getcurx(arg1: *mut WINDOW) -> c_int; 380 | pub fn getcury(arg1: *mut WINDOW) -> c_int; 381 | pub fn getbegx(arg1: *mut WINDOW) -> c_int; 382 | pub fn getbegy(arg1: *mut WINDOW) -> c_int; 383 | pub fn getmaxx(arg1: *mut WINDOW) -> c_int; 384 | pub fn getmaxy(arg1: *mut WINDOW) -> c_int; 385 | pub fn getparx(arg1: *mut WINDOW) -> c_int; 386 | pub fn getpary(arg1: *mut WINDOW) -> c_int; 387 | pub fn is_term_resized(arg1: c_int, arg2: c_int) -> c_int; 388 | pub fn keybound(arg1: c_int, arg2: c_int) -> *mut c_schar; 389 | pub fn curses_version() -> *mut c_schar; 390 | pub fn assume_default_colors(arg1: c_int, arg2: c_int) -> c_int; 391 | pub fn define_key(arg1: *mut c_schar, arg2: c_int) -> c_int; 392 | pub fn get_escdelay() -> c_int; 393 | pub fn key_defined(arg1: *mut c_schar) -> c_int; 394 | pub fn keyok(arg1: c_int, arg2: c_int) -> c_int; 395 | pub fn resize_term(arg1: c_int, arg2: c_int) -> c_int; 396 | pub fn resizeterm(arg1: c_int, arg2: c_int) -> c_int; 397 | pub fn set_escdelay(arg1: c_int) -> c_int; 398 | pub fn set_tabsize(arg1: c_int) -> c_int; 399 | pub fn use_default_colors() -> c_int; 400 | pub fn use_extended_names(arg1: c_int) -> c_int; 401 | pub fn use_legacy_coding(arg1: c_int) -> c_int; 402 | pub fn use_screen(arg1: *mut SCREEN, arg2: NCURSES_SCREEN_CB, arg3: *mut c_void) -> c_int; 403 | pub fn use_window(arg1: *mut WINDOW, arg2: NCURSES_WINDOW_CB, arg3: *mut c_void) -> c_int; 404 | pub fn wresize(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 405 | pub fn nofilter(); 406 | pub fn wgetparent(arg1: *mut WINDOW) -> *mut WINDOW; 407 | pub fn is_cleared(arg1: *mut WINDOW) -> c_int; 408 | pub fn is_idcok(arg1: *mut WINDOW) -> c_int; 409 | pub fn is_idlok(arg1: *mut WINDOW) -> c_int; 410 | pub fn is_immedok(arg1: *mut WINDOW) -> c_int; 411 | pub fn is_keypad(arg1: *mut WINDOW) -> c_int; 412 | pub fn is_leaveok(arg1: *mut WINDOW) -> c_int; 413 | pub fn is_nodelay(arg1: *mut WINDOW) -> c_int; 414 | pub fn is_notimeout(arg1: *mut WINDOW) -> c_int; 415 | pub fn is_pad(arg1: *mut WINDOW) -> c_int; 416 | pub fn is_scrollok(arg1: *mut WINDOW) -> c_int; 417 | pub fn is_subwin(arg1: *mut WINDOW) -> c_int; 418 | pub fn is_syncok(arg1: *mut WINDOW) -> c_int; 419 | pub fn wgetscrreg(arg1: *mut WINDOW, arg2: *mut c_int, arg3: *mut c_int) -> c_int; 420 | pub fn has_mouse() -> c_int; 421 | pub fn getmouse(arg1: *mut MEVENT) -> c_int; 422 | pub fn ungetmouse(arg1: *mut MEVENT) -> c_int; 423 | pub fn mousemask(arg1: mmask_t, arg2: *mut mmask_t) -> mmask_t; 424 | pub fn wenclose(arg1: *mut WINDOW, arg2: c_int, arg3: c_int) -> c_int; 425 | pub fn mouseinterval(arg1: c_int) -> c_int; 426 | pub fn wmouse_trafo(arg1: *mut WINDOW, arg2: *mut c_int, arg3: *mut c_int, arg4: c_int) -> c_int; 427 | pub fn mouse_trafo(arg1: *mut c_int, arg2: *mut c_int, arg3: c_int) -> c_int; 428 | pub fn mcprint(arg1: *mut c_schar, arg2: c_int) -> c_int; 429 | pub fn has_key(arg1: c_int) -> c_int; 430 | 431 | // Debug only 432 | /* 433 | fn _move(arg1: c_int, arg2: c_int) -> c_int; 434 | fn _tracef(arg1: *mut c_schar); 435 | fn _tracedump(arg1: *mut c_schar, arg2: *mut WINDOW); 436 | fn _traceattr(arg1: attr_t) -> *mut c_schar; 437 | fn _traceattr2(arg1: c_int, arg2: chtype) -> *mut c_schar; 438 | fn _nc_tracebits() -> *mut c_schar; 439 | fn _tracechar(arg1: c_int) -> *mut c_schar; 440 | fn _tracechtype(arg1: chtype) -> *mut c_schar; 441 | fn _tracechtype2(arg1: c_int, arg2: chtype) -> *mut c_schar; 442 | fn _tracemouse(arg1: *mut MEVENT) -> *mut c_schar; 443 | fn trace(arg1: c_uint); 444 | */ 445 | 446 | // Wide-character functions, only available in ncursesw. 447 | // Each of these obsoletes something above, marked with a '//wide:' 448 | pub fn add_wch(arg1: *mut cchar_t) -> c_int; 449 | pub fn add_wchnstr(arg1: *mut cchar_t, arg2: c_int) -> c_int; 450 | pub fn add_wchstr(arg1: *mut cchar_t) -> c_int; 451 | pub fn addnwstr(arg1: *mut wchar_t, arg2: c_int) -> c_int; 452 | pub fn addwstr(arg1: *mut wchar_t) -> c_int; 453 | pub fn bkgrnd(arg1: *mut cchar_t) -> c_int; 454 | pub fn bkgrndset(arg1: *mut cchar_t); 455 | pub fn border_set(arg1: *mut cchar_t, arg2: *mut cchar_t, arg3: *mut cchar_t, arg4: *mut cchar_t, arg5: *mut cchar_t, arg6: *mut cchar_t, arg7: *mut cchar_t, arg8: *mut cchar_t) -> c_int; 456 | pub fn box_set(arg1: *mut WINDOW, arg2: *mut cchar_t, arg3: *mut cchar_t) -> c_int; 457 | pub fn echo_wchar(arg1: *mut cchar_t) -> c_int; 458 | pub fn erasewchar(arg1: *mut wchar_t) -> c_int; 459 | pub fn get_wch(arg1: *mut wint_t) -> c_int; 460 | pub fn get_wstr(arg1: *mut wint_t) -> c_int; 461 | pub fn getbkgrnd(arg1: *mut cchar_t) -> c_int; 462 | pub fn getcchar(arg1: *mut cchar_t, arg2: *mut wchar_t, arg3: *mut attr_t, arg4: *mut c_short, arg5: *mut c_void) -> c_int; 463 | pub fn getn_wstr(arg1: *mut wint_t, arg2: c_int) -> c_int; 464 | pub fn hline_set(arg1: *mut cchar_t, arg2: c_int) -> c_int; 465 | pub fn in_wch(arg1: *mut cchar_t) -> c_int; 466 | pub fn in_wchnstr(arg1: *mut cchar_t, arg2: c_int) -> c_int; 467 | pub fn in_wchstr(arg1: *mut cchar_t) -> c_int; 468 | pub fn innwstr(arg1: *mut wchar_t, arg2: c_int) -> c_int; 469 | pub fn ins_nwstr(arg1: *mut wchar_t, arg2: c_int) -> c_int; 470 | pub fn ins_wch(arg1: *mut cchar_t) -> c_int; 471 | pub fn ins_wstr(arg1: *mut wchar_t) -> c_int; 472 | pub fn inwstr(arg1: *mut wchar_t) -> c_int; 473 | pub fn key_name(arg1: wchar_t) -> *mut c_schar; 474 | pub fn killwchar(arg1: *mut wchar_t) -> c_int; 475 | pub fn mvadd_wch(arg1: c_int, arg2: c_int, arg3: *mut cchar_t) -> c_int; 476 | pub fn mvadd_wchnstr(arg1: c_int, arg2: c_int, arg3: *mut cchar_t, arg4: c_int) -> c_int; 477 | pub fn mvadd_wchstr(arg1: c_int, arg2: c_int, arg3: *mut cchar_t) -> c_int; 478 | pub fn mvaddnwstr(arg1: c_int, arg2: c_int, arg3: *mut wchar_t, arg4: c_int) -> c_int; 479 | pub fn mvaddwstr(arg1: c_int, arg2: c_int, arg3: *mut wchar_t) -> c_int; 480 | pub fn mvget_wch(arg1: c_int, arg2: c_int, arg3: *mut wint_t) -> c_int; 481 | pub fn mvget_wstr(arg1: c_int, arg2: c_int, arg3: *mut wint_t) -> c_int; 482 | pub fn mvgetn_wstr(arg1: c_int, arg2: c_int, arg3: *mut wint_t, arg4: c_int) -> c_int; 483 | pub fn mvhline_set(arg1: c_int, arg2: c_int, arg3: *mut cchar_t, arg4: c_int) -> c_int; 484 | pub fn mvin_wch(arg1: c_int, arg2: c_int, arg3: *mut cchar_t) -> c_int; 485 | pub fn mvin_wchnstr(arg1: c_int, arg2: c_int, arg3: *mut cchar_t, arg4: c_int) -> c_int; 486 | pub fn mvin_wchstr(arg1: c_int, arg2: c_int, arg3: *mut cchar_t) -> c_int; 487 | pub fn mvinnwstr(arg1: c_int, arg2: c_int, arg3: *mut wchar_t, arg4: c_int) -> c_int; 488 | pub fn mvins_nwstr(arg1: c_int, arg2: c_int, arg3: *mut wchar_t, arg4: c_int) -> c_int; 489 | pub fn mvins_wch(arg1: c_int, arg2: c_int, arg3: *mut cchar_t) -> c_int; 490 | pub fn mvins_wstr(arg1: c_int, arg2: c_int, arg3: *mut wchar_t) -> c_int; 491 | pub fn mvinwstr(arg1: c_int, arg2: c_int, arg3: *mut wchar_t) -> c_int; 492 | pub fn mvvline_set(arg1: c_int, arg2: c_int, arg3: *mut cchar_t, arg4: c_int) -> c_int; 493 | pub fn mvwadd_wch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t) -> c_int; 494 | pub fn mvwadd_wchnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t, arg5: c_int) -> c_int; 495 | pub fn mvwadd_wchstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t) -> c_int; 496 | pub fn mvwaddnwstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wchar_t, arg5: c_int) -> c_int; 497 | pub fn mvwaddwstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wchar_t) -> c_int; 498 | pub fn mvwget_wch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wint_t) -> c_int; 499 | pub fn mvwget_wstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wint_t) -> c_int; 500 | pub fn mvwgetn_wstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wint_t, arg5: c_int) -> c_int; 501 | pub fn mvwhline_set(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t, arg5: c_int) -> c_int; 502 | pub fn mvwin_wch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t) -> c_int; 503 | pub fn mvwin_wchnstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t, arg5: c_int) -> c_int; 504 | pub fn mvwin_wchstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t) -> c_int; 505 | pub fn mvwinnwstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wchar_t, arg5: c_int) -> c_int; 506 | pub fn mvwins_nwstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wchar_t, arg5: c_int) -> c_int; 507 | pub fn mvwins_wch(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t) -> c_int; 508 | pub fn mvwins_wstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wchar_t) -> c_int; 509 | pub fn mvwinwstr(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut wchar_t) -> c_int; 510 | pub fn mvwvline_set(arg1: *mut WINDOW, arg2: c_int, arg3: c_int, arg4: *mut cchar_t, arg5: c_int) -> c_int; 511 | pub fn pecho_wchar(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 512 | pub fn setcchar(arg1: *mut cchar_t, arg2: *mut wchar_t, arg3: attr_t, arg4: c_short, arg5: *mut c_void) -> c_int; 513 | pub fn slk_wset(arg1: c_int, arg2: *mut wchar_t, arg3: c_int) -> c_int; 514 | pub fn term_attrs() -> attr_t; 515 | pub fn unget_wch(arg1: wchar_t) -> c_int; 516 | pub fn vid_attr(arg1: attr_t, arg2: c_short, arg3: *mut c_void) -> c_int; 517 | pub fn vid_puts(arg1: attr_t, arg2: c_short, arg3: *mut c_void, arg4: NCURSES_OUTC) -> c_int; 518 | pub fn vline_set(arg1: *mut cchar_t, arg2: c_int) -> c_int; 519 | pub fn wadd_wch(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 520 | pub fn wadd_wchnstr(arg1: *mut WINDOW, arg2: *mut cchar_t, arg3: c_int) -> c_int; 521 | pub fn wadd_wchstr(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 522 | pub fn waddnwstr(arg1: *mut WINDOW, arg2: *mut wchar_t, arg3: c_int) -> c_int; 523 | pub fn waddwstr(arg1: *mut WINDOW, arg2: *mut wchar_t) -> c_int; 524 | pub fn wbkgrnd(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 525 | pub fn wbkgrndset(arg1: *mut WINDOW, arg2: *mut cchar_t); 526 | pub fn wborder_set(arg1: *mut WINDOW, arg2: *mut cchar_t, arg3: *mut cchar_t, arg4: *mut cchar_t, arg5: *mut cchar_t, arg6: *mut cchar_t, arg7: *mut cchar_t, arg8: *mut cchar_t, arg9: *mut cchar_t) -> c_int; 527 | pub fn wecho_wchar(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 528 | pub fn wget_wch(arg1: *mut WINDOW, arg2: *mut wint_t) -> c_int; 529 | pub fn wget_wstr(arg1: *mut WINDOW, arg2: *mut wint_t) -> c_int; 530 | pub fn wgetbkgrnd(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 531 | pub fn wgetn_wstr(arg1: *mut WINDOW, arg2: *mut wint_t, arg3: c_int) -> c_int; 532 | pub fn whline_set(arg1: *mut WINDOW, arg2: *mut cchar_t, arg3: c_int) -> c_int; 533 | pub fn win_wch(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 534 | pub fn win_wchnstr(arg1: *mut WINDOW, arg2: *mut cchar_t, arg3: c_int) -> c_int; 535 | pub fn win_wchstr(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 536 | pub fn winnwstr(arg1: *mut WINDOW, arg2: *mut wchar_t, arg3: c_int) -> c_int; 537 | pub fn wins_nwstr(arg1: *mut WINDOW, arg2: *mut wchar_t, arg3: c_int) -> c_int; 538 | pub fn wins_wch(arg1: *mut WINDOW, arg2: *mut cchar_t) -> c_int; 539 | pub fn wins_wstr(arg1: *mut WINDOW, arg2: *mut wchar_t) -> c_int; 540 | pub fn winwstr(arg1: *mut WINDOW, arg2: *mut wchar_t) -> c_int; 541 | pub fn wunctrl(arg1: *mut cchar_t) -> *mut wchar_t; 542 | pub fn wvline_set(arg1: *mut WINDOW, arg2: *mut cchar_t, arg3: c_int) -> c_int; 543 | } 544 | 545 | 546 | //////////////////////////////////////////////////////////////////////////////// 547 | // Manually-ported preprocessor stuff 548 | 549 | pub static COLOR_BLACK: c_short = 0; 550 | pub static COLOR_RED: c_short = 1; 551 | pub static COLOR_GREEN: c_short = 2; 552 | pub static COLOR_YELLOW: c_short = 3; 553 | pub static COLOR_BLUE: c_short = 4; 554 | pub static COLOR_MAGENTA: c_short = 5; 555 | pub static COLOR_CYAN: c_short = 6; 556 | pub static COLOR_WHITE: c_short = 7; 557 | 558 | pub const NCURSES_ATTR_SHIFT: c_int = 8; 559 | pub static A_NORMAL: c_int = 0; 560 | //#define A_ATTRIBUTES NCURSES_BITS(~(1U - 1U),0) 561 | //#define A_CHARTEXT (NCURSES_BITS(1U,0) - 1U) 562 | pub static A_COLOR: c_int = ((1 << 8) - 1) << (NCURSES_ATTR_SHIFT); 563 | pub static A_STANDOUT: c_int = 1 << (NCURSES_ATTR_SHIFT + 8); 564 | pub static A_UNDERLINE: c_int = 1 << (NCURSES_ATTR_SHIFT + 9); 565 | pub static A_REVERSE: c_int = 1 << (NCURSES_ATTR_SHIFT + 10); 566 | pub static A_BLINK: c_int = 1 << (NCURSES_ATTR_SHIFT + 11); 567 | pub static A_DIM: c_int = 1 << (NCURSES_ATTR_SHIFT + 12); 568 | pub static A_BOLD: c_int = 1 << (NCURSES_ATTR_SHIFT + 13); 569 | 570 | pub static KEY_DOWN: wint_t = 258; 571 | pub static KEY_UP: wint_t = 259; 572 | pub static KEY_LEFT: wint_t = 260; 573 | pub static KEY_RIGHT: wint_t = 261; 574 | 575 | pub static KEY_F0: wint_t = 264; 576 | 577 | pub fn KEY_F(n: wint_t) -> wint_t { 578 | return KEY_F0 + n; 579 | } 580 | 581 | 582 | // Return values 583 | pub static ERR: c_int = -1; 584 | pub static OK: c_int = 0; 585 | 586 | pub static KEY_CODE_YES: c_int = 256; 587 | 588 | --------------------------------------------------------------------------------