aboutsummaryrefslogtreecommitdiffstats
path: root/src/info.rs
blob: 8056180da159609d2ed8f64dec4a43dcc1ec40e2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
use core::libc::{c_char,c_int};

/// The default colors available on a terminal emulator.
#[deriving(Eq)]
pub enum Color {
    ColorBlack = 0,
    ColorRed,
    ColorGreen,
    ColorYellow,
    ColorBlue,
    ColorMagenta,
    ColorCyan,
    ColorWhite,
}

/**
 * Initialize the terminfo database.
 *
 * This must be called before any functions from this module are used. The
 * current terminal is determined by looking at the `TERM` environment
 * variable.
 */
pub fn init () {
    unsafe { c::setupterm(ptr::null(), 1, ptr::null()) };
}

macro_rules! def_escape(
    ($name:ident -> $escape:expr) => (
        pub fn $name () -> ~str {
            let attr = $escape;
            match escape(attr) {
                Some(e) => e,
                None    => fail!(fmt!("%s is not supported on this terminal",
                                      attr)),
            }
        }
    );
    ($name:ident -> $escape:expr, $ty1:ident) => (
        pub fn $name (p1: $ty1) -> ~str {
            let attr = $escape;
            match escape1(attr, p1 as int) {
                Some(e) => e,
                None    => fail!(fmt!("%s is not supported on this terminal",
                                      attr)),
            }
        }
    );
    ($name:ident -> $escape:expr, $ty1:ident, $ty2:ident) => (
        pub fn $name (p1: $ty1, p2: $ty2) -> ~str {
            let attr = $escape;
            match escape2(attr, p1 as int, p2 as int) {
                Some(e) => e,
                None    => fail!(fmt!("%s is not supported on this terminal",
                                      attr)),
            }
        }
    );
)

// XXX macros can't take attributes yet (including documentation), so change
// these to /// once that is fixed

// The terminal escape to clear the screen.
def_escape!(clear_screen         -> "clear")
// The terminal escape to set the foreground color to `p1`.
def_escape!(set_a_foreground     -> "setaf", Color)
// The terminal escape to set the background color to `p1`.
def_escape!(set_a_background     -> "setab", Color)
// The terminal escape to reset the foreground and background colors.
def_escape!(orig_pair            -> "op")
// The terminal escape to reset all attributes.
def_escape!(exit_attribute_mode  -> "sgr0")
// The terminal escape to move the cursor to the top left of the screen.
def_escape!(cursor_home          -> "home")
// The terminal escape to move the cursor to (`p1`, `p2`).
def_escape!(cursor_address       -> "cup", uint, uint)
// The terminal escape to enable underline mode.
def_escape!(enter_underline_mode -> "smul")
// The terminal escape to disable underline mode.
def_escape!(exit_underline_mode  -> "rmul")
// The terminal escape to enable standout mode.
def_escape!(enter_standout_mode  -> "smso")
// The terminal escape to disable standout mode.
def_escape!(exit_standout_mode   -> "rmso")
// The terminal escape to enable reverse video mode.
def_escape!(enter_reverse_mode   -> "rev")
// The terminal escape to enable bold mode.
def_escape!(enter_bold_mode      -> "bold")
// The terminal escape to enable blink mode.
def_escape!(enter_blink_mode     -> "blink")
// The terminal escape to make the cursor invisible.
def_escape!(cursor_invisible     -> "civis")
// The terminal escape to make the cursor visible.
def_escape!(cursor_normal        -> "cnorm")
// The terminal escape to enable the alternate screen.
def_escape!(enter_ca_mode        -> "smcup")
// The terminal escape to disable the alternate screen.
def_escape!(exit_ca_mode         -> "rmcup")
// The terminal escape to enter keypad mode.
def_escape!(keypad_xmit          -> "smkx")
// The terminal escape to leave keypad mode.
def_escape!(keypad_local         -> "rmkx")

// The terminal escape generated by the backspace key.
def_escape!(key_backspace   -> "kbs")
// The terminal escape generated by the return key.
def_escape!(carriage_return -> "cr")
// The terminal escape generated by the tab key.
def_escape!(tab             -> "ht")
// The terminal escape generated by the up arrow key.
def_escape!(key_up          -> "kcuu1")
// The terminal escape generated by the down arrow key.
def_escape!(key_down        -> "kcud1")
// The terminal escape generated by the left arrow key.
def_escape!(key_left        -> "kcub1")
// The terminal escape generated by the right arrow key.
def_escape!(key_right       -> "kcuf1")
// The terminal escape generated by the home key.
def_escape!(key_home        -> "khome")
// The terminal escape generated by the end key.
def_escape!(key_end         -> "kend")
// The terminal escape generated by the insert key.
def_escape!(key_ic          -> "kich1")
// The terminal escape generated by the delete key.
def_escape!(key_dc          -> "kdch1")
// The terminal escape generated by the F1 key.
def_escape!(key_f1          -> "kf1")
// The terminal escape generated by the F2 key.
def_escape!(key_f2          -> "kf2")
// The terminal escape generated by the F3 key.
def_escape!(key_f3          -> "kf3")
// The terminal escape generated by the F4 key.
def_escape!(key_f4          -> "kf4")
// The terminal escape generated by the F5 key.
def_escape!(key_f5          -> "kf5")
// The terminal escape generated by the F6 key.
def_escape!(key_f6          -> "kf6")
// The terminal escape generated by the F7 key.
def_escape!(key_f7          -> "kf7")
// The terminal escape generated by the F8 key.
def_escape!(key_f8          -> "kf8")
// The terminal escape generated by the F9 key.
def_escape!(key_f9          -> "kf9")
// The terminal escape generated by the F10 key.
def_escape!(key_f10         -> "kf10")
// The terminal escape generated by the F11 key.
def_escape!(key_f11         -> "kf11")
// The terminal escape generated by the F12 key.
def_escape!(key_f12         -> "kf12")

/// The terminal escape generated by the F<`n`> key.
pub fn key_f (n: uint) -> ~str {
    let attr = fmt!("kf%?", n);
    match escape(attr) {
        Some(e) => e,
        None    => fail!(fmt!("%s is not supported on this terminal", attr)),
    }
}

/// The terminal escape corresponding to the `name` terminfo capability.
pub fn escape (name: &str) -> Option<~str> {
    do str::as_c_str(name) |c_name| {
        unsafe {
            let e = tigetstr(c_name);
            if e == ptr::null() {
                None
            }
            else {
                Some(str::raw::from_c_str(e))
            }
        }
    }
}

/**
 * The terminal escape corresponding to the `name` terminfo capability.
 *
 * This capability must take one parameter, which should be passed as `p1`.
 */
pub fn escape1 (name: &str, p1: int) -> Option<~str> {
    do str::as_c_str(name) |c_name| {
        unsafe {
            let e = tigetstr(c_name);
            if e == ptr::null() {
                None
            }
            else {
                Some(str::raw::from_c_str(tiparm1(e, p1)))
            }
        }
    }
}

/**
 * The terminal escape corresponding to the `name` terminfo capability.
 *
 * This capability must take two parameters, which should be passed as `p1`
 * and `p2`.
 */
pub fn escape2 (name: &str, p1: int, p2: int) -> Option<~str> {
    do str::as_c_str(name) |c_name| {
        unsafe {
            let e = tigetstr(c_name);
            if e == ptr::null() {
                None
            }
            else {
                Some(str::raw::from_c_str(tiparm2(e, p1, p2)))
            }
        }
    }
}

unsafe fn tigetstr (name: *c_char) -> *c_char {
    let c_out = c::tigetstr(name);
    if c_out as int == -1 {
        fail!(fmt!("%s is not a terminal capability",
                   unsafe { str::raw::from_c_str(name) }));
    }
    c_out
}

unsafe fn tiparm1 (name: *c_char, p1: int) -> *c_char {
    let ret = helper::tiparm1(name, p1 as c_int);
    if ret == ptr::null() {
        fail!(fmt!("Couldn't assemble parameters with %s %d",
                   unsafe { str::raw::from_c_str(name) }, p1));
    }
    ret
}

unsafe fn tiparm2 (name: *c_char, p1: int, p2: int) -> *c_char {
    let ret = helper::tiparm2(name, p1 as c_int, p2 as c_int);
    if ret == ptr::null() {
        fail!(fmt!("Couldn't assemble parameters with %s %d %d",
                   unsafe { str::raw::from_c_str(name) }, p1, p2));
    }
    ret
}

#[link_name = "curses"]
extern mod c {
    fn setupterm (term: *c_char, fd: c_int, errret: *c_int) -> c_int;
    fn tigetstr (s: *c_char) -> *c_char;
}

// tiparm uses varargs, which you can't bind from rust yet
// actually, you sort of probably can? there's just an llvm assertion that
// prevents multiple bindings to the same function from working (rust/#5791)
#[link_name = "curses_helper"]
extern mod helper {
    fn tiparm1(s: *c_char, p1: c_int) -> *c_char;
    fn tiparm2(s: *c_char, p1: c_int, p2: c_int) -> *c_char;
}