forked from mixxxdj/mixxx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconsole.cpp
205 lines (182 loc) · 7.88 KB
/
console.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include "console.h"
#include <stdio.h>
#include <QtDebug>
#include "util/versionstore.h"
#ifdef __WINDOWS__
#include <io.h> // Debug Console
#include <string.h>
#include <conio.h>
#include <strsafe.h>
typedef BOOL(WINAPI* pfGetCurrentConsoleFontEx)(HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
typedef BOOL(WINAPI* pfSetCurrentConsoleFontEx)(HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
Console::Console()
: m_shouldResetCodePage(false),
m_shouldResetConsoleTitle(false),
m_shouldFreeConsole(false) {
// Unlike Linux and MacOS Windows does not support windows and console
// applications at the same time.
//
// We link Mixxx win with the /subsystem:windows flag, to avoid a new
// console popping up when the program is started by a double-click.
// If Mixxx is started from a command line like cmd.exe the output is not
// shown by default.
// Here we fixing that by detecting the console case and redirect to output
// to it if not already redirected.
// Note: GetFileType needs to be called before AttachConsole(),
// else it always returns FILE_TYPE_CHAR
DWORD typeStdIn = GetFileType(GetStdHandle(STD_INPUT_HANDLE));
DWORD typeStdOut = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
DWORD typeStdErr = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
// we are started from a console process
m_shouldFreeConsole = true;
if (typeStdIn == FILE_TYPE_UNKNOWN) {
// the input is not already redirected
FILE* pStdin = stdin;
if (freopen_s(&pStdin, "CONIN$", "r", stdin)) {
qWarning() << "Could not open stdin. Error code:" << GetLastError();
}
}
if (typeStdOut == FILE_TYPE_UNKNOWN) {
// the output is not already redirected
FILE* pStdout = stdout;
if (freopen_s(&pStdout, "CONOUT$", "w", stdout)) {
qWarning() << "Could not open stdout. Error code:" << GetLastError();
} else {
if (setvbuf(stdout, NULL, _IONBF, 0) != 0) {
qWarning() << "Setting no buffer for stdout failed.";
}
}
}
if (typeStdErr == FILE_TYPE_UNKNOWN) {
// the error is not already redirected
FILE* pStderr = stderr;
if (freopen_s(&pStderr, "CONOUT$", "w", stderr)) {
qWarning() << "Could not open stderr. Error code:" << GetLastError();
} else {
if (setvbuf(stdout, NULL, _IONBF, 0) != 0) {
qWarning() << "Setting no buffer for stderr failed.";
}
}
}
// Save current code page
m_oldCodePage = GetConsoleOutputCP();
m_shouldResetCodePage = true;
// Verify using the unicode font "Consolas"
HMODULE kernel32_dll = LoadLibraryW(L"kernel32.dll");
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (kernel32_dll && hStdOut != INVALID_HANDLE_VALUE) {
pfGetCurrentConsoleFontEx pfGCCFX =
(pfGetCurrentConsoleFontEx) GetProcAddress(kernel32_dll,
"GetCurrentConsoleFontEx"); // Supported from Windows Vista
pfSetCurrentConsoleFontEx pfSCCFX =
(pfSetCurrentConsoleFontEx) GetProcAddress(kernel32_dll,
"SetCurrentConsoleFontEx"); // Supported from Windows Vista
bool setFont = true;
if (pfGCCFX) {
CONSOLE_FONT_INFOEX font;
if (pfGCCFX(hStdOut, FALSE, &font)) {
if (wcsncmp(font.FaceName, L"Consolas", 8) == 0) {
// Nothing to do
setFont = false;
}
}
}
if (setFont) {
if (pfSCCFX) {
// Use a unicode font
CONSOLE_FONT_INFOEX newFont;
newFont.cbSize = sizeof newFont;
newFont.nFont = 0;
newFont.dwFontSize.X = 0;
newFont.dwFontSize.Y = 14;
newFont.FontFamily = FF_DONTCARE;
newFont.FontWeight = FW_NORMAL;
wcscpy_s(newFont.FaceName, L"Consolas");
pfSCCFX(hStdOut, FALSE, &newFont);
} else {
// This happens on Windows XP
qWarning() << "The console font may not support non ANSI characters." <<
"In case of character issues switch to font \"Lucida Console\"";
}
}
TCHAR szNewTitle[MAX_PATH];
// Save current console title.
if (GetConsoleTitle(m_oldTitle, MAX_PATH)) {
// Build new console title string.
#ifdef UNICODE
StringCchPrintf(szNewTitle,
MAX_PATH,
TEXT("%s : %s"),
m_oldTitle,
VersionStore::applicationName().utf16());
#else
StringCchPrintf(szNewTitle,
MAX_PATH,
TEXT("%s : %s"),
m_oldTitle,
VersionStore::applicationName().toLocal8Bit().data());
#endif
// Set console title to new title
if (SetConsoleTitle(szNewTitle)) {
m_shouldResetConsoleTitle = true;
} else {
qWarning() << "SetConsoleTitle failed" << GetLastError();
}
}
}
// Setup Windows console encoding
// toLocal8Bit() returns the ANSI file encoding format
// this does not necessarily match the OEM console encoding
// https://www.microsoft.com/resources/msdn/goglobal/default.mspx
// In case of a German Windows XP to 10 console encoding is cp850
// where files encoding is cp1252
// Qt has no solution for it https://bugreports.qt.io/browse/QTBUG-13303
// http://stackoverflow.com/questions/1259084/what-encoding-code-page-is-cmd-exe-using
// We try to change the console encoding to file encoding
// For a German windows we expect
// LOCALE_IDEFAULTANSICODEPAGE "1252" // ANSI Codepage used by Qt toLocal8Bit
// LOCALE_IDEFAULTCODEPAGE "850" // OEM Codepage Console
// set console to the default ANSI Code Page
UINT defaultCodePage;
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE,
reinterpret_cast<LPWSTR>(&defaultCodePage),
sizeof(defaultCodePage)) != 0) {
SetConsoleOutputCP(defaultCodePage);
} else {
qWarning() << "GetLocaleInfo failed. Error code:" << GetLastError();
}
} else {
// started by double click
// no need to deal with a console
}
}
Console::~Console() {
// Reset Windows console to old code page
// We need to stick with the unicode font since
// changing back will destroy the console history
if (m_shouldResetCodePage) {
SetConsoleOutputCP(m_oldCodePage);
}
if (m_shouldResetConsoleTitle) {
if (!SetConsoleTitle(m_oldTitle)) {
qWarning() << "SetConsoleTitle failed" << GetLastError();
}
}
if (m_shouldFreeConsole) {
// Note: The console has already written the command on top of the output
// because it was originally released due to the /subsystem:windows flag.
// We may send a fake "Enter" key here, using SendInput() to get a new
// command prompt, but this executes a user entry unconditionally or has other
// bad side effects.
FreeConsole();
}
}
#else // __WINDOWS__
// Nothing to do on non Windows targets
Console::Console() {
}
Console::~Console() {
}
#endif // __WINDOWS__