term_ctrl/support.rs
1// Copyright 2017 Lyndon Brown
2//
3// Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
4// copy, modify, or distribute this file except in compliance with said license. You can find copies
5// of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
6// <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
7// respectively.
8
9//! Formatted output support helpers
10
11/// Are ANSI format sequences supported on stdout?
12///
13/// - On Unix this is reliable, returning `true` only if **stdout** is connected to a tty (as
14/// opposed to being redirected to a file or other program).
15/// - On Windows, this gives an answer on the same principle, however even if returning `true` such
16/// that we know we are connected to a terminal, this does not mean that the terminal actually
17/// supports ANSI control sequences. Before Windows 10 you should assume not. On Windows 10+ you
18/// must use the [`enable_ansi_support()`] function to turn on support.
19#[inline(always)]
20pub fn fmt_supported_stdout() -> bool {
21 atty::is(atty::Stream::Stdout)
22}
23
24/// Are ANSI format sequences supported on stderr?
25///
26/// - On Unix this is reliable, returning `true` only if **stderr** is connected to a tty (as
27/// opposed to being redirected to a file or other program).
28/// - On Windows, this gives an answer on the same principle, however even if returning `true` such
29/// that we know we are connected to a terminal, this does not mean that the terminal actually
30/// supports ANSI control sequences. Before Windows 10 you should assume not. On Windows 10+ you
31/// must use the [`enable_ansi_support()`] function to turn on support.
32#[inline(always)]
33pub fn fmt_supported_stderr() -> bool {
34 atty::is(atty::Stream::Stderr)
35}
36
37/// Should I use formatting on stdout?
38///
39/// Convenience helper, taking user preference, and checking support. Combines them to give an
40/// answer of `true` for yes, `false` for no.
41#[inline(always)]
42pub fn use_fmt_stdout(user_pref: bool) -> bool {
43 user_pref && fmt_supported_stdout()
44}
45
46/// Should I use formatting on stderr?
47///
48/// Convenience helper, taking user preference, and checking support. Combines them to give an
49/// answer of `true` for yes, `false` for no.
50#[inline(always)]
51pub fn use_fmt_stderr(user_pref: bool) -> bool {
52 user_pref && fmt_supported_stderr()
53}
54
55/*
56 Copied and slightly modified from the `ansi_term` crate (MIT licensed).
57*/
58/// Enables ANSI code support on Windows 10.
59///
60/// This uses Windows API calls to alter the properties of the console that
61/// the program is running in.
62///
63/// https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
64///
65/// Returns a `Result` with the Windows error code if unsuccessful.
66#[cfg(windows)]
67pub fn enable_ansi_support() -> Result<(), u32> {
68 use winapi::um::processenv::GetStdHandle;
69 use winapi::um::errhandlingapi::GetLastError;
70 use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
71
72 const STD_OUT_HANDLE: u32 = -11i32 as u32;
73 const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x0004;
74
75 macro_rules! handle_error {
76 ( $result:expr ) => { match GetLastError() { 0 => Ok($result), e => Err(e), } };
77 }
78
79 unsafe {
80 // https://docs.microsoft.com/en-us/windows/console/getstdhandle
81 let std_out_handle = handle_error!(GetStdHandle(STD_OUT_HANDLE))?;
82
83 // https://docs.microsoft.com/en-us/windows/console/getconsolemode
84 let mut console_mode: u32 = 0;
85 handle_error!(GetConsoleMode(std_out_handle, &mut console_mode))?;
86
87 // VT processing not already enabled?
88 if console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 {
89 // https://docs.microsoft.com/en-us/windows/console/setconsolemode
90 handle_error!(SetConsoleMode(std_out_handle,
91 console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))?;
92 }
93 }
94 Ok(())
95}