pancurses_result/curses.rs
1use color::Color;
2use general::*;
3use initialize::end_window;
4use std::sync::Mutex;
5use std::time::Duration;
6use window::Window;
7
8/// The visibility of the cursor
9#[repr(i32)]
10pub enum CursorVisibility {
11 Invisible = 0,
12 Normal,
13 HighlyVisible,
14}
15
16/// A number of bits per second
17pub struct BitsPerSecond {
18 bps: i32,
19}
20impl BitsPerSecond {
21 pub fn new(bps: i32) -> Self {
22 BitsPerSecond { bps }
23 }
24
25 pub fn bits_per_second(&self) -> i32 {
26 self.bps
27 }
28}
29
30/// The input buffering mode.
31///
32/// This allows us to control which of `raw` and `cbreak` modes are applied.
33pub enum InputBufferingMode {
34 /// This is the default and means that curses buffers input until a new line
35 /// is read.
36 ///
37 /// This corresponds to no `cbreak` mode and no `raw` mode.
38 Buffered,
39 /// Allow for unbuffered input while generating signals upon seeing control
40 /// characters.
41 ///
42 /// For example, this allows `Control+C` to cause the program to exit.
43 ///
44 /// This corresponds to `cbreak` mode without `raw` mode.
45 UnbufferedWithSignals,
46 /// Allow for unbuffered input without interpreting the meaning of any keys.
47 ///
48 /// For example, this allows `Control+C` to be typed *without* forcing the
49 /// program to exit.
50 ///
51 /// This corresponds to `cbreak` mode and `raw` mode.
52 UnbufferedNoSignals,
53}
54
55/// The curses instance. To initialize the curses instance, call [`initscr`].
56///
57/// Many curses functions have been renamed for one reason or another. All
58/// renamed functions state the curses function they corollate to.
59///
60/// [`initscr`]: fn.initscr.html
61pub struct Curses {
62 window: Window,
63 key_name_mutex: Mutex<()>,
64 color: Option<Color>,
65}
66
67impl Curses {
68 pub(crate) fn new(window: Window) -> Self {
69 Curses {
70 window,
71 key_name_mutex: Mutex::new(()),
72 color: None,
73 }
74 }
75
76 /// Get a reference to the main [`Window`] of the curses instance.
77 ///
78 /// This corresponds to `stdscr`.
79 ///
80 /// [`Window`]: struct.Window.html`]
81 pub fn window(&self) -> &Window {
82 &self.window
83 }
84 /// Get a mutable reference to the main [`Window`] of the curses instance.
85 ///
86 /// This corresponds to `stdscr`.
87 ///
88 /// [`Window`]: struct.Window.html`]
89 pub fn window_mut(&mut self) -> &mut Window {
90 &mut self.window
91 }
92
93 /// Check if the terminal has support for colors.
94 pub fn has_colors(&self) -> bool {
95 pancurses::has_colors()
96 }
97 /// Start the color subsystem.
98 ///
99 /// If it has already been started, this does nothing.
100 pub fn start_color(&mut self) -> Result<(), ()> {
101 if self.color.is_none() {
102 check(pancurses::start_color())?;
103 self.color = Some(Color::new());
104 }
105 Ok(())
106 }
107 /// Get an immutable reference to the [`Color`] subsystem.
108 ///
109 /// This method will panic if [`start_color`] has not successfully completed yet.
110 ///
111 /// [`Color`]: struct.Color.html
112 /// [`start_color`]: struct.Curses.html#method.start_color
113 pub fn color(&self) -> &Color {
114 self.color
115 .as_ref()
116 .expect("Color subsystem has not yet been successfully started")
117 }
118 /// Get a mutable reference to the [`Color`] subsystem.
119 ///
120 /// This method will panic if [`start_color`] has not successfully completed yet.
121 ///
122 /// [`Color`]: struct.Color.html
123 /// [`start_color`]: struct.Curses.html#method.start_color
124 pub fn color_mut(&mut self) -> &mut Color {
125 self.color
126 .as_mut()
127 .expect("Color subsystem has not yet been successfully started")
128 }
129
130 /// Set the visibility of the cursor.
131 ///
132 /// This corresponds of `curs_set`.
133 pub fn set_cursor_visibility(&mut self, visibility: CursorVisibility) -> Result<(), ()> {
134 check(pancurses::curs_set(unsafe {
135 std::mem::transmute(visibility)
136 }))
137 }
138
139 /// Save the current terminal state as program mode (in curses).
140 ///
141 /// This is done automatically by [`initscr`].
142 ///
143 /// This corresponds of `def_prog_mode`.
144 ///
145 /// [`initscr`]: fn.initscr.html
146 pub fn define_program_mode(&mut self) -> Result<(), ()> {
147 check(pancurses::def_prog_mode())
148 }
149 /// Save the current terminal state as shell mode (not in curses).
150 ///
151 /// This is done automatically by [`initscr`].
152 ///
153 /// This corresponds of `def_shell_mode`.
154 ///
155 /// [`initscr`]: fn.initscr.html
156 pub fn define_shell_mode(&mut self) -> Result<(), ()> {
157 check(pancurses::def_shell_mode())
158 }
159 /// Restore the terminal to program mode (in curses).
160 ///
161 /// This corresponds of `reset_prog_mode`.
162 pub fn restore_program_mode(&mut self) -> Result<(), ()> {
163 check(pancurses::reset_prog_mode())
164 }
165 /// Restore the terminal to program mode (not in curses).
166 ///
167 /// This corresponds of `reset_shell_mode`.
168 pub fn restore_shell_mode(&mut self) -> Result<(), ()> {
169 check(pancurses::reset_shell_mode())
170 }
171
172 /// Get the output rate of the terminal in bits per second.
173 ///
174 /// This corresponds to `baudrate`.
175 pub fn output_rate(&self) -> BitsPerSecond {
176 BitsPerSecond {
177 bps: pancurses::baudrate(),
178 }
179 }
180 /// Insert a millisecond pause in output. *Don't use this extensively.*
181 pub fn delay_output(&mut self, time: Duration) -> Result<(), ()> {
182 check(pancurses::delay_output(as_millis(time)))
183 }
184
185 /// Push updates from the virtual screen to the physical screen.
186 ///
187 /// This corresponds of `doupdate`.
188 pub fn update(&mut self) -> Result<(), ()> {
189 check(pancurses::doupdate())
190 }
191
192 /// Control whether characters typed by the user are written to the screen
193 /// as they are typed.
194 ///
195 /// If enabled, characters typed by the user are written to the screen as
196 /// they are typed.
197 ///
198 /// If disabled, characters typed by the user are interpretted by the
199 /// program and not echoed to the screen.
200 ///
201 /// This corresponds of `echo` and `noecho`.
202 pub fn set_echo_input(&mut self, echo: bool) -> Result<(), ()> {
203 if echo {
204 check(pancurses::echo())
205 } else {
206 check(pancurses::noecho())
207 }
208 }
209 /// Set the input buffering mode.
210 ///
211 /// See [`InputBufferingMode`] for more informatation.
212 ///
213 /// This corresponds of `cbreak`, `nocbreak`, `raw`, and `noraw`.
214 ///
215 /// [`InputBufferingMode`]: enum.InputBufferingMode.html
216 pub fn set_input_buffering_mode(&mut self, mode: InputBufferingMode) -> Result<(), ()> {
217 match mode {
218 InputBufferingMode::Buffered => {
219 let l = check(pancurses::noraw());
220 let r = check(pancurses::nocbreak());
221 l.and(r)
222 }
223 InputBufferingMode::UnbufferedWithSignals => {
224 let l = check(pancurses::noraw());
225 let r = check(pancurses::cbreak());
226 l.and(r)
227 }
228 InputBufferingMode::UnbufferedNoSignals => {
229 let l = check(pancurses::cbreak());
230 let r = check(pancurses::raw());
231 l.and(r)
232 }
233 }
234 }
235 /// Enable new line translations.
236 ///
237 /// When enabled, the return *key* is translated into newline on
238 /// input and return and line-feed on output.
239 ///
240 /// This is enabled by default. Disabling this can cause curses
241 /// to make better use of the line-feed capability, will have
242 /// faster cursor motion, and will detect the return key (see
243 /// `Input::KeyEnter`).
244 ///
245 /// This corresponds of `nl` and `nonl`.
246 pub fn set_translate_new_lines(&mut self, translate: bool) -> Result<(), ()> {
247 if translate {
248 check(pancurses::nl())
249 } else {
250 check(pancurses::nonl())
251 }
252 }
253 /// Throw away all unread key events.
254 ///
255 /// This corresponds of `flushinp`.
256 pub fn flush_input(&mut self) -> Result<(), ()> {
257 check(pancurses::flushinp())
258 }
259 /// [`read_char`] will block for at most `duration` and wait for input.
260 ///
261 /// This will fail if `duration` is not inbetween 0.1 and 2.55 seconds.
262 /// `duration` is rounded down to the nearest tenth of a second. For more
263 /// fidelity, see [`Window::set_timeout`].
264 ///
265 /// From reading the ncurses source code, I have deduced that this overrides
266 /// the specific [`Window`]'s timeout.
267 ///
268 /// Use [`disable_cbreak`] to stop this.
269 ///
270 /// This corresponds of `halfdelay`.
271 ///
272 /// [`disable_cbreak`]: struct.Curses.html#method.disable_cbreak
273 /// [`Window::set_timeout`]: struct.Window.html#method.set_timeout
274 /// [`Window`]: struct.Window.html
275 pub fn set_timeout(&mut self, duration: Duration) -> Result<(), ()> {
276 let tenths = as_millis(duration) / 100;
277 if tenths < 1 || tenths > 255 {
278 Err(())?
279 }
280 check(pancurses::half_delay(tenths))
281 }
282
283 /// End the instance of curses, allowing for error handling outside of
284 /// panicking.
285 ///
286 /// This returns the terminal to shell mode.
287 ///
288 /// This disposes of the main `Window`.
289 ///
290 /// This corresponds of `endwin`.
291 pub fn end_curses(self) -> Result<(), ()> {
292 let r = end_window();
293 std::mem::forget(self);
294 r
295 }
296
297 /// Flash the terminal screen. If not possible, an alert is sounded.
298 ///
299 /// Returns `Ok` if flashing succeeds, and `Err` otherwise.
300 pub fn flash(&mut self) -> Result<(), ()> {
301 check(pancurses::flash())
302 }
303
304 /// Get a string representing a key code.
305 ///
306 /// This corresponds of `keyname`.
307 pub fn key_name(&self, key_code: i32) -> Option<String> {
308 let _key_name = self.key_name_mutex.lock().unwrap();
309 pancurses::keyname(key_code)
310 }
311
312 /// Get the status of the mouse.
313 ///
314 /// This corresponds of `getmouse`.
315 pub fn mouse_read(&self) -> Result<MouseEvent, ()> {
316 pancurses::getmouse().map_err(|_| ())
317 }
318 /// Get the maximum time between press and release events for it
319 /// to be recognized as a click.
320 ///
321 /// This corresponds of `mouseinterval(-1)`.
322 pub fn mouse_interval(&self) -> Duration {
323 Duration::from_millis(pancurses::mouseinterval(-1) as u64)
324 }
325 /// Set the maximum time between press and release events for it
326 /// to be recognized as a click.
327 ///
328 /// As of right now this will always succeed, but it is possible
329 /// this behavior will change in the future.
330 ///
331 /// This corresponds of `mouseinterval`.
332 pub fn set_mouse_interval(&mut self, interval: Duration) -> Result<(), ()> {
333 pancurses::mouseinterval(as_millis(interval));
334 Ok(())
335 }
336 /// Set the mouse events to be reported.
337 ///
338 /// Returns the masks that were applied.
339 ///
340 /// As of right now this will always succeed, but it is possible
341 /// this behavior will change in the future.
342 ///
343 /// If `mask == 0` then the mouse pointer may be turned off.
344 ///
345 /// This corresponds of `mousemask`.
346 pub fn set_mouse_mask(
347 &mut self,
348 mask: MouseMask,
349 old_mask: Option<&mut MouseMask>,
350 ) -> Result<MouseMask, ()> {
351 let old_mask_ptr = match old_mask {
352 Some(mask) => mask,
353 None => std::ptr::null_mut(),
354 };
355 Ok(pancurses::mousemask(mask, old_mask_ptr))
356 }
357
358 /// Sleep for a certain number of milliseconds.
359 ///
360 /// This corresponds of `napms`.
361 pub fn sleep(&mut self, duration: Duration) -> Result<(), ()> {
362 check(pancurses::napms(as_millis(duration)))
363 }
364
365 /// Attempt to resize the terminal.
366 ///
367 /// This corresponds of `resize_term`.
368 pub fn resize_terminal(&mut self, rows: i32, columns: i32) -> Result<(), ()> {
369 check(pancurses::resize_term(rows, columns))
370 }
371
372 /// Attempt to beep the terminal.
373 pub fn beep(&mut self) -> Result<(), ()> {
374 check(pancurses::beep())
375 }
376
377 /// Control whether characters with `A_BLINK` will actually blink the screen
378 /// or if it will set a high intensity background.
379 ///
380 /// When enabled, the screen will actually blink instead of setting a high
381 /// intensity background.
382 ///
383 /// When disabled, Allow the terminal to either actually blink or set a high
384 /// intensity background when `blink` is called.
385 ///
386 /// This is only supported on Windows.
387 ///
388 /// This corresponds of `set_blink`.
389 pub fn set_force_blink(&mut self, blink: bool) -> Result<(), ()> {
390 check(pancurses::set_blink(blink))
391 }
392
393 /// Set the title of the terminal.
394 ///
395 /// This is only supported on Windows.
396 pub fn set_title<T: AsRef<str>>(&mut self, title: T) -> Result<(), ()> {
397 Ok(pancurses::set_title(title.as_ref()))
398 }
399}
400
401/// Call [`end_window`] and `unwrap` the result.
402///
403/// [`end_window`]: struct.Curses.html#method.end_window
404impl Drop for Curses {
405 fn drop(&mut self) {
406 end_window().unwrap();
407 }
408}