termion/
cursor.rs

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