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}