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}