1use std::fmt;
46
47#[cfg(windows)]
48use crate::Result;
49use crate::{csi, impl_display, Command};
50
51pub use sys::position;
52
53pub(crate) mod sys;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct MoveTo(pub u16, pub u16);
62
63impl Command for MoveTo {
64 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
65 write!(f, csi!("{};{}H"), self.1 + 1, self.0 + 1)
66 }
67
68 #[cfg(windows)]
69 fn execute_winapi(&self) -> Result<()> {
70 sys::move_to(self.0, self.1)
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct MoveToNextLine(pub u16);
83
84impl Command for MoveToNextLine {
85 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
86 write!(f, csi!("{}E"), self.0)?;
87 Ok(())
88 }
89
90 #[cfg(windows)]
91 fn execute_winapi(&self) -> Result<()> {
92 if self.0 != 0 {
93 sys::move_to_next_line(self.0)?;
94 }
95 Ok(())
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub struct MoveToPreviousLine(pub u16);
108
109impl Command for MoveToPreviousLine {
110 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
111 write!(f, csi!("{}F"), self.0)?;
112 Ok(())
113 }
114
115 #[cfg(windows)]
116 fn execute_winapi(&self) -> Result<()> {
117 if self.0 != 0 {
118 sys::move_to_previous_line(self.0)?;
119 }
120 Ok(())
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub struct MoveToColumn(pub u16);
131
132impl Command for MoveToColumn {
133 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
134 write!(f, csi!("{}G"), self.0 + 1)?;
135 Ok(())
136 }
137
138 #[cfg(windows)]
139 fn execute_winapi(&self) -> Result<()> {
140 sys::move_to_column(self.0)
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub struct MoveToRow(pub u16);
151
152impl Command for MoveToRow {
153 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
154 write!(f, csi!("{}d"), self.0 + 1)?;
155 Ok(())
156 }
157
158 #[cfg(windows)]
159 fn execute_winapi(&self) -> Result<()> {
160 sys::move_to_row(self.0)
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub struct MoveUp(pub u16);
172
173impl Command for MoveUp {
174 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
175 write!(f, csi!("{}A"), self.0)?;
176 Ok(())
177 }
178
179 #[cfg(windows)]
180 fn execute_winapi(&self) -> Result<()> {
181 sys::move_up(self.0)
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub struct MoveRight(pub u16);
193
194impl Command for MoveRight {
195 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
196 write!(f, csi!("{}C"), self.0)?;
197 Ok(())
198 }
199
200 #[cfg(windows)]
201 fn execute_winapi(&self) -> Result<()> {
202 sys::move_right(self.0)
203 }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213pub struct MoveDown(pub u16);
214
215impl Command for MoveDown {
216 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
217 write!(f, csi!("{}B"), self.0)?;
218 Ok(())
219 }
220
221 #[cfg(windows)]
222 fn execute_winapi(&self) -> Result<()> {
223 sys::move_down(self.0)
224 }
225}
226
227#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub struct MoveLeft(pub u16);
235
236impl Command for MoveLeft {
237 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
238 write!(f, csi!("{}D"), self.0)?;
239 Ok(())
240 }
241
242 #[cfg(windows)]
243 fn execute_winapi(&self) -> Result<()> {
244 sys::move_left(self.0)
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub struct SavePosition;
258
259impl Command for SavePosition {
260 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
261 f.write_str("\x1B7")
262 }
263
264 #[cfg(windows)]
265 fn execute_winapi(&self) -> Result<()> {
266 sys::save_position()
267 }
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq)]
279pub struct RestorePosition;
280
281impl Command for RestorePosition {
282 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
283 f.write_str("\x1B8")
284 }
285
286 #[cfg(windows)]
287 fn execute_winapi(&self) -> Result<()> {
288 sys::restore_position()
289 }
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub struct Hide;
299
300impl Command for Hide {
301 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
302 f.write_str(csi!("?25l"))
303 }
304
305 #[cfg(windows)]
306 fn execute_winapi(&self) -> Result<()> {
307 sys::show_cursor(false)
308 }
309}
310
311#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub struct Show;
318
319impl Command for Show {
320 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
321 f.write_str(csi!("?25h"))
322 }
323
324 #[cfg(windows)]
325 fn execute_winapi(&self) -> Result<()> {
326 sys::show_cursor(true)
327 }
328}
329
330#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub struct EnableBlinking;
338
339impl Command for EnableBlinking {
340 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
341 f.write_str(csi!("?12h"))
342 }
343
344 #[cfg(windows)]
345 fn execute_winapi(&self) -> Result<()> {
346 Ok(())
347 }
348}
349
350#[derive(Debug, Clone, Copy, PartialEq, Eq)]
357pub struct DisableBlinking;
358
359impl Command for DisableBlinking {
360 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
361 f.write_str(csi!("?12l"))
362 }
363
364 #[cfg(windows)]
365 fn execute_winapi(&self) -> Result<()> {
366 Ok(())
367 }
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
376pub enum CursorShape {
377 UnderScore,
378 Line,
379 Block,
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
388pub struct SetCursorShape(pub CursorShape);
389
390impl Command for SetCursorShape {
391 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
392 use CursorShape::*;
393 match self.0 {
394 UnderScore => f.write_str("\x1b[3 q"),
395 Line => f.write_str("\x1b[5 q"),
396 Block => f.write_str("\x1b[2 q"),
397 }
398 }
399
400 #[cfg(windows)]
401 fn execute_winapi(&self) -> Result<()> {
402 Ok(())
403 }
404}
405
406impl_display!(for MoveTo);
407impl_display!(for MoveToColumn);
408impl_display!(for MoveToRow);
409impl_display!(for MoveToNextLine);
410impl_display!(for MoveToPreviousLine);
411impl_display!(for MoveUp);
412impl_display!(for MoveDown);
413impl_display!(for MoveLeft);
414impl_display!(for MoveRight);
415impl_display!(for SavePosition);
416impl_display!(for RestorePosition);
417impl_display!(for Hide);
418impl_display!(for Show);
419impl_display!(for EnableBlinking);
420impl_display!(for DisableBlinking);
421impl_display!(for SetCursorShape);
422
423#[cfg(test)]
424mod tests {
425 use std::io::{self, stdout};
426
427 use crate::execute;
428
429 use super::{
430 position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
431 };
432
433 #[test]
435 #[ignore]
436 fn test_move_to() {
437 let (saved_x, saved_y) = position().unwrap();
438
439 execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap();
440 assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
441
442 execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap();
443 assert_eq!(position().unwrap(), (saved_x, saved_y));
444 }
445
446 #[test]
448 #[ignore]
449 fn test_move_right() {
450 let (saved_x, saved_y) = position().unwrap();
451 execute!(io::stdout(), MoveRight(1)).unwrap();
452 assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
453 }
454
455 #[test]
457 #[ignore]
458 fn test_move_left() {
459 execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap();
460 assert_eq!(position().unwrap(), (0, 0));
461 }
462
463 #[test]
465 #[ignore]
466 fn test_move_up() {
467 execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap();
468 assert_eq!(position().unwrap(), (0, 0));
469 }
470
471 #[test]
473 #[ignore]
474 fn test_move_down() {
475 execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap();
476
477 assert_eq!(position().unwrap(), (0, 2));
478 }
479
480 #[test]
482 #[ignore]
483 fn test_save_restore_position() {
484 let (saved_x, saved_y) = position().unwrap();
485
486 execute!(
487 stdout(),
488 SavePosition,
489 MoveTo(saved_x + 1, saved_y + 1),
490 RestorePosition
491 )
492 .unwrap();
493
494 let (x, y) = position().unwrap();
495
496 assert_eq!(x, saved_x);
497 assert_eq!(y, saved_y);
498 }
499}