Branch data Line data Source code
1 : : // Copyright (C) 2023 The Qt Company Ltd.
2 : : // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3 : :
4 : : #include <QAtomicInt>
5 : : #include <QByteArray>
6 : : #include <QCoreApplication>
7 : :
8 : : #include "console.h"
9 : : #include "utilities.h"
10 : :
11 : : #if defined(Q_OS_WIN)
12 : : # include <windows.h>
13 : : # include <io.h>
14 : : # include <QOperatingSystemVersion>
15 : : # include <QThread>
16 : :
17 : : Q_CORE_EXPORT void qWinMsgHandler(QtMsgType t, const char* str);
18 : : #else
19 : : # include <unistd.h>
20 : : # include <sys/ioctl.h>
21 : : # include <termios.h>
22 : : # include <signal.h>
23 : : # if defined(Q_OS_MACOS)
24 : : # include <libproc.h>
25 : : # endif
26 : :
27 : : # include "unixsignalhandler.h"
28 : : #endif
29 : :
30 : : using namespace Qt::StringLiterals;
31 : :
32 : :
33 : : struct ConsoleGlobal
34 : : {
35 : : bool stdoutSupportsAnsiColor = false;
36 : : bool stderrSupportsAnsiColor = false;
37 : : bool stdoutIsConsoleWindow = false;
38 : : bool stderrIsConsoleWindow = false;
39 : : bool isRunningInQtCreator = false;
40 : : QAtomicInt consoleWidthCached = 0;
41 : :
42 : : ConsoleGlobal();
43 : : };
44 : :
45 : 115 : Q_GLOBAL_STATIC(ConsoleGlobal, cg)
46 : :
47 : : QT_USE_NAMESPACE_AM
48 : :
49 : 115 : ConsoleGlobal::ConsoleGlobal()
50 : : {
51 : 115 : enum { ColorAuto, ColorOff, ColorOn } forceColor = ColorAuto;
52 : 115 : const QByteArray forceColorOutput = qgetenv("AM_FORCE_COLOR_OUTPUT");
53 [ + + - + ]: 115 : if (forceColorOutput == "off" || forceColorOutput == "0")
54 : : forceColor = ColorOff;
55 [ + - + - ]: 114 : else if (forceColorOutput == "on" || forceColorOutput == "1")
56 : : forceColor = ColorOn;
57 : :
58 : : #if defined(Q_OS_UNIX)
59 [ - + ]: 115 : if (::isatty(STDOUT_FILENO)) {
60 : 0 : stdoutIsConsoleWindow = true;
61 : 0 : stdoutSupportsAnsiColor = true;
62 : : }
63 [ - + ]: 115 : if (::isatty(STDERR_FILENO)) {
64 : 0 : stderrIsConsoleWindow = true;
65 : 0 : stderrSupportsAnsiColor = true;
66 : : }
67 : :
68 : : #elif defined(Q_OS_WIN)
69 : : if (HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); (h != INVALID_HANDLE_VALUE)) {
70 : : // enable ANSI mode on Windows 10
71 : : DWORD mode = 0;
72 : : if (GetConsoleMode(h, &mode)) {
73 : : mode |= 0x04;
74 : : if (SetConsoleMode(h, mode)) {
75 : : stdoutSupportsAnsiColor = true;
76 : : stdoutIsConsoleWindow = true;
77 : : }
78 : : }
79 : : }
80 : : if (HANDLE h = GetStdHandle(STD_ERROR_HANDLE); (h != INVALID_HANDLE_VALUE)) {
81 : : // enable ANSI mode on Windows 10
82 : : DWORD mode = 0;
83 : : if (GetConsoleMode(h, &mode)) {
84 : : mode |= 0x04;
85 : : if (SetConsoleMode(h, mode)) {
86 : : stdoutSupportsAnsiColor = true;
87 : : stderrIsConsoleWindow = true;
88 : : }
89 : : }
90 : : }
91 : : #endif
92 : :
93 [ + - ]: 115 : qint64 pid = QCoreApplication::applicationPid();
94 [ + + ]: 690 : for (int level = 0; level < 5; ++level) { // only check 5 levels deep
95 [ + - ]: 575 : pid = getParentPid(pid);
96 [ + - ]: 575 : if (pid <= 1)
97 : : break;
98 : :
99 : : #if defined(Q_OS_LINUX)
100 [ + - + - ]: 1150 : QFileInfo fi(u"/proc/%1/exe"_s.arg(pid));
101 [ + - - + ]: 1150 : if (fi.symLinkTarget().contains(u"qtcreator")) {
102 : 0 : isRunningInQtCreator = true;
103 : 0 : break;
104 : : }
105 : : #elif defined(Q_OS_MACOS)
106 : : static char buffer[PROC_PIDPATHINFO_MAXSIZE + 1];
107 : : int len = proc_pidpath(pid, buffer, sizeof(buffer) - 1);
108 : : if ((len > 0) && QByteArray::fromRawData(buffer, len).contains("Qt Creator")) {
109 : : isRunningInQtCreator = true;
110 : : break;
111 : : }
112 : : #elif defined(Q_OS_WIN)
113 : : HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
114 : : if (hProcess) {
115 : : wchar_t exeName[1024] = { 0 };
116 : : DWORD exeNameSize = sizeof(exeName) - 1;
117 : : if (QueryFullProcessImageNameW(hProcess, 0, exeName, &exeNameSize)) {
118 : : if (QString::fromWCharArray(exeName, exeNameSize).contains(u"qtcreator"_s)) {
119 : : isRunningInQtCreator = true;
120 : : break;
121 : : }
122 : : }
123 : : }
124 : : #endif
125 : 575 : }
126 : :
127 [ + + ]: 115 : if (forceColor != ColorAuto) {
128 : 1 : stdoutSupportsAnsiColor = stderrSupportsAnsiColor = (forceColor == ColorOn);
129 : : } else {
130 [ + - ]: 114 : if (!stdoutSupportsAnsiColor)
131 : 114 : stdoutSupportsAnsiColor = isRunningInQtCreator;
132 [ + - ]: 114 : if (!stderrSupportsAnsiColor)
133 : 114 : stderrSupportsAnsiColor = isRunningInQtCreator;
134 : : }
135 : :
136 : : #if defined(Q_OS_UNIX) && defined(SIGWINCH)
137 [ + - + - ]: 230 : UnixSignalHandler::instance()->install(UnixSignalHandler::RawSignalHandler, SIGWINCH, [](int) {
138 : : // we are in a signal handler, so we just clear the cached value in the atomic int
139 : 0 : cg()->consoleWidthCached = 0;
140 : 0 : });
141 : : #elif defined(Q_OS_WIN)
142 : : class ConsoleThread : public QThread
143 : : {
144 : : public:
145 : : ConsoleThread(QObject *parent)
146 : : : QThread(parent)
147 : : { }
148 : :
149 : : ~ConsoleThread()
150 : : {
151 : : terminate();
152 : : wait();
153 : : }
154 : :
155 : : protected:
156 : : void run() override
157 : : {
158 : : HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
159 : : DWORD mode = 0;
160 : : if (!GetConsoleMode(h, &mode))
161 : : return;
162 : : if (!SetConsoleMode(h, mode | ENABLE_WINDOW_INPUT))
163 : : return;
164 : :
165 : : INPUT_RECORD ir;
166 : : DWORD irRead = 0;
167 : : while (ReadConsoleInputW(h, &ir, 1, &irRead)) {
168 : : if ((irRead == 1) && (ir.EventType == WINDOW_BUFFER_SIZE_EVENT))
169 : : cg()->consoleWidthCached = 0;
170 : : }
171 : : }
172 : : };
173 : : qAddPreRoutine([]() { (new ConsoleThread(QCoreApplication::instance()))->start(); });
174 : : #endif // Q_OS_WIN
175 : 115 : }
176 : :
177 : : QT_BEGIN_NAMESPACE_AM
178 : :
179 : 114 : bool Console::ensureInitialized()
180 : : {
181 : 114 : return cg();
182 : : }
183 : :
184 : 16 : bool Console::stdoutSupportsAnsiColor()
185 : : {
186 : 16 : return cg()->stdoutSupportsAnsiColor;
187 : : }
188 : :
189 : 507 : bool Console::stderrSupportsAnsiColor()
190 : : {
191 : 507 : return cg()->stderrSupportsAnsiColor;
192 : : }
193 : :
194 : 119 : bool Console::isRunningInQtCreator()
195 : : {
196 : 119 : return cg()->isRunningInQtCreator;
197 : : }
198 : :
199 : 0 : bool Console::stdoutIsConsoleWindow()
200 : : {
201 : 0 : return cg()->stdoutIsConsoleWindow;
202 : : }
203 : :
204 : 0 : bool Console::stderrIsConsoleWindow()
205 : : {
206 : 0 : return cg()->stderrIsConsoleWindow;
207 : : }
208 : :
209 : 388 : int Console::width()
210 : : {
211 [ + - ]: 388 : int consoleWidthCalculated = cg()->consoleWidthCached;
212 : :
213 [ + - ]: 388 : if (consoleWidthCalculated <= 0) {
214 [ - + ]: 388 : if (cg()->stderrIsConsoleWindow) {
215 : : #if defined(Q_OS_UNIX)
216 : 0 : struct ::winsize ws;
217 [ # # # # ]: 0 : if ((::ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == 0) && (ws.ws_col > 0))
218 : 0 : consoleWidthCalculated = ws.ws_col;
219 : : #elif defined(Q_OS_WIN)
220 : : HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
221 : : CONSOLE_SCREEN_BUFFER_INFO csbi;
222 : : if (GetConsoleScreenBufferInfo(h, &csbi))
223 : : consoleWidthCalculated = csbi.dwSize.X;
224 : : #endif
225 : : }
226 : 388 : cg()->consoleWidthCached = consoleWidthCalculated;
227 : : }
228 [ + - + - ]: 388 : if ((consoleWidthCalculated <= 0) && cg()->isRunningInQtCreator)
229 : : return 120;
230 : : else
231 : 388 : return consoleWidthCalculated;
232 : : }
233 : :
234 : : QT_END_NAMESPACE_AM
|