| 
									
										
										
										
											2016-09-07 10:39:32 +01:00
										 |  |  | //! Cursor movement.
 | 
					
						
							| 
									
										
										
										
											2016-07-23 15:40:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | use std::fmt;
 | 
					
						
							| 
									
										
										
										
											2017-03-12 20:18:32 +00:00
										 |  |  | use std::io::{self, Write, Error, ErrorKind, Read};
 | 
					
						
							|  |  |  | use async::async_stdin;
 | 
					
						
							|  |  |  | use std::time::{SystemTime, Duration};
 | 
					
						
							|  |  |  | use raw::CONTROL_SEQUENCE_TIMEOUT;
 | 
					
						
							| 
									
										
										
										
											2016-07-23 15:40:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | derive_csi_sequence!("Hide the cursor.", Hide, "?25l");
 | 
					
						
							|  |  |  | derive_csi_sequence!("Show the cursor.", Show, "?25h");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Goto some position ((1,1)-based).
 | 
					
						
							|  |  |  | ///
 | 
					
						
							|  |  |  | /// # Why one-based?
 | 
					
						
							|  |  |  | ///
 | 
					
						
							|  |  |  | /// ANSI escapes are very poorly designed, and one of the many odd aspects is being one-based. This
 | 
					
						
							|  |  |  | /// can be quite strange at first, but it is not that big of an obstruction once you get used to
 | 
					
						
							|  |  |  | /// it.
 | 
					
						
							| 
									
										
										
										
											2016-09-07 10:39:32 +01:00
										 |  |  | ///
 | 
					
						
							|  |  |  | /// # Example
 | 
					
						
							|  |  |  | ///
 | 
					
						
							|  |  |  | /// ```rust
 | 
					
						
							|  |  |  | /// extern crate termion;
 | 
					
						
							|  |  |  | ///
 | 
					
						
							|  |  |  | /// fn main() {
 | 
					
						
							|  |  |  | ///     print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(5, 3));
 | 
					
						
							|  |  |  | /// }
 | 
					
						
							|  |  |  | /// ```
 | 
					
						
							| 
									
										
										
										
											2016-07-23 15:40:27 +01:00
										 |  |  | #[derive(Copy, Clone, PartialEq, Eq)]
 | 
					
						
							|  |  |  | pub struct Goto(pub u16, pub u16);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl Default for Goto {
 | 
					
						
							| 
									
										
										
										
											2017-03-24 20:53:05 +00:00
										 |  |  |     fn default() -> Goto {
 | 
					
						
							|  |  |  |         Goto(1, 1)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2016-07-23 15:40:27 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl fmt::Display for Goto {
 | 
					
						
							|  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
					
						
							|  |  |  |         debug_assert!(self != &Goto(0, 0), "Goto is one-based.");
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-24 19:22:47 +01:00
										 |  |  |         write!(f, csi!("{};{}H"), self.1, self.0)
 | 
					
						
							| 
									
										
										
										
											2016-07-23 15:40:27 +01:00
										 |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Move cursor left.
 | 
					
						
							|  |  |  | #[derive(Copy, Clone, PartialEq, Eq)]
 | 
					
						
							|  |  |  | pub struct Left(pub u16);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl fmt::Display for Left {
 | 
					
						
							|  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
					
						
							|  |  |  |         write!(f, csi!("{}D"), self.0)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Move cursor right.
 | 
					
						
							|  |  |  | #[derive(Copy, Clone, PartialEq, Eq)]
 | 
					
						
							|  |  |  | pub struct Right(pub u16);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl fmt::Display for Right {
 | 
					
						
							|  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
					
						
							|  |  |  |         write!(f, csi!("{}C"), self.0)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2016-07-23 16:49:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /// Move cursor up.
 | 
					
						
							|  |  |  | #[derive(Copy, Clone, PartialEq, Eq)]
 | 
					
						
							|  |  |  | pub struct Up(pub u16);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl fmt::Display for Up {
 | 
					
						
							|  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
					
						
							|  |  |  |         write!(f, csi!("{}A"), self.0)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Move cursor down.
 | 
					
						
							|  |  |  | #[derive(Copy, Clone, PartialEq, Eq)]
 | 
					
						
							|  |  |  | pub struct Down(pub u16);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl fmt::Display for Down {
 | 
					
						
							|  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
					
						
							|  |  |  |         write!(f, csi!("{}B"), self.0)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2017-03-12 20:18:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /// Types that allow detection of the cursor position.
 | 
					
						
							|  |  |  | pub trait DetectCursorPos {
 | 
					
						
							|  |  |  |     /// Get the (1,1)-based cursor position from the terminal.
 | 
					
						
							|  |  |  |     fn cursor_pos(&mut self) -> io::Result<(u16, u16)>;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl<W: Write> DetectCursorPos for W {
 | 
					
						
							|  |  |  |     fn cursor_pos(&mut self) -> io::Result<(u16, u16)> {
 | 
					
						
							|  |  |  |         let mut stdin = async_stdin();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Where is the cursor?
 | 
					
						
							|  |  |  |         // Use `ESC [ 6 n`.
 | 
					
						
							|  |  |  |         write!(self, "\x1B[6n")?;
 | 
					
						
							|  |  |  |         self.flush()?;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let mut buf: [u8; 1] = [0];
 | 
					
						
							|  |  |  |         let mut read_chars = Vec::new();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
 | 
					
						
							|  |  |  |         let now = SystemTime::now();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Either consume all data up to R or wait for a timeout.
 | 
					
						
							|  |  |  |         while buf[0] != b'R' && now.elapsed().unwrap() < timeout {
 | 
					
						
							|  |  |  |             if stdin.read(&mut buf)? > 0 {
 | 
					
						
							|  |  |  |                 read_chars.push(buf[0]);
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if read_chars.len() == 0 {
 | 
					
						
							|  |  |  |             return Err(Error::new(ErrorKind::Other, "Cursor position detection timed out."));
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // The answer will look like `ESC [ Cy ; Cx R`.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         read_chars.pop(); // remove trailing R.
 | 
					
						
							|  |  |  |         let read_str = String::from_utf8(read_chars).unwrap();
 | 
					
						
							|  |  |  |         let beg = read_str.rfind('[').unwrap();
 | 
					
						
							| 
									
										
										
										
											2017-03-24 20:53:05 +00:00
										 |  |  |         let coords: String = read_str.chars().skip(beg + 1).collect();
 | 
					
						
							| 
									
										
										
										
											2017-03-12 20:18:32 +00:00
										 |  |  |         let mut nums = coords.split(';');
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-24 20:53:05 +00:00
										 |  |  |         let cy = nums.next()
 | 
					
						
							|  |  |  |             .unwrap()
 | 
					
						
							|  |  |  |             .parse::<u16>()
 | 
					
						
							|  |  |  |             .unwrap();
 | 
					
						
							|  |  |  |         let cx = nums.next()
 | 
					
						
							|  |  |  |             .unwrap()
 | 
					
						
							|  |  |  |             .parse::<u16>()
 | 
					
						
							|  |  |  |             .unwrap();
 | 
					
						
							| 
									
										
										
										
											2017-03-12 20:18:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Ok((cx, cy))
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 |