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