termion/
cursor.rs

1//! Cursor movement.
2
3use std::fmt;
4use std::ops;
5use std::io::{self, Write, Error, ErrorKind, Read};
6use async::async_stdin_until;
7use std::time::{SystemTime, Duration};
8use raw::CONTROL_SEQUENCE_TIMEOUT;
9use numtoa::NumToA;
10
11derive_csi_sequence!("Hide the cursor.", Hide, "?25l");
12derive_csi_sequence!("Show the cursor.", Show, "?25h");
13
14derive_csi_sequence!("Restore the cursor.", Restore, "u");
15derive_csi_sequence!("Save the cursor.", Save, "s");
16
17/// Goto some position ((1,1)-based).
18///
19/// # Why one-based?
20///
21/// ANSI escapes are very poorly designed, and one of the many odd aspects is being one-based. This
22/// can be quite strange at first, but it is not that big of an obstruction once you get used to
23/// it.
24///
25/// # Example
26///
27/// ```rust
28/// extern crate termion;
29///
30/// fn main() {
31///     print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(5, 3));
32/// }
33/// ```
34#[derive(Copy, Clone, PartialEq, Eq)]
35pub struct Goto(pub u16, pub u16);
36
37impl From<Goto> for String {
38    fn from(this: Goto) -> String {
39        let (mut x, mut y) = ([0u8; 20], [0u8; 20]);
40        ["\x1B[", this.1.numtoa_str(10, &mut x), ";", this.0.numtoa_str(10, &mut y), "H"].concat()
41    }
42}
43
44impl Default for Goto {
45    fn default() -> Goto {
46        Goto(1, 1)
47    }
48}
49
50impl fmt::Display for Goto {
51    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52        debug_assert!(self != &Goto(0, 0), "Goto is one-based.");
53        f.write_str(&String::from(*self))
54    }
55}
56
57/// Move cursor left.
58#[derive(Copy, Clone, PartialEq, Eq)]
59pub struct Left(pub u16);
60
61impl From<Left> for String {
62    fn from(this: Left) -> String {
63        let mut buf = [0u8; 20];
64        ["\x1B[", this.0.numtoa_str(10, &mut buf), "D"].concat()
65    }
66}
67
68impl fmt::Display for Left {
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        f.write_str(&String::from(*self))
71    }
72}
73
74/// Move cursor right.
75#[derive(Copy, Clone, PartialEq, Eq)]
76pub struct Right(pub u16);
77
78impl From<Right> for String {
79    fn from(this: Right) -> String {
80        let mut buf = [0u8; 20];
81        ["\x1B[", this.0.numtoa_str(10, &mut buf), "C"].concat()
82    }
83}
84
85impl fmt::Display for Right {
86    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87        f.write_str(&String::from(*self))
88    }
89}
90
91/// Move cursor up.
92#[derive(Copy, Clone, PartialEq, Eq)]
93pub struct Up(pub u16);
94
95impl From<Up> for String {
96    fn from(this: Up) -> String {
97        let mut buf = [0u8; 20];
98        ["\x1B[", this.0.numtoa_str(10, &mut buf), "A"].concat()
99    }
100}
101
102impl fmt::Display for Up {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        f.write_str(&String::from(*self))
105    }
106}
107
108/// Move cursor down.
109#[derive(Copy, Clone, PartialEq, Eq)]
110pub struct Down(pub u16);
111
112impl From<Down> for String {
113    fn from(this: Down) -> String {
114        let mut buf = [0u8; 20];
115        ["\x1B[", this.0.numtoa_str(10, &mut buf), "B"].concat()
116    }
117}
118
119impl fmt::Display for Down {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        f.write_str(&String::from(*self))
122    }
123}
124
125/// Types that allow detection of the cursor position.
126pub trait DetectCursorPos {
127    /// Get the (1,1)-based cursor position from the terminal.
128    fn cursor_pos(&mut self) -> io::Result<(u16, u16)>;
129}
130
131impl<W: Write> DetectCursorPos for W {
132    fn cursor_pos(&mut self) -> io::Result<(u16, u16)> {
133        let delimiter = b'R';
134        let mut stdin = async_stdin_until(delimiter);
135
136        // Where is the cursor?
137        // Use `ESC [ 6 n`.
138        write!(self, "\x1B[6n")?;
139        self.flush()?;
140
141        let mut buf: [u8; 1] = [0];
142        let mut read_chars = Vec::new();
143
144        let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
145        let now = SystemTime::now();
146
147        // Either consume all data up to R or wait for a timeout.
148        while buf[0] != delimiter && now.elapsed().unwrap() < timeout {
149            if stdin.read(&mut buf)? > 0 {
150                read_chars.push(buf[0]);
151            }
152        }
153
154        if read_chars.is_empty() {
155            return Err(Error::new(ErrorKind::Other, "Cursor position detection timed out."));
156        }
157
158        // The answer will look like `ESC [ Cy ; Cx R`.
159
160        read_chars.pop(); // remove trailing R.
161        let read_str = String::from_utf8(read_chars).unwrap();
162        let beg = read_str.rfind('[').unwrap();
163        let coords: String = read_str.chars().skip(beg + 1).collect();
164        let mut nums = coords.split(';');
165
166        let cy = nums.next()
167            .unwrap()
168            .parse::<u16>()
169            .unwrap();
170        let cx = nums.next()
171            .unwrap()
172            .parse::<u16>()
173            .unwrap();
174
175        Ok((cx, cy))
176    }
177}
178
179/// Hide the cursor for the lifetime of this struct.
180/// It will hide the cursor on creation with from() and show it back on drop().
181pub struct HideCursor<W: Write> {
182    /// The output target.
183    output: W,
184}
185
186impl<W: Write> HideCursor<W> {
187    /// Create a hide cursor wrapper struct for the provided output and hides the cursor.
188    pub fn from(mut output: W) -> Self {
189        write!(output, "{}", Hide).expect("hide the cursor");
190        HideCursor { output: output }
191    }
192}
193
194impl<W: Write> Drop for HideCursor<W> {
195    fn drop(&mut self) {
196        write!(self, "{}", Show).expect("show the cursor");
197    }
198}
199
200impl<W: Write> ops::Deref for HideCursor<W> {
201    type Target = W;
202
203    fn deref(&self) -> &W {
204        &self.output
205    }
206}
207
208impl<W: Write> ops::DerefMut for HideCursor<W> {
209    fn deref_mut(&mut self) -> &mut W {
210        &mut self.output
211    }
212}
213
214impl<W: Write> Write for HideCursor<W> {
215    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
216        self.output.write(buf)
217    }
218
219    fn flush(&mut self) -> io::Result<()> {
220        self.output.flush()
221    }
222}