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