use std::fmt;
use crate::{csi, impl_display, Command};
pub(crate) mod sys;
#[cfg(feature = "events")]
pub use sys::position;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveTo(pub u16, pub u16);
impl Command for MoveTo {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{};{}H"), self.1 + 1, self.0 + 1)
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::move_to(self.0, self.1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveToNextLine(pub u16);
impl Command for MoveToNextLine {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}E"), self.0)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
if self.0 != 0 {
sys::move_to_next_line(self.0)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveToPreviousLine(pub u16);
impl Command for MoveToPreviousLine {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}F"), self.0)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
if self.0 != 0 {
sys::move_to_previous_line(self.0)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveToColumn(pub u16);
impl Command for MoveToColumn {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}G"), self.0 + 1)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::move_to_column(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveToRow(pub u16);
impl Command for MoveToRow {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}d"), self.0 + 1)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::move_to_row(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveUp(pub u16);
impl Command for MoveUp {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}A"), self.0)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::move_up(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveRight(pub u16);
impl Command for MoveRight {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}C"), self.0)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::move_right(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveDown(pub u16);
impl Command for MoveDown {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}B"), self.0)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::move_down(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveLeft(pub u16);
impl Command for MoveLeft {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("{}D"), self.0)?;
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::move_left(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SavePosition;
impl Command for SavePosition {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str("\x1B7")
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::save_position()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RestorePosition;
impl Command for RestorePosition {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str("\x1B8")
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::restore_position()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Hide;
impl Command for Hide {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?25l"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::show_cursor(false)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Show;
impl Command for Show {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?25h"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
sys::show_cursor(true)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnableBlinking;
impl Command for EnableBlinking {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?12h"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DisableBlinking;
impl Command for DisableBlinking {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?12l"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SetCursorStyle {
DefaultUserShape,
BlinkingBlock,
SteadyBlock,
BlinkingUnderScore,
SteadyUnderScore,
BlinkingBar,
SteadyBar,
}
impl Command for SetCursorStyle {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
match self {
SetCursorStyle::DefaultUserShape => f.write_str("\x1b[0 q"),
SetCursorStyle::BlinkingBlock => f.write_str("\x1b[1 q"),
SetCursorStyle::SteadyBlock => f.write_str("\x1b[2 q"),
SetCursorStyle::BlinkingUnderScore => f.write_str("\x1b[3 q"),
SetCursorStyle::SteadyUnderScore => f.write_str("\x1b[4 q"),
SetCursorStyle::BlinkingBar => f.write_str("\x1b[5 q"),
SetCursorStyle::SteadyBar => f.write_str("\x1b[6 q"),
}
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Ok(())
}
}
impl_display!(for MoveTo);
impl_display!(for MoveToColumn);
impl_display!(for MoveToRow);
impl_display!(for MoveToNextLine);
impl_display!(for MoveToPreviousLine);
impl_display!(for MoveUp);
impl_display!(for MoveDown);
impl_display!(for MoveLeft);
impl_display!(for MoveRight);
impl_display!(for SavePosition);
impl_display!(for RestorePosition);
impl_display!(for Hide);
impl_display!(for Show);
impl_display!(for EnableBlinking);
impl_display!(for DisableBlinking);
impl_display!(for SetCursorStyle);
#[cfg(test)]
#[cfg(feature = "events")]
mod tests {
use std::io::{self, stdout};
use crate::execute;
use super::{
sys::position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
};
#[test]
#[ignore]
fn test_move_to() {
let (saved_x, saved_y) = position().unwrap();
execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap();
assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap();
assert_eq!(position().unwrap(), (saved_x, saved_y));
}
#[test]
#[ignore]
fn test_move_right() {
let (saved_x, saved_y) = position().unwrap();
execute!(io::stdout(), MoveRight(1)).unwrap();
assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
}
#[test]
#[ignore]
fn test_move_left() {
execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap();
assert_eq!(position().unwrap(), (0, 0));
}
#[test]
#[ignore]
fn test_move_up() {
execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap();
assert_eq!(position().unwrap(), (0, 0));
}
#[test]
#[ignore]
fn test_move_down() {
execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap();
assert_eq!(position().unwrap(), (0, 2));
}
#[test]
#[ignore]
fn test_save_restore_position() {
let (saved_x, saved_y) = position().unwrap();
execute!(
stdout(),
SavePosition,
MoveTo(saved_x + 1, saved_y + 1),
RestorePosition
)
.unwrap();
let (x, y) = position().unwrap();
assert_eq!(x, saved_x);
assert_eq!(y, saved_y);
}
}