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: Result<(u16, u16), Error> = 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}
57