1 //! Windows specific definitions
2 #![allow(clippy::try_err)] // suggested fix does not work (cannot infer...)
3 
4 use std::io::{self, Write};
5 use std::mem;
6 use std::sync::atomic::{AtomicBool, Ordering};
7 
8 use log::{debug, warn};
9 use unicode_segmentation::UnicodeSegmentation;
10 use unicode_width::UnicodeWidthStr;
11 use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE, WORD};
12 use winapi::shared::winerror;
13 use winapi::um::handleapi::INVALID_HANDLE_VALUE;
14 use winapi::um::wincon::{self, CONSOLE_SCREEN_BUFFER_INFO, COORD};
15 use winapi::um::winnt::{CHAR, HANDLE};
16 use winapi::um::{consoleapi, processenv, winbase, winuser};
17 
18 use super::{width, RawMode, RawReader, Renderer, Term};
19 use crate::config::{BellStyle, ColorMode, Config, OutputStreamType};
20 use crate::error;
21 use crate::highlight::Highlighter;
22 use crate::keys::{KeyCode as K, KeyEvent, Modifiers as M};
23 use crate::layout::{Layout, Position};
24 use crate::line_buffer::LineBuffer;
25 use crate::Result;
26 
27 const STDIN_FILENO: DWORD = winbase::STD_INPUT_HANDLE;
28 const STDOUT_FILENO: DWORD = winbase::STD_OUTPUT_HANDLE;
29 const STDERR_FILENO: DWORD = winbase::STD_ERROR_HANDLE;
30 
get_std_handle(fd: DWORD) -> Result<HANDLE>31 fn get_std_handle(fd: DWORD) -> Result<HANDLE> {
32     let handle = unsafe { processenv::GetStdHandle(fd) };
33     if handle == INVALID_HANDLE_VALUE {
34         Err(io::Error::last_os_error())?;
35     } else if handle.is_null() {
36         Err(io::Error::new(
37             io::ErrorKind::Other,
38             "no stdio handle available for this process",
39         ))?;
40     }
41     Ok(handle)
42 }
43 
check(rc: BOOL) -> Result<()>44 fn check(rc: BOOL) -> Result<()> {
45     if rc == FALSE {
46         Err(io::Error::last_os_error())?
47     } else {
48         Ok(())
49     }
50 }
51 
get_win_size(handle: HANDLE) -> (usize, usize)52 fn get_win_size(handle: HANDLE) -> (usize, usize) {
53     let mut info = unsafe { mem::zeroed() };
54     match unsafe { wincon::GetConsoleScreenBufferInfo(handle, &mut info) } {
55         FALSE => (80, 24),
56         _ => (
57             info.dwSize.X as usize,
58             (1 + info.srWindow.Bottom - info.srWindow.Top) as usize,
59         ), // (info.srWindow.Right - info.srWindow.Left + 1)
60     }
61 }
62 
get_console_mode(handle: HANDLE) -> Result<DWORD>63 fn get_console_mode(handle: HANDLE) -> Result<DWORD> {
64     let mut original_mode = 0;
65     check(unsafe { consoleapi::GetConsoleMode(handle, &mut original_mode) })?;
66     Ok(original_mode)
67 }
68 
69 #[must_use = "You must restore default mode (disable_raw_mode)"]
70 #[cfg(not(test))]
71 pub type Mode = ConsoleMode;
72 
73 #[derive(Clone, Copy, Debug)]
74 pub struct ConsoleMode {
75     original_stdin_mode: DWORD,
76     stdin_handle: HANDLE,
77     original_stdstream_mode: Option<DWORD>,
78     stdstream_handle: HANDLE,
79 }
80 
81 impl RawMode for ConsoleMode {
82     /// Disable RAW mode for the terminal.
disable_raw_mode(&self) -> Result<()>83     fn disable_raw_mode(&self) -> Result<()> {
84         check(unsafe { consoleapi::SetConsoleMode(self.stdin_handle, self.original_stdin_mode) })?;
85         if let Some(original_stdstream_mode) = self.original_stdstream_mode {
86             check(unsafe {
87                 consoleapi::SetConsoleMode(self.stdstream_handle, original_stdstream_mode)
88             })?;
89         }
90         Ok(())
91     }
92 }
93 
94 /// Console input reader
95 pub struct ConsoleRawReader {
96     handle: HANDLE,
97 }
98 
99 impl ConsoleRawReader {
create() -> Result<ConsoleRawReader>100     pub fn create() -> Result<ConsoleRawReader> {
101         let handle = get_std_handle(STDIN_FILENO)?;
102         Ok(ConsoleRawReader { handle })
103     }
104 }
105 
106 impl RawReader for ConsoleRawReader {
next_key(&mut self, _: bool) -> Result<KeyEvent>107     fn next_key(&mut self, _: bool) -> Result<KeyEvent> {
108         use std::char::decode_utf16;
109         use winapi::um::wincon::{
110             LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED,
111             SHIFT_PRESSED,
112         };
113 
114         let mut rec: wincon::INPUT_RECORD = unsafe { mem::zeroed() };
115         let mut count = 0;
116         let mut surrogate = 0;
117         loop {
118             // TODO GetNumberOfConsoleInputEvents
119             check(unsafe {
120                 consoleapi::ReadConsoleInputW(self.handle, &mut rec, 1 as DWORD, &mut count)
121             })?;
122 
123             if rec.EventType == wincon::WINDOW_BUFFER_SIZE_EVENT {
124                 SIGWINCH.store(true, Ordering::SeqCst);
125                 debug!(target: "rustyline", "SIGWINCH");
126                 return Err(error::ReadlineError::WindowResize); // sigwinch +
127                                                                 // err => err
128                                                                 // ignored
129             } else if rec.EventType != wincon::KEY_EVENT {
130                 continue;
131             }
132             let key_event = unsafe { rec.Event.KeyEvent() };
133             // writeln!(io::stderr(), "key_event: {:?}", key_event).unwrap();
134             if key_event.bKeyDown == 0 && key_event.wVirtualKeyCode != winuser::VK_MENU as WORD {
135                 continue;
136             }
137             // key_event.wRepeatCount seems to be always set to 1 (maybe because we only
138             // read one character at a time)
139 
140             let alt_gr = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)
141                 == (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
142             let alt = key_event.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0;
143             let mut mods = M::NONE;
144             if !alt_gr
145                 && key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0
146             {
147                 mods |= M::CTRL;
148             }
149             if alt && !alt_gr {
150                 mods |= M::ALT;
151             }
152             if key_event.dwControlKeyState & SHIFT_PRESSED != 0 {
153                 mods |= M::SHIFT;
154             }
155 
156             let utf16 = unsafe { *key_event.uChar.UnicodeChar() };
157             let key = if utf16 == 0 {
158                 KeyEvent(
159                     match i32::from(key_event.wVirtualKeyCode) {
160                         winuser::VK_LEFT => K::Left,
161                         winuser::VK_RIGHT => K::Right,
162                         winuser::VK_UP => K::Up,
163                         winuser::VK_DOWN => K::Down,
164                         winuser::VK_DELETE => K::Delete,
165                         winuser::VK_HOME => K::Home,
166                         winuser::VK_END => K::End,
167                         winuser::VK_PRIOR => K::PageUp,
168                         winuser::VK_NEXT => K::PageDown,
169                         winuser::VK_INSERT => K::Insert,
170                         winuser::VK_F1 => K::F(1),
171                         winuser::VK_F2 => K::F(2),
172                         winuser::VK_F3 => K::F(3),
173                         winuser::VK_F4 => K::F(4),
174                         winuser::VK_F5 => K::F(5),
175                         winuser::VK_F6 => K::F(6),
176                         winuser::VK_F7 => K::F(7),
177                         winuser::VK_F8 => K::F(8),
178                         winuser::VK_F9 => K::F(9),
179                         winuser::VK_F10 => K::F(10),
180                         winuser::VK_F11 => K::F(11),
181                         winuser::VK_F12 => K::F(12),
182                         // winuser::VK_BACK is correctly handled because the key_event.UnicodeChar
183                         // is also set.
184                         _ => continue,
185                     },
186                     mods,
187                 )
188             } else if utf16 == 27 {
189                 KeyEvent(K::Esc, mods)
190             } else {
191                 if utf16 >= 0xD800 && utf16 < 0xDC00 {
192                     surrogate = utf16;
193                     continue;
194                 }
195                 let orc = if surrogate == 0 {
196                     decode_utf16(Some(utf16)).next()
197                 } else {
198                     decode_utf16([surrogate, utf16].iter().cloned()).next()
199                 };
200                 let rc = if let Some(rc) = orc {
201                     rc
202                 } else {
203                     return Err(error::ReadlineError::Eof);
204                 };
205                 let c = rc?;
206                 KeyEvent::new(c, mods)
207             };
208             debug!(target: "rustyline", "key: {:?}", key);
209             return Ok(key);
210         }
211     }
212 
read_pasted_text(&mut self) -> Result<String>213     fn read_pasted_text(&mut self) -> Result<String> {
214         unimplemented!()
215     }
216 }
217 
218 pub struct ConsoleRenderer {
219     out: OutputStreamType,
220     handle: HANDLE,
221     cols: usize, // Number of columns in terminal
222     buffer: String,
223     colors_enabled: bool,
224     bell_style: BellStyle,
225 }
226 
227 impl ConsoleRenderer {
new( handle: HANDLE, out: OutputStreamType, colors_enabled: bool, bell_style: BellStyle, ) -> ConsoleRenderer228     fn new(
229         handle: HANDLE,
230         out: OutputStreamType,
231         colors_enabled: bool,
232         bell_style: BellStyle,
233     ) -> ConsoleRenderer {
234         // Multi line editing is enabled by ENABLE_WRAP_AT_EOL_OUTPUT mode
235         let (cols, _) = get_win_size(handle);
236         ConsoleRenderer {
237             out,
238             handle,
239             cols,
240             buffer: String::with_capacity(1024),
241             colors_enabled,
242             bell_style,
243         }
244     }
245 
get_console_screen_buffer_info(&self) -> Result<CONSOLE_SCREEN_BUFFER_INFO>246     fn get_console_screen_buffer_info(&self) -> Result<CONSOLE_SCREEN_BUFFER_INFO> {
247         let mut info = unsafe { mem::zeroed() };
248         check(unsafe { wincon::GetConsoleScreenBufferInfo(self.handle, &mut info) })?;
249         Ok(info)
250     }
251 
set_console_cursor_position(&mut self, pos: COORD) -> Result<()>252     fn set_console_cursor_position(&mut self, pos: COORD) -> Result<()> {
253         check(unsafe { wincon::SetConsoleCursorPosition(self.handle, pos) })
254     }
255 
clear(&mut self, length: DWORD, pos: COORD, attr: WORD) -> Result<()>256     fn clear(&mut self, length: DWORD, pos: COORD, attr: WORD) -> Result<()> {
257         let mut _count = 0;
258         check(unsafe {
259             wincon::FillConsoleOutputCharacterA(self.handle, ' ' as CHAR, length, pos, &mut _count)
260         })?;
261         check(unsafe {
262             wincon::FillConsoleOutputAttribute(self.handle, attr, length, pos, &mut _count)
263         })
264     }
265 
set_cursor_visible(&mut self, visible: BOOL) -> Result<()>266     fn set_cursor_visible(&mut self, visible: BOOL) -> Result<()> {
267         set_cursor_visible(self.handle, visible)
268     }
269 
270     // You can't have both ENABLE_WRAP_AT_EOL_OUTPUT and
271     // ENABLE_VIRTUAL_TERMINAL_PROCESSING. So we need to wrap manually.
wrap_at_eol(&mut self, s: &str, mut col: usize) -> usize272     fn wrap_at_eol(&mut self, s: &str, mut col: usize) -> usize {
273         let mut esc_seq = 0;
274         for c in s.graphemes(true) {
275             if c == "\n" {
276                 col = 0;
277                 self.buffer.push_str(c);
278             } else {
279                 let cw = width(c, &mut esc_seq);
280                 col += cw;
281                 if col > self.cols {
282                     self.buffer.push('\n');
283                     col = cw;
284                 }
285                 self.buffer.push_str(c);
286             }
287         }
288         if col == self.cols {
289             self.buffer.push('\n');
290             col = 0;
291         }
292         col
293     }
294 
295     // position at the start of the prompt, clear to end of previous input
clear_old_rows(&mut self, info: &CONSOLE_SCREEN_BUFFER_INFO, layout: &Layout) -> Result<()>296     fn clear_old_rows(&mut self, info: &CONSOLE_SCREEN_BUFFER_INFO, layout: &Layout) -> Result<()> {
297         let current_row = layout.cursor.row;
298         let old_rows = layout.end.row;
299         let mut coord = info.dwCursorPosition;
300         coord.X = 0;
301         coord.Y -= current_row as i16;
302         self.set_console_cursor_position(coord)?;
303         self.clear(
304             (info.dwSize.X * (old_rows as i16 + 1)) as DWORD,
305             coord,
306             info.wAttributes,
307         )
308     }
309 }
310 
set_cursor_visible(handle: HANDLE, visible: BOOL) -> Result<()>311 fn set_cursor_visible(handle: HANDLE, visible: BOOL) -> Result<()> {
312     let mut info = unsafe { mem::zeroed() };
313     check(unsafe { wincon::GetConsoleCursorInfo(handle, &mut info) })?;
314     if info.bVisible == visible {
315         return Ok(());
316     }
317     info.bVisible = visible;
318     check(unsafe { wincon::SetConsoleCursorInfo(handle, &info) })
319 }
320 
321 impl Renderer for ConsoleRenderer {
322     type Reader = ConsoleRawReader;
323 
move_cursor(&mut self, old: Position, new: Position) -> Result<()>324     fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
325         let mut cursor = self.get_console_screen_buffer_info()?.dwCursorPosition;
326         if new.row > old.row {
327             cursor.Y += (new.row - old.row) as i16;
328         } else {
329             cursor.Y -= (old.row - new.row) as i16;
330         }
331         if new.col > old.col {
332             cursor.X += (new.col - old.col) as i16;
333         } else {
334             cursor.X -= (old.col - new.col) as i16;
335         }
336         self.set_console_cursor_position(cursor)
337     }
338 
refresh_line( &mut self, prompt: &str, line: &LineBuffer, hint: Option<&str>, old_layout: &Layout, new_layout: &Layout, highlighter: Option<&dyn Highlighter>, ) -> Result<()>339     fn refresh_line(
340         &mut self,
341         prompt: &str,
342         line: &LineBuffer,
343         hint: Option<&str>,
344         old_layout: &Layout,
345         new_layout: &Layout,
346         highlighter: Option<&dyn Highlighter>,
347     ) -> Result<()> {
348         let default_prompt = new_layout.default_prompt;
349         let cursor = new_layout.cursor;
350         let end_pos = new_layout.end;
351 
352         self.buffer.clear();
353         let mut col = 0;
354         if let Some(highlighter) = highlighter {
355             // TODO handle ansi escape code (SetConsoleTextAttribute)
356             // append the prompt
357             col = self.wrap_at_eol(&highlighter.highlight_prompt(prompt, default_prompt), col);
358             // append the input line
359             col = self.wrap_at_eol(&highlighter.highlight(line, line.pos()), col);
360         } else {
361             // append the prompt
362             self.buffer.push_str(prompt);
363             // append the input line
364             self.buffer.push_str(line);
365         }
366         // append hint
367         if let Some(hint) = hint {
368             if let Some(highlighter) = highlighter {
369                 self.wrap_at_eol(&highlighter.highlight_hint(hint), col);
370             } else {
371                 self.buffer.push_str(hint);
372             }
373         }
374         let info = self.get_console_screen_buffer_info()?;
375         self.set_cursor_visible(FALSE)?; // just to avoid flickering
376         let handle = self.handle;
377         scopeguard::defer! {
378             let _ = set_cursor_visible(handle, TRUE);
379         }
380         // position at the start of the prompt, clear to end of previous input
381         self.clear_old_rows(&info, old_layout)?;
382         // display prompt, input line and hint
383         self.write_and_flush(self.buffer.as_bytes())?;
384 
385         // position the cursor
386         let mut coord = self.get_console_screen_buffer_info()?.dwCursorPosition;
387         coord.X = cursor.col as i16;
388         coord.Y -= (end_pos.row - cursor.row) as i16;
389         self.set_console_cursor_position(coord)?;
390 
391         Ok(())
392     }
393 
write_and_flush(&self, buf: &[u8]) -> Result<()>394     fn write_and_flush(&self, buf: &[u8]) -> Result<()> {
395         match self.out {
396             OutputStreamType::Stdout => {
397                 io::stdout().write_all(buf)?;
398                 io::stdout().flush()?;
399             }
400             OutputStreamType::Stderr => {
401                 io::stderr().write_all(buf)?;
402                 io::stderr().flush()?;
403             }
404         }
405         Ok(())
406     }
407 
408     /// Characters with 2 column width are correctly handled (not split).
calculate_position(&self, s: &str, orig: Position) -> Position409     fn calculate_position(&self, s: &str, orig: Position) -> Position {
410         let mut pos = orig;
411         for c in s.graphemes(true) {
412             if c == "\n" {
413                 pos.col = 0;
414                 pos.row += 1;
415             } else {
416                 let cw = c.width();
417                 pos.col += cw;
418                 if pos.col > self.cols {
419                     pos.row += 1;
420                     pos.col = cw;
421                 }
422             }
423         }
424         if pos.col == self.cols {
425             pos.col = 0;
426             pos.row += 1;
427         }
428         pos
429     }
430 
beep(&mut self) -> Result<()>431     fn beep(&mut self) -> Result<()> {
432         match self.bell_style {
433             BellStyle::Audible => {
434                 io::stderr().write_all(b"\x07")?;
435                 io::stderr().flush()?;
436                 Ok(())
437             }
438             _ => Ok(()),
439         }
440     }
441 
442     /// Clear the screen. Used to handle ctrl+l
clear_screen(&mut self) -> Result<()>443     fn clear_screen(&mut self) -> Result<()> {
444         let info = self.get_console_screen_buffer_info()?;
445         let coord = COORD { X: 0, Y: 0 };
446         check(unsafe { wincon::SetConsoleCursorPosition(self.handle, coord) })?;
447         let n = info.dwSize.X as DWORD * info.dwSize.Y as DWORD;
448         self.clear(n, coord, info.wAttributes)
449     }
450 
sigwinch(&self) -> bool451     fn sigwinch(&self) -> bool {
452         SIGWINCH.compare_and_swap(true, false, Ordering::SeqCst)
453     }
454 
455     /// Try to get the number of columns in the current terminal,
456     /// or assume 80 if it fails.
update_size(&mut self)457     fn update_size(&mut self) {
458         let (cols, _) = get_win_size(self.handle);
459         self.cols = cols;
460     }
461 
get_columns(&self) -> usize462     fn get_columns(&self) -> usize {
463         self.cols
464     }
465 
466     /// Try to get the number of rows in the current terminal,
467     /// or assume 24 if it fails.
get_rows(&self) -> usize468     fn get_rows(&self) -> usize {
469         let (_, rows) = get_win_size(self.handle);
470         rows
471     }
472 
colors_enabled(&self) -> bool473     fn colors_enabled(&self) -> bool {
474         self.colors_enabled
475     }
476 
move_cursor_at_leftmost(&mut self, _: &mut ConsoleRawReader) -> Result<()>477     fn move_cursor_at_leftmost(&mut self, _: &mut ConsoleRawReader) -> Result<()> {
478         self.write_and_flush(b"")?; // we must do this otherwise the cursor position is not reported correctly
479         let mut info = self.get_console_screen_buffer_info()?;
480         if info.dwCursorPosition.X == 0 {
481             return Ok(());
482         }
483         debug!(target: "rustyline", "initial cursor location: {:?}, {:?}", info.dwCursorPosition.X, info.dwCursorPosition.Y);
484         info.dwCursorPosition.X = 0;
485         info.dwCursorPosition.Y += 1;
486         let res = self.set_console_cursor_position(info.dwCursorPosition);
487         if let Err(error::ReadlineError::Io(ref e)) = res {
488             if e.raw_os_error() == Some(winerror::ERROR_INVALID_PARAMETER as i32) {
489                 warn!(target: "rustyline", "invalid cursor position: ({:?}, {:?}) in ({:?}, {:?})", info.dwCursorPosition.X, info.dwCursorPosition.Y, info.dwSize.X, info.dwSize.Y);
490                 println!();
491                 return Ok(());
492             }
493         }
494         res
495     }
496 }
497 
498 static SIGWINCH: AtomicBool = AtomicBool::new(false);
499 
500 #[cfg(not(test))]
501 pub type Terminal = Console;
502 
503 #[derive(Clone, Debug)]
504 pub struct Console {
505     stdin_isatty: bool,
506     stdin_handle: HANDLE,
507     stdstream_isatty: bool,
508     stdstream_handle: HANDLE,
509     pub(crate) color_mode: ColorMode,
510     ansi_colors_supported: bool,
511     stream_type: OutputStreamType,
512     bell_style: BellStyle,
513 }
514 
515 impl Console {
colors_enabled(&self) -> bool516     fn colors_enabled(&self) -> bool {
517         // TODO ANSI Colors & Windows <10
518         match self.color_mode {
519             ColorMode::Enabled => self.stdstream_isatty && self.ansi_colors_supported,
520             ColorMode::Forced => true,
521             ColorMode::Disabled => false,
522         }
523     }
524 }
525 
526 impl Term for Console {
527     type Mode = ConsoleMode;
528     type Reader = ConsoleRawReader;
529     type Writer = ConsoleRenderer;
530 
new( color_mode: ColorMode, stream_type: OutputStreamType, _tab_stop: usize, bell_style: BellStyle, ) -> Console531     fn new(
532         color_mode: ColorMode,
533         stream_type: OutputStreamType,
534         _tab_stop: usize,
535         bell_style: BellStyle,
536     ) -> Console {
537         use std::ptr;
538         let stdin_handle = get_std_handle(STDIN_FILENO);
539         let stdin_isatty = match stdin_handle {
540             Ok(handle) => {
541                 // If this function doesn't fail then fd is a TTY
542                 get_console_mode(handle).is_ok()
543             }
544             Err(_) => false,
545         };
546 
547         let stdstream_handle = get_std_handle(if stream_type == OutputStreamType::Stdout {
548             STDOUT_FILENO
549         } else {
550             STDERR_FILENO
551         });
552         let stdstream_isatty = match stdstream_handle {
553             Ok(handle) => {
554                 // If this function doesn't fail then fd is a TTY
555                 get_console_mode(handle).is_ok()
556             }
557             Err(_) => false,
558         };
559 
560         Console {
561             stdin_isatty,
562             stdin_handle: stdin_handle.unwrap_or(ptr::null_mut()),
563             stdstream_isatty,
564             stdstream_handle: stdstream_handle.unwrap_or(ptr::null_mut()),
565             color_mode,
566             ansi_colors_supported: false,
567             stream_type,
568             bell_style,
569         }
570     }
571 
572     /// Checking for an unsupported TERM in windows is a no-op
is_unsupported(&self) -> bool573     fn is_unsupported(&self) -> bool {
574         false
575     }
576 
is_stdin_tty(&self) -> bool577     fn is_stdin_tty(&self) -> bool {
578         self.stdin_isatty
579     }
580 
is_output_tty(&self) -> bool581     fn is_output_tty(&self) -> bool {
582         self.stdstream_isatty
583     }
584 
585     // pub fn install_sigwinch_handler(&mut self) {
586     // See ReadConsoleInputW && WINDOW_BUFFER_SIZE_EVENT
587     // }
588 
589     /// Enable RAW mode for the terminal.
enable_raw_mode(&mut self) -> Result<Self::Mode>590     fn enable_raw_mode(&mut self) -> Result<Self::Mode> {
591         if !self.stdin_isatty {
592             Err(io::Error::new(
593                 io::ErrorKind::Other,
594                 "no stdio handle available for this process",
595             ))?;
596         }
597         let original_stdin_mode = get_console_mode(self.stdin_handle)?;
598         // Disable these modes
599         let mut raw = original_stdin_mode
600             & !(wincon::ENABLE_LINE_INPUT
601                 | wincon::ENABLE_ECHO_INPUT
602                 | wincon::ENABLE_PROCESSED_INPUT);
603         // Enable these modes
604         raw |= wincon::ENABLE_EXTENDED_FLAGS;
605         raw |= wincon::ENABLE_INSERT_MODE;
606         raw |= wincon::ENABLE_QUICK_EDIT_MODE;
607         raw |= wincon::ENABLE_WINDOW_INPUT;
608         check(unsafe { consoleapi::SetConsoleMode(self.stdin_handle, raw) })?;
609 
610         let original_stdstream_mode = if self.stdstream_isatty {
611             let original_stdstream_mode = get_console_mode(self.stdstream_handle)?;
612 
613             let mut mode = original_stdstream_mode;
614             if mode & wincon::ENABLE_WRAP_AT_EOL_OUTPUT == 0 {
615                 mode |= wincon::ENABLE_WRAP_AT_EOL_OUTPUT;
616                 debug!(target: "rustyline", "activate ENABLE_WRAP_AT_EOL_OUTPUT");
617                 unsafe {
618                     assert!(consoleapi::SetConsoleMode(self.stdstream_handle, mode) != 0);
619                 }
620             }
621             // To enable ANSI colors (Windows 10 only):
622             // https://docs.microsoft.com/en-us/windows/console/setconsolemode
623             self.ansi_colors_supported = mode & wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0;
624             if self.ansi_colors_supported {
625                 if self.color_mode == ColorMode::Disabled {
626                     mode &= !wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
627                     debug!(target: "rustyline", "deactivate ENABLE_VIRTUAL_TERMINAL_PROCESSING");
628                     unsafe {
629                         assert!(consoleapi::SetConsoleMode(self.stdstream_handle, mode) != 0);
630                     }
631                 } else {
632                     debug!(target: "rustyline", "ANSI colors already enabled");
633                 }
634             } else if self.color_mode != ColorMode::Disabled {
635                 mode |= wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
636                 self.ansi_colors_supported =
637                     unsafe { consoleapi::SetConsoleMode(self.stdstream_handle, mode) != 0 };
638                 debug!(target: "rustyline", "ansi_colors_supported: {}", self.ansi_colors_supported);
639             }
640             Some(original_stdstream_mode)
641         } else {
642             None
643         };
644 
645         Ok(ConsoleMode {
646             original_stdin_mode,
647             stdin_handle: self.stdin_handle,
648             original_stdstream_mode,
649             stdstream_handle: self.stdstream_handle,
650         })
651     }
652 
create_reader(&self, _: &Config) -> Result<ConsoleRawReader>653     fn create_reader(&self, _: &Config) -> Result<ConsoleRawReader> {
654         ConsoleRawReader::create()
655     }
656 
create_writer(&self) -> ConsoleRenderer657     fn create_writer(&self) -> ConsoleRenderer {
658         ConsoleRenderer::new(
659             self.stdstream_handle,
660             self.stream_type,
661             self.colors_enabled(),
662             self.bell_style,
663         )
664     }
665 }
666 
667 unsafe impl Send for Console {}
668 unsafe impl Sync for Console {}
669 
670 #[cfg(test)]
671 mod test {
672     use super::Console;
673 
674     #[test]
test_send()675     fn test_send() {
676         fn assert_send<T: Send>() {}
677         assert_send::<Console>();
678     }
679 
680     #[test]
test_sync()681     fn test_sync() {
682         fn assert_sync<T: Sync>() {}
683         assert_sync::<Console>();
684     }
685 }
686