#[cfg(windows)]
use crossterm_winapi::{Handle, ScreenBuffer};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[doc(no_inline)]
use crate::Command;
use crate::{impl_display, Result};
mod ansi;
pub(crate) mod sys;
pub fn enable_raw_mode() -> Result<()> {
sys::enable_raw_mode()
}
pub fn disable_raw_mode() -> Result<()> {
sys::disable_raw_mode()
}
pub fn size() -> Result<(u16, u16)> {
sys::size()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EnterAlternateScreen;
impl Command for EnterAlternateScreen {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
ansi::ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
let alternate_screen = ScreenBuffer::create();
alternate_screen.show()?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LeaveAlternateScreen;
impl Command for LeaveAlternateScreen {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
ansi::LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE
}
#[cfg(windows)]
fn execute_winapi(&self) -> 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,
FromCursorDown,
FromCursorUp,
CurrentLine,
UntilNewLine,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScrollUp(pub u16);
impl Command for ScrollUp {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
ansi::scroll_up_csi_sequence(self.0)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
sys::scroll_up(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScrollDown(pub u16);
impl Command for ScrollDown {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
ansi::scroll_down_csi_sequence(self.0)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
sys::scroll_down(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Clear(pub ClearType);
impl Command for Clear {
type AnsiType = &'static str;
fn ansi_code(&self) -> Self::AnsiType {
match self.0 {
ClearType::All => ansi::CLEAR_ALL_CSI_SEQUENCE,
ClearType::FromCursorDown => ansi::CLEAR_FROM_CURSOR_DOWN_CSI_SEQUENCE,
ClearType::FromCursorUp => ansi::CLEAR_FROM_CURSOR_UP_CSI_SEQUENCE,
ClearType::CurrentLine => ansi::CLEAR_FROM_CURRENT_LINE_CSI_SEQUENCE,
ClearType::UntilNewLine => ansi::CLEAR_UNTIL_NEW_LINE_CSI_SEQUENCE,
}
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
sys::clear(self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetSize(pub u16, pub u16);
impl Command for SetSize {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
ansi::set_size_csi_sequence(self.0, self.1)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
sys::set_size(self.0, self.1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetTitle<'a>(pub &'a str);
impl<'a> Command for SetTitle<'a> {
type AnsiType = String;
fn ansi_code(&self) -> Self::AnsiType {
ansi::set_title_ansi_sequence(self.0)
}
#[cfg(windows)]
fn execute_winapi(&self) -> Result<()> {
sys::set_window_title(self.0)
}
}
impl_display!(for ScrollUp);
impl_display!(for ScrollDown);
impl_display!(for SetSize);
impl_display!(for Clear);
#[cfg(test)]
mod tests {
use std::{
io::{stdout, Write},
thread, time,
};
use crate::execute;
use super::{size, SetSize};
#[test]
#[ignore]
fn test_resize_ansi() {
try_enable_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());
}
fn try_enable_ansi() -> bool {
#[cfg(windows)]
{
if cfg!(target_os = "windows") {
use crate::ansi_support::set_virtual_terminal_processing;
match set_virtual_terminal_processing(true) {
Ok(_) => return true,
Err(_) => return false,
}
}
}
true
}
}