crossterm/cursor/sys/
unix.rs

1use std::{
2    io::{self, Error, ErrorKind, Write},
3    time::Duration,
4};
5
6use crate::{
7    event::{filter::CursorPositionFilter, poll_internal, read_internal, InternalEvent},
8    terminal::{disable_raw_mode, enable_raw_mode, sys::is_raw_mode_enabled},
9};
10
11/// Returns the cursor position (column, row).
12///
13/// The top left cell is represented as `(0, 0)`.
14///
15/// On unix systems, this function will block and possibly time out while
16/// [`crossterm::event::read`](crate::event::read) or [`crossterm::event::poll`](crate::event::poll) are being called.
17pub fn position() -> io::Result<(u16, u16)> {
18    if is_raw_mode_enabled() {
19        read_position_raw()
20    } else {
21        read_position()
22    }
23}
24
25fn read_position() -> io::Result<(u16, u16)> {
26    enable_raw_mode()?;
27    let pos = read_position_raw();
28    disable_raw_mode()?;
29    pos
30}
31
32fn read_position_raw() -> io::Result<(u16, u16)> {
33    // Use `ESC [ 6 n` to and retrieve the cursor position.
34    let mut stdout = io::stdout();
35    stdout.write_all(b"\x1B[6n")?;
36    stdout.flush()?;
37
38    loop {
39        match poll_internal(Some(Duration::from_millis(2000)), &CursorPositionFilter) {
40            Ok(true) => {
41                if let Ok(InternalEvent::CursorPosition(x, y)) =
42                    read_internal(&CursorPositionFilter)
43                {
44                    return Ok((x, y));
45                }
46            }
47            Ok(false) => {
48                return Err(Error::new(
49                    ErrorKind::Other,
50                    "The cursor position could not be read within a normal duration",
51                ));
52            }
53            Err(_) => {}
54        }
55    }
56}