bear_lib_terminal/terminal/
mod.rs

1//! Rusticized interface for the FFI.
2
3
4mod input;
5pub mod config;
6pub mod state;
7
8use std::char;
9use colors::Color;
10use geometry::{Rect, Point, Size};
11use self::config::{ConfigPart, Window};
12use bear_lib_terminal_sys as ffi;
13use bear_lib_terminal_sys::ColorT;
14
15pub use self::input::{Event, KeyCode};
16
17
18/// Creates the terminal window of the specified size with the specified title, without showing it.
19/// To show the window use the [`refresh()`](fn.refresh.html) function.
20///
21/// Equivalent to the [`terminal_open()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#open) with a subsequent call to
22/// the [`terminal_set()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#set) with the title.
23pub fn open(title: &str, width: u32, height: u32) {
24	ffi::open();
25	set(Window::empty().size(Size::new(width as i32, height as i32)).title(title.to_string()));
26}
27
28/// Closes the terminal window, causing all subsequent functions from the module (apart from [`open()`](fn.open.html)) to fail
29///
30/// Equivalent to the [`terminal_close()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#close).
31pub fn close() {
32	ffi::close();
33}
34
35/// Invoke the [`terminal_set()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#set) with the argument's `config_str`.
36///
37/// Returns `false` iff the config string is malformed.
38///
39/// For build-in [`ConfigPart`](config/trait.ConfigPart.html)s see the [`config`](config/index.html) module.
40pub fn set<T: ConfigPart>(cfg: T) -> bool {
41	ffi::set(&*&cfg.to_config_str())
42}
43
44/// Flushes all changes made to the screen; also shows the window after the [`open()`](fn.open.html) call
45///
46/// Equivalent to the [`terminal_refresh()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#refresh).
47pub fn refresh() {
48	ffi::refresh();
49}
50
51/// Clears the screen (either partailly or fully)
52///
53/// If `area` is `None`, clears the entire screen, all layers
54/// (identically to the [`terminal_clear()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#clear).
55///
56/// Otherwise clears specified area on the current layer, as would the
57/// [`terminal_clear_area()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#clear_area).
58pub fn clear(area: Option<Rect>) {
59	match area {
60		Some(rect) => ffi::clear_area(rect.top_left.x, rect.top_left.y, rect.size.width, rect.size.height),
61		None       => ffi::clear(),
62	}
63}
64
65/// Sets the current layer's crop area.
66///
67/// <sub>I don't get it either, refer the [`terminal_crop()` C API function's documentation](http://foo.wyrd.name/en:bearlibterminal:reference#crop).</sub>
68pub fn crop(rect: Rect) {
69	ffi::crop(rect.top_left.x, rect.top_left.y, rect.size.width, rect.size.height);
70}
71
72/// Selects the current layer.
73///
74/// The layer `index` must be between 0 and 255.
75/// For more information consult the documentation for the [`terminal_layer()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#layer).
76pub fn layer(index: i32) {
77	ffi::layer(index);
78}
79
80/// Sets the current foreground color, which will affect all the output functions called later.
81///
82/// This is equivalent to the [`terminal_color()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#color).
83pub fn set_foreground(color: Color) {
84	ffi::color(to_color_t(color));
85}
86
87/// Sets the foreground color before calling the function and resets it afterwards.
88pub fn with_foreground<F: FnOnce()>(color: Color, callback: F) {
89	let current = ffi::state_color(ffi::TK_COLOR);
90	set_foreground(color);
91	callback();
92	ffi::color(current);
93}
94
95/// Sets the current background color, which will affect all the output functions called later.
96///
97/// This is equivalent to the [`terminal_bkcolor()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#bkcolor).
98pub fn set_background(color: Color) {
99	ffi::bkcolor(to_color_t(color));
100}
101
102/// Sets the background color before calling the function and resets it afterwards.
103pub fn with_background<F: FnOnce()>(color: Color, callback: F) {
104	let current = ffi::state_color(ffi::TK_BKCOLOR);
105	set_background(color);
106	callback();
107	ffi::bkcolor(current);
108}
109
110/// Sets the current foreground and background color, which will affect all the output functions called later.
111///
112/// This is equivalent to calling [`set_background()`](fn.set_background.html) and [`set_foreground()`](fn.set_foreground.html) in succession.
113pub fn set_colors(fg: Color, bg: Color) {
114	set_foreground(fg);
115	set_background(bg);
116}
117
118/// Sets the foreground and background color before calling the function and resets them afterwards.
119pub fn with_colors<F: FnOnce()>(fg: Color, bg: Color, callback: F) {
120	with_foreground(fg, ||
121		with_background(bg, ||
122			callback()
123		)
124	);
125}
126
127/// Enable or disable composition, (dis)allowing for "stacking" tiles on top of each other in the same cell.
128///
129/// For details and other uses consult the documentation for the
130/// [`terminal_composition()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#composition).
131pub fn composition(enable: bool) {
132	ffi::composition(enable);
133}
134
135/// Prints the specified character to the specified location.
136///
137/// Equivalent to the [`terminal_put()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#put).
138pub fn put(point: Point, cell: char) {
139	ffi::put(point.x, point.y, cell as i32);
140}
141
142/// Equivalent to [`put()`](fn.put.html) with a `Point` constructed from the first two arguments.
143pub fn put_xy(x: i32, y: i32, cell: char) {
144	ffi::put(x, y, cell as i32);
145}
146
147/// Prints the specified character to the specified pixel-offsetted location, gradient-colouring it from the corners.
148///
149/// For details see the docs for the [`terminal_put_ext()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#put_ext).
150pub fn put_ext(pos: Point, offset: Point, cell: char, corners: &Vec<Color>) {
151	ffi::put_ext(pos.x, pos.y, offset.x, offset.y, cell as i32, &corners.iter().cloned().map(to_color_t).collect::<Vec<_>>()[..]);
152}
153
154/// Get the character in the specified coordinates on the specified layer.
155///
156/// Returns 0 if the cell is empty on the specified layer.
157///
158/// Consult the documentation for the [`terminal_pick()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#pick) for more data.
159pub fn pick(point: Point, index: i32) -> char {
160	char::from_u32(ffi::pick(point.x, point.y, index) as u32).unwrap()
161}
162
163/// Get the color of the character in the specified coordinates on the specified layer.
164///
165/// Consult the documentation for the [`terminal_pick_color()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#pick_color),
166/// despite its laconicity.
167pub fn pick_foreground_color(point: Point, index: i32) -> Color {
168	from_color_t(ffi::pick_color(point.x, point.y, index))
169}
170
171/// Get the background color in the specified coordinates.
172///
173/// Consult the documentation for the [`terminal_pick_bkcolor()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#pick_bkcolor)
174/// for the same amount of information.
175pub fn pick_background_color(point: Point) -> Color {
176	from_color_t(ffi::pick_bkcolor(point.x, point.y))
177}
178
179/// Prints the specified string to the specified location, formatting it along the way.
180///
181/// For formatting spec see the docs for the [`terminal_print()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#print).
182pub fn print(point: Point, value: &str) {
183	let _ = ffi::print(point.x, point.y, value);
184}
185
186/// Equivalent to [`print()`](fn.print.html) with a `Point` constructed from the first two arguments.
187pub fn print_xy(x: i32, y: i32, value: &str) {
188	print(Point::new(x, y), value);
189}
190
191/// Calculate the argument's width/height without printing it.
192///
193/// Whether the function returns the width or the height depends on the presence of the `bbox` tag in the string.
194///
195/// Refer to the [docs for the `terminal_measure()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#measure), note,
196/// that the return type therein is incorrect.
197pub fn measure(value: &str) -> i32 {
198	ffi::measure(value)
199}
200
201/// Check, whether the next [`read_event()`](fn.read_event.html) call will return `Some`.
202///
203/// Consult the [documentation for the `terminal_has_input()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#has_input).
204pub fn has_input() -> bool {
205	ffi::has_input()
206}
207
208/// Returns the next event, blocks until one's available.
209///
210/// This is equivalent to the [`terminal_read()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#read).
211pub fn wait_event() -> Option<Event> {
212	to_event(ffi::read())
213}
214
215/// Returns an instance of [`EventIterator`](struct.EventIterator.html), an infinite iterator over Terminal events.
216pub fn events() -> EventIterator {
217	EventIterator
218}
219
220/// Returns the next event in the queue if it's available, otherwise returns `None`.
221///
222/// If one intends on waiting for events, the [`wait_event()`](fn.wait_event.html) function is recommended.
223///
224/// This is equivalent to the behaviour mentioned in the
225/// [docs for the `terminal_read()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#read), but not the function's behaviour itself.
226pub fn read_event() -> Option<Event> {
227	if !has_input() {
228		None
229	} else {
230		wait_event()
231	}
232}
233
234/// Returns the next event in the queue if it's available without popping it therefrom, otherwise returns `None`.
235///
236/// If one intends on waiting for events, the [`wait_event()`](fn.wait_event.html) function is recommended.
237///
238/// If one intends on popping the events, the [`read_event()`](fn.read_event.html) function is recommended.
239///
240/// If one intends on just checking if an event is ready, the [`has_input()`](fn.has_input.html) function is recommended.
241///
242/// This is equivalent to the [`terminal_peek()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#peek).
243pub fn peek_event() -> Option<Event> {
244	match ffi::peek() {
245		0 => None,
246		event => to_event(event),
247	}
248}
249
250/// Reads up to `max` characters without parsing, starting at the specified coordinates.
251///
252/// Returns `None` if the user closed the window or pressed `Escape`,
253/// `Some` containing the read string otherwise.
254///
255/// The read string will contain up to `max` characters.
256///
257/// The string being read will be kept on-screen only *during* the reading process, the scene will be restored before returning (needs a refresh, though).
258///
259/// Refer to the [documentation for the for the `terminal_read_str()` C API function](http://foo.wyrd.name/en:bearlibterminal:reference#read_str)
260/// for specific information.
261pub fn read_str(point: Point, max: i32) -> Option<String> {
262	ffi::read_str(point.x, point.y, max)
263}
264
265/// Sleep for the specified amount of milliseconds.
266///
267/// See the [`terminal_delay()` C API function's documentation](http://foo.wyrd.name/en:bearlibterminal:reference#delay).
268pub fn delay(period: i32) {
269	ffi::delay(period)
270}
271
272
273/// Infinite iterator over Terminal events, instantiated by [`events()`](fn.events.html).
274///
275/// Yields `None` iff BLT is closed.
276///
277/// # Examples
278///
279/// The "standard" way:
280///
281/// ```ignore
282/// while let Some(event) = terminal::wait_event() {
283/// 	// ...
284/// }
285/// ```
286///
287/// Is equivalent to, but not as good as:
288///
289/// ```ignore
290/// for event in terminal::events() {
291/// 	// ...
292/// }
293/// ```
294pub struct EventIterator;
295
296impl Iterator for EventIterator {
297	type Item = Event;
298
299	fn next(&mut self) -> Option<Event> {
300		wait_event()
301	}
302}
303
304
305fn from_color_t(color: ColorT) -> Color {
306	let alpha = ((color >> 24) & 0xFF) as u8;
307	let red   = ((color >> 16) & 0xFF) as u8;
308	let green = ((color >> 8)  & 0xFF) as u8;
309	let blue  = (color         & 0xFF) as u8;
310
311	Color::from_rgba(red, green, blue, alpha)
312}
313
314fn to_color_t(color: Color) -> ColorT {
315	(
316		((color.alpha as ColorT) << 24) |
317		((color.red   as ColorT) << 16) |
318		((color.green as ColorT) << 8)  |
319		(color.blue   as ColorT)
320	)
321}
322
323fn to_keycode(code: i32) -> Option<KeyCode> {
324	match code {
325		ffi::TK_A            => Some(KeyCode::A),
326		ffi::TK_B            => Some(KeyCode::B),
327		ffi::TK_C            => Some(KeyCode::C),
328		ffi::TK_D            => Some(KeyCode::D),
329		ffi::TK_E            => Some(KeyCode::E),
330		ffi::TK_F            => Some(KeyCode::F),
331		ffi::TK_G            => Some(KeyCode::G),
332		ffi::TK_H            => Some(KeyCode::H),
333		ffi::TK_I            => Some(KeyCode::I),
334		ffi::TK_J            => Some(KeyCode::J),
335		ffi::TK_K            => Some(KeyCode::K),
336		ffi::TK_L            => Some(KeyCode::L),
337		ffi::TK_M            => Some(KeyCode::M),
338		ffi::TK_N            => Some(KeyCode::N),
339		ffi::TK_O            => Some(KeyCode::O),
340		ffi::TK_P            => Some(KeyCode::P),
341		ffi::TK_Q            => Some(KeyCode::Q),
342		ffi::TK_R            => Some(KeyCode::R),
343		ffi::TK_S            => Some(KeyCode::S),
344		ffi::TK_T            => Some(KeyCode::T),
345		ffi::TK_U            => Some(KeyCode::U),
346		ffi::TK_V            => Some(KeyCode::V),
347		ffi::TK_W            => Some(KeyCode::W),
348		ffi::TK_X            => Some(KeyCode::X),
349		ffi::TK_Y            => Some(KeyCode::Y),
350		ffi::TK_Z            => Some(KeyCode::Z),
351		ffi::TK_1            => Some(KeyCode::Row1),
352		ffi::TK_2            => Some(KeyCode::Row2),
353		ffi::TK_3            => Some(KeyCode::Row3),
354		ffi::TK_4            => Some(KeyCode::Row4),
355		ffi::TK_5            => Some(KeyCode::Row5),
356		ffi::TK_6            => Some(KeyCode::Row6),
357		ffi::TK_7            => Some(KeyCode::Row7),
358		ffi::TK_8            => Some(KeyCode::Row8),
359		ffi::TK_9            => Some(KeyCode::Row9),
360		ffi::TK_0            => Some(KeyCode::Row0),
361		ffi::TK_ENTER        => Some(KeyCode::Enter),
362		ffi::TK_ESCAPE       => Some(KeyCode::Escape),
363		ffi::TK_BACKSPACE    => Some(KeyCode::Backspace),
364		ffi::TK_TAB          => Some(KeyCode::Tab),
365		ffi::TK_SPACE        => Some(KeyCode::Space),
366		ffi::TK_MINUS        => Some(KeyCode::Minus),
367		ffi::TK_EQUALS       => Some(KeyCode::Equals),
368		ffi::TK_LBRACKET     => Some(KeyCode::LeftBracket),
369		ffi::TK_RBRACKET     => Some(KeyCode::RightBracket),
370		ffi::TK_BACKSLASH    => Some(KeyCode::Backslash),
371		ffi::TK_SEMICOLON    => Some(KeyCode::Semicolon),
372		ffi::TK_APOSTROPHE   => Some(KeyCode::Apostrophe),
373		ffi::TK_GRAVE        => Some(KeyCode::Grave),
374		ffi::TK_COMMA        => Some(KeyCode::Comma),
375		ffi::TK_PERIOD       => Some(KeyCode::Period),
376		ffi::TK_SLASH        => Some(KeyCode::Slash),
377		ffi::TK_F1           => Some(KeyCode::F1),
378		ffi::TK_F2           => Some(KeyCode::F2),
379		ffi::TK_F3           => Some(KeyCode::F3),
380		ffi::TK_F4           => Some(KeyCode::F4),
381		ffi::TK_F5           => Some(KeyCode::F5),
382		ffi::TK_F6           => Some(KeyCode::F6),
383		ffi::TK_F7           => Some(KeyCode::F7),
384		ffi::TK_F8           => Some(KeyCode::F8),
385		ffi::TK_F9           => Some(KeyCode::F9),
386		ffi::TK_F10          => Some(KeyCode::F10),
387		ffi::TK_F11          => Some(KeyCode::F11),
388		ffi::TK_F12          => Some(KeyCode::F12),
389		ffi::TK_PAUSE        => Some(KeyCode::Pause),
390		ffi::TK_INSERT       => Some(KeyCode::Insert),
391		ffi::TK_HOME         => Some(KeyCode::Home),
392		ffi::TK_PAGEUP       => Some(KeyCode::PageUp),
393		ffi::TK_DELETE       => Some(KeyCode::Delete),
394		ffi::TK_END          => Some(KeyCode::End),
395		ffi::TK_PAGEDOWN     => Some(KeyCode::PageDown),
396		ffi::TK_RIGHT        => Some(KeyCode::Right),
397		ffi::TK_LEFT         => Some(KeyCode::Left),
398		ffi::TK_DOWN         => Some(KeyCode::Down),
399		ffi::TK_UP           => Some(KeyCode::Up),
400		ffi::TK_KP_DIVIDE    => Some(KeyCode::NumDivide),
401		ffi::TK_KP_MULTIPLY  => Some(KeyCode::NumMultiply),
402		ffi::TK_KP_MINUS     => Some(KeyCode::NumMinus),
403		ffi::TK_KP_PLUS      => Some(KeyCode::NumPlus),
404		ffi::TK_KP_ENTER     => Some(KeyCode::NumEnter),
405		ffi::TK_KP_1         => Some(KeyCode::Num1),
406		ffi::TK_KP_2         => Some(KeyCode::Num2),
407		ffi::TK_KP_3         => Some(KeyCode::Num3),
408		ffi::TK_KP_4         => Some(KeyCode::Num4),
409		ffi::TK_KP_5         => Some(KeyCode::Num5),
410		ffi::TK_KP_6         => Some(KeyCode::Num6),
411		ffi::TK_KP_7         => Some(KeyCode::Num7),
412		ffi::TK_KP_8         => Some(KeyCode::Num8),
413		ffi::TK_KP_9         => Some(KeyCode::Num9),
414		ffi::TK_KP_0         => Some(KeyCode::Num0),
415		ffi::TK_KP_PERIOD    => Some(KeyCode::NumPeriod),
416		ffi::TK_MOUSE_LEFT   => Some(KeyCode::MouseLeft),
417		ffi::TK_MOUSE_RIGHT  => Some(KeyCode::MouseRight),
418		ffi::TK_MOUSE_MIDDLE => Some(KeyCode::MouseMiddle),
419		ffi::TK_MOUSE_X1     => Some(KeyCode::MouseFourth),
420		ffi::TK_MOUSE_X2     => Some(KeyCode::MouseFifth),
421		_                    => None,
422	}
423}
424
425fn to_event(code: i32) -> Option<Event> {
426	match code {
427		ffi::TK_CLOSE        => Some(Event::Close),
428		ffi::TK_RESIZED      => Some(get_window_resize()),
429		ffi::TK_MOUSE_MOVE   => Some(get_mouse_move()),
430		ffi::TK_MOUSE_SCROLL => Some(get_mouse_scroll()),
431		_                    => to_key_event(code),
432	}
433}
434
435fn to_key_event(code: i32) -> Option<Event> {
436	let key      = code & !ffi::TK_KEY_RELEASED;
437	let released = (code & ffi::TK_KEY_RELEASED) == ffi::TK_KEY_RELEASED;
438
439	match key {
440		ffi::TK_SHIFT   => Some(if released {Event::ShiftReleased}   else {Event::ShiftPressed}),
441		ffi::TK_CONTROL => Some(if released {Event::ControlReleased} else {Event::ControlPressed}),
442		ffi::TK_ALT     => Some(if released {Event::AltReleased}     else {Event::AltPressed}),
443		key             => {
444			let ctrl  = ffi::check(ffi::TK_CONTROL);
445			let shift = ffi::check(ffi::TK_SHIFT);
446
447			match to_keycode(key) {
448				Some(converted) => Some(get_key(released, converted, ctrl, shift)),
449				None            => None,
450			}
451		}
452	}
453}
454
455fn get_window_resize() -> Event {
456	Event::Resize{
457		width: ffi::state(ffi::TK_WIDTH),
458		height: ffi::state(ffi::TK_HEIGHT),
459	}
460}
461
462fn get_mouse_move() -> Event {
463	Event::MouseMove{
464		x: ffi::state(ffi::TK_MOUSE_X),
465		y: ffi::state(ffi::TK_MOUSE_Y),
466	}
467}
468
469fn get_mouse_scroll() -> Event {
470	Event::MouseScroll{
471		delta: ffi::state(ffi::TK_MOUSE_WHEEL),
472	}
473}
474
475fn get_key(released: bool, key: KeyCode, ctrl: bool, shift: bool) -> Event {
476	if released {
477		Event::KeyReleased{
478			key: key,
479			ctrl: ctrl,
480			shift: shift,
481		}
482	} else {
483		Event::KeyPressed{
484			key: key,
485			ctrl: ctrl,
486			shift: shift,
487		}
488	}
489}