1
//! Helper functions for handling the Windows console from a GUI context.
2
//!
3
//! Windows subsystem applications must explicitly attach to an existing console
4
//! before stdio works, and if not available, create their own if they wish to
5
//! print anything.
6
//!
7
//! These functions enable that, primarily for the purposes of displaying Rust
8
//! panics.
9

            
10
use std::result::Result;
11

            
12
use merc_utilities::MercError;
13
#[cfg(windows)]
14
use winapi::um::consoleapi::AllocConsole;
15

            
16
#[cfg(windows)]
17
use winapi::um::wincon::ATTACH_PARENT_PROCESS;
18
#[cfg(windows)]
19
use winapi::um::wincon::AttachConsole;
20
#[cfg(windows)]
21
use winapi::um::wincon::FreeConsole;
22
#[cfg(windows)]
23
use winapi::um::wincon::GetConsoleWindow;
24

            
25
pub struct Console {
26
    #[cfg(windows)]
27
    attached: bool,
28
}
29

            
30
/// Initialises the console. On Windows this either attaches to the
31
pub fn init() -> Result<Console, MercError> {
32
    #[cfg(windows)]
33
    unsafe {
34
        // SAFETY: Only unsafe because we use the winapi crate to call Windows API functions.
35
        // Check if we're attached to an existing Windows console
36
        if GetConsoleWindow().is_null() {
37
            // Try to attach to an existing Windows console.
38
            //
39
            // It's normally a no-brainer to call this - it just makes println! and friends
40
            // work as expected, without cluttering the screen with a console in the general
41
            // case.
42
            if AttachConsole(ATTACH_PARENT_PROCESS) == 0 {
43
                // Try to attach to a console, and if not, allocate ourselves a new one.
44
                if AllocConsole() != 0 {
45
                    Ok(Console { attached: false })
46
                } else {
47
                    Err("Failed to attach to a console, and to create one".into())
48
                }
49
            } else {
50
                // We attached to an existing console.
51
                Ok(Console { attached: true })
52
            }
53
        } else {
54
            // The program was started with a console attached.
55
            Ok(Console { attached: true })
56
        }
57
    }
58

            
59
    #[cfg(not(windows))]
60
    {
61
        Ok(Console {})
62
    }
63
}
64

            
65
impl Drop for Console {
66
    fn drop(&mut self) {
67
        // Free the allocated console, when it was not attached.
68
        #[cfg(windows)]
69
        if !self.attached {
70
            unsafe { FreeConsole() };
71
        }
72
    }
73
}