diff --git a/Cargo.lock b/Cargo.lock index 0cb9d013e..885ad0667 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4596,6 +4596,7 @@ dependencies = [ "wezterm-client", "wezterm-gui-subcommands", "wezterm-term", + "winapi 0.3.9", ] [[package]] diff --git a/wezterm/Cargo.toml b/wezterm/Cargo.toml index e03ab32a4..508af18e7 100644 --- a/wezterm/Cargo.toml +++ b/wezterm/Cargo.toml @@ -38,3 +38,16 @@ wezterm-term = { path = "../term" } [target."cfg(unix)".dependencies] termios = "0.3" + +[target."cfg(windows)".dependencies.winapi] +features = [ + "winbase", + "winerror", + "winnls", + "winuser", + "consoleapi", + "handleapi", + "fileapi", + "synchapi", +] +version = "0.3" diff --git a/wezterm/src/asciicast.rs b/wezterm/src/asciicast.rs index 4b375fa1e..e5041ffa3 100644 --- a/wezterm/src/asciicast.rs +++ b/wezterm/src/asciicast.rs @@ -8,15 +8,15 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::ffi::OsString; use std::io::{BufRead, BufReader, BufWriter, Read, Write}; -use std::mem::MaybeUninit; use std::path::PathBuf; use std::sync::mpsc::channel; use std::time::{Duration, Instant}; use structopt::StructOpt; -use termios::{cfmakeraw, tcsetattr, Termios, TCSAFLUSH}; #[cfg(unix)] use unix::UnixTty as Tty; use wezterm_term::color::ColorPalette; +#[cfg(windows)] +use win::WinTty as Tty; /// See /// for file format specification @@ -107,9 +107,123 @@ impl Event { } } +#[cfg(windows)] +mod win { + use super::*; + use filedescriptor::AsRawFileDescriptor; + use std::fs::OpenOptions; + use std::os::windows::io::AsRawHandle; + use winapi::um::consoleapi::*; + use winapi::um::wincon::*; + use winapi::um::winnls::CP_UTF8; + + pub struct WinTty { + saved_input: u32, + saved_output: u32, + saved_cp: u32, + read: FileDescriptor, + write: FileDescriptor, + } + + impl WinTty { + pub fn new() -> anyhow::Result { + let read = + FileDescriptor::new(OpenOptions::new().read(true).write(true).open("CONIN$")?); + let write = + FileDescriptor::new(OpenOptions::new().read(true).write(true).open("CONOUT$")?); + + let mut saved_input = 0; + let mut saved_output = 0; + let saved_cp; + unsafe { + GetConsoleMode(read.as_raw_file_descriptor(), &mut saved_input); + GetConsoleMode(write.as_raw_file_descriptor(), &mut saved_output); + saved_cp = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + } + + Ok(Self { + saved_input, + saved_output, + saved_cp, + read, + write, + }) + } + + pub fn set_cooked(&mut self) -> anyhow::Result<()> { + unsafe { + SetConsoleOutputCP(self.saved_cp); + SetConsoleMode(self.read.as_raw_handle(), self.saved_input); + SetConsoleMode(self.write.as_raw_handle(), self.saved_output); + } + Ok(()) + } + + pub fn set_raw(&mut self) -> anyhow::Result<()> { + unsafe { + SetConsoleMode( + self.read.as_raw_file_descriptor(), + ENABLE_VIRTUAL_TERMINAL_INPUT, + ); + SetConsoleMode( + self.write.as_raw_file_descriptor(), + ENABLE_PROCESSED_OUTPUT + | ENABLE_WRAP_AT_EOL_OUTPUT + | ENABLE_VIRTUAL_TERMINAL_PROCESSING + | DISABLE_NEWLINE_AUTO_RETURN, + ); + } + Ok(()) + } + + pub fn get_size(&self) -> anyhow::Result { + let mut info: CONSOLE_SCREEN_BUFFER_INFO = unsafe { std::mem::zeroed() }; + let ok = unsafe { + GetConsoleScreenBufferInfo( + self.write.as_raw_handle() as *mut _, + &mut info as *mut _, + ) + }; + if ok == 0 { + anyhow::bail!( + "GetConsoleScreenBufferInfo failed: {}", + std::io::Error::last_os_error() + ); + } + + let cols = 1 + (info.srWindow.Right - info.srWindow.Left); + let rows = 1 + (info.srWindow.Bottom - info.srWindow.Top); + + Ok(PtySize { + rows: rows as u16, + cols: cols as u16, + pixel_width: 0, + pixel_height: 0, + }) + } + + pub fn reader(&self) -> anyhow::Result { + Ok(self.read.try_clone()?) + } + + pub fn write_all(&mut self, data: &[u8]) -> anyhow::Result<()> { + Ok(self.write.write_all(data)?) + } + } + + impl Drop for WinTty { + fn drop(&mut self) { + let _ = self.set_cooked(); + } + } +} + +#[cfg(unix)] mod unix { use super::*; use std::os::unix::io::AsRawFd; + use termios::{cfmakeraw, tcsetattr, Termios, TCSAFLUSH}; pub struct UnixTty { tty: FileDescriptor, @@ -152,7 +266,7 @@ mod unix { } pub fn get_size(&self) -> anyhow::Result { - let mut size = MaybeUninit::::uninit(); + let mut size = std::mem::MaybeUninit::::uninit(); if unsafe { libc::ioctl(self.tty.as_raw_fd(), libc::TIOCGWINSZ as _, &mut size) } != 0 { anyhow::bail!( "failed to ioctl(TIOCGWINSZ): {:#}",