use std::{fmt, io};
#[cfg(windows)]
use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(windows)]
use winapi::um::wincon::ENABLE_WRAP_AT_EOL_OUTPUT;
#[doc(no_inline)]
use crate::Command;
use crate::{csi, impl_display};
pub(crate) mod sys;
#[cfg(feature = "events")]
pub use sys::supports_keyboard_enhancement;
pub fn is_raw_mode_enabled() -> io::Result<bool> {
#[cfg(unix)]
{
Ok(sys::is_raw_mode_enabled())
}
#[cfg(windows)]
{
sys::is_raw_mode_enabled()
}
}
pub fn enable_raw_mode() -> io::Result<()> {
sys::enable_raw_mode()
}
pub fn disable_raw_mode() -> io::Result<()> {
sys::disable_raw_mode()
}
pub fn size() -> io::Result<(u16, u16)> {
sys::size()
}
#[derive(Debug)]
pub struct WindowSize {
pub rows: u16,
pub columns: u16,
pub width: u16,
pub height: u16,
}
pub fn window_size() -> io::Result<WindowSize> {
sys::window_size()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DisableLineWrap;
impl Command for DisableLineWrap {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?7l"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
let screen_buffer = ScreenBuffer::current()?;
let console_mode = ConsoleMode::from(screen_buffer.handle().clone());
let new_mode = console_mode.mode()? & !ENABLE_WRAP_AT_EOL_OUTPUT;
console_mode.set_mode(new_mode)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnableLineWrap;
impl Command for EnableLineWrap {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?7h"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
let screen_buffer = ScreenBuffer::current()?;
let console_mode = ConsoleMode::from(screen_buffer.handle().clone());
let new_mode = console_mode.mode()? | ENABLE_WRAP_AT_EOL_OUTPUT;
console_mode.set_mode(new_mode)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnterAlternateScreen;
impl Command for EnterAlternateScreen {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?1049h"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
let alternate_screen = ScreenBuffer::create()?;
alternate_screen.show()?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LeaveAlternateScreen;
impl Command for LeaveAlternateScreen {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?1049l"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
let screen_buffer = ScreenBuffer::from(Handle::current_out_handle()?);
screen_buffer.show()?;
Ok(())
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum ClearType {
All,
Purge,
FromCursorDown,
FromCursorUp,
CurrentLine,
UntilNewLine,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScrollUp(pub u16);
impl Command for ScrollUp {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
if self.0 != 0 {
write!(f, csi!("{}S"), self.0)?;
}
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
sys::scroll_up(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScrollDown(pub u16);
impl Command for ScrollDown {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
if self.0 != 0 {
write!(f, csi!("{}T"), self.0)?;
}
Ok(())
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
sys::scroll_down(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Clear(pub ClearType);
impl Command for Clear {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(match self.0 {
ClearType::All => csi!("2J"),
ClearType::Purge => csi!("3J"),
ClearType::FromCursorDown => csi!("J"),
ClearType::FromCursorUp => csi!("1J"),
ClearType::CurrentLine => csi!("2K"),
ClearType::UntilNewLine => csi!("K"),
})
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
sys::clear(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetSize(pub u16, pub u16);
impl Command for SetSize {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, csi!("8;{};{}t"), self.1, self.0)
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
sys::set_size(self.0, self.1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetTitle<T>(pub T);
impl<T: fmt::Display> Command for SetTitle<T> {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, "\x1B]0;{}\x07", &self.0)
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
sys::set_window_title(&self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BeginSynchronizedUpdate;
impl Command for BeginSynchronizedUpdate {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?2026h"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
Ok(())
}
#[cfg(windows)]
#[inline]
fn is_ansi_code_supported(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EndSynchronizedUpdate;
impl Command for EndSynchronizedUpdate {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(csi!("?2026l"))
}
#[cfg(windows)]
fn execute_winapi(&self) -> io::Result<()> {
Ok(())
}
#[cfg(windows)]
#[inline]
fn is_ansi_code_supported(&self) -> bool {
true
}
}
impl_display!(for ScrollUp);
impl_display!(for ScrollDown);
impl_display!(for SetSize);
impl_display!(for Clear);
#[cfg(test)]
mod tests {
use std::{io::stdout, thread, time};
use crate::execute;
use super::*;
#[test]
#[ignore]
fn test_resize_ansi() {
let (width, height) = size().unwrap();
execute!(stdout(), SetSize(35, 35)).unwrap();
thread::sleep(time::Duration::from_millis(30));
assert_eq!((35, 35), size().unwrap());
execute!(stdout(), SetSize(width, height)).unwrap();
thread::sleep(time::Duration::from_millis(30));
assert_eq!((width, height), size().unwrap());
}
#[test]
fn test_raw_mode() {
assert!(!is_raw_mode_enabled().unwrap());
if enable_raw_mode().is_err() {
return;
}
assert!(is_raw_mode_enabled().unwrap());
enable_raw_mode().unwrap();
assert!(is_raw_mode_enabled().unwrap());
disable_raw_mode().unwrap();
assert!(!is_raw_mode_enabled().unwrap());
}
}