1// LAF Base Library
2// Copyright (c) 2021 Igara Studio S.A.
3// Copyright (c) 2001-2016 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "base/system_console.h"
13
14#ifdef LAF_WINDOWS
15
16// Windows needs some adjustments to the console if the process is
17// linked with /subsystem:windows. These adjustments are not great but
18// are good enough. See system_console.h for more information.
19
20#include <cstdio>
21#include <iostream>
22
23#include <windows.h>
24#include <io.h>
25
26namespace base {
27
28static bool withConsole = false;
29
30SystemConsole::SystemConsole()
31{
32 // If some output handle (stdout/stderr) is not attached to a
33 // console, we can attach the process to the parent process console.
34 bool unknownOut = (::GetFileType(::GetStdHandle(STD_OUTPUT_HANDLE)) == FILE_TYPE_UNKNOWN);
35 bool unknownErr = (::GetFileType(::GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_UNKNOWN);
36 if (unknownOut || unknownErr) {
37 // AttachConsole() can fails if the parent console doesn't have a
38 // console, which is the most common, i.e. when the user
39 // double-click a shortcut to start the program.
40 if (::AttachConsole(ATTACH_PARENT_PROCESS)) {
41 // In this case we're attached to the parent process
42 // (e.g. cmd.exe) console.
43 withConsole = true;
44 }
45 }
46
47 if (withConsole) {
48 // Here we redirect stdout/stderr to use the parent console's ones.
49 if (unknownOut) std::freopen("CONOUT$", "w", stdout);
50 if (unknownErr) std::freopen("CONOUT$", "w", stderr);
51
52 // Synchronize C++'s cout/cerr streams with C's stdout/stderr.
53 std::ios::sync_with_stdio();
54 }
55}
56
57SystemConsole::~SystemConsole()
58{
59 if (withConsole) {
60 ::FreeConsole();
61 withConsole = false;
62 }
63}
64
65void SystemConsole::prepareShell()
66{
67 if (withConsole)
68 ::FreeConsole();
69
70 // In this case, for a better user experience, we create a new
71 // console so we can write text in a synchronized way with the
72 // console. (The parent console stdin is not reliable for
73 // interactive command input in the current state, without doing
74 // this the input from the cmd.exe would be executed by cmd.exe and
75 // by our app.)
76 withConsole = true;
77 ::AllocConsole();
78 ::AttachConsole(::GetCurrentProcessId());
79
80 std::freopen("CONIN$", "r", stdin);
81 std::freopen("CONOUT$", "w", stdout);
82 std::freopen("CONOUT$", "w", stderr);
83 std::ios::sync_with_stdio();
84}
85
86}
87
88#else // On Unix-like systems the console works just fine
89
90namespace base {
91
92SystemConsole::SystemConsole() { }
93SystemConsole::~SystemConsole() { }
94void SystemConsole::prepareShell() { }
95
96}
97
98#endif
99