1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2017 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
25 #define WIN32_LEAN_AND_MEAN 1
29 #include <MUtils/Terminal.h>
30 #include <MUtils/Global.h>
31 #include <MUtils/OSSupport.h>
32 #include "Utils_Win32.h"
33 #include "CriticalSection_Win32.h"
37 #include <QStringList>
49 #define stricmp(X,Y) _stricmp((X),(Y))
52 #define VALID_HANLDE(X) (((X) != NULL) && ((X) != INVALID_HANDLE_VALUE))
54 ///////////////////////////////////////////////////////////////////////////////
56 ///////////////////////////////////////////////////////////////////////////////
59 static MUtils::Internal::CriticalSection g_terminal_lock
;
61 //Is terminal attached?
62 static volatile bool g_terminal_attached
= false;
64 //Terminal output buffer
65 static const size_t BUFF_SIZE
= 8192;
66 static char g_conOutBuff
[BUFF_SIZE
] = { '\0' };
69 static QScopedPointer
<std::filebuf
> g_fileBuf_stdout
;
70 static QScopedPointer
<std::filebuf
> g_fileBuf_stderr
;
73 static QScopedPointer
<QFile
> g_terminal_log_file
;
76 static HICON g_terminal_icon
= NULL
;
78 ///////////////////////////////////////////////////////////////////////////////
80 ///////////////////////////////////////////////////////////////////////////////
82 static inline void make_timestamp(char *timestamp
, const size_t &buffsize
)
88 if(localtime_s(&timeinfo
, &rawtime
) == 0)
90 strftime(timestamp
, buffsize
, "%H:%M:%S", &timeinfo
);
98 static inline bool null_or_whitespace(const char *const str
)
105 if (!(isspace(str
[pos
]) || iscntrl(str
[pos
])))
115 static inline size_t clean_string(char *const str
)
117 bool space_flag
= true;
118 size_t src
= 0, out
= 0;
122 if (isspace(str
[src
]) || iscntrl(str
[src
])) /*replace any space-sequence with a single space character*/
131 else /*otherwise we'll just copy over the current character*/
142 if (space_flag
&& (out
> 0)) /*trim trailing space, if any*/
151 static inline void set_hicon(HICON
*const ptr
, const HICON val
)
160 ///////////////////////////////////////////////////////////////////////////////
162 ///////////////////////////////////////////////////////////////////////////////
164 static inline std::filebuf
*terminal_connect(FILE *const fs
, std::ostream
&os
)
166 std::filebuf
*result
= NULL
;
168 if (freopen_s(&temp
, "CONOUT$", "wb", fs
) == 0)
170 os
.rdbuf(result
= new std::filebuf(temp
));
175 static void terminal_shutdown(void)
177 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
179 if (g_terminal_attached
)
181 g_fileBuf_stdout
.reset();
182 g_fileBuf_stderr
.reset();
184 if(stdout
) freopen_s(&temp
[0], "NUL", "wb", stdout
);
185 if(stderr
) freopen_s(&temp
[1], "NUL", "wb", stderr
);
187 set_hicon(&g_terminal_icon
, NULL
);
188 g_terminal_attached
= false;
192 void MUtils::Terminal::setup(int &argc
, char **argv
, const char* const appName
, const bool forceEnabled
)
194 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
195 bool enableConsole
= (MUTILS_DEBUG
) || forceEnabled
;
199 wchar_t *logfile
= NULL
; size_t logfile_len
= 0;
200 if(!_wdupenv_s(&logfile
, &logfile_len
, L
"MUTILS_LOGFILE"))
202 if(logfile
&& (logfile_len
> 0))
204 g_terminal_log_file
.reset(new QFile(MUTILS_QSTR(logfile
)));
205 if(g_terminal_log_file
->open(QIODevice::WriteOnly
))
207 static const char MARKER
[3] = { char(0xEF), char(0xBB), char(0xBF) };
208 g_terminal_log_file
->write(MARKER
, 3);
217 for(int i
= 0; i
< argc
; i
++)
219 if(!stricmp(argv
[i
], "--console"))
221 enableConsole
= true;
223 else if(!stricmp(argv
[i
], "--no-console"))
225 enableConsole
= false;
232 if(!g_terminal_attached
)
234 if(AllocConsole() != FALSE
)
236 SetConsoleOutputCP(CP_UTF8
);
237 SetConsoleCtrlHandler(NULL
, TRUE
);
238 if(appName
&& appName
[0])
241 _snprintf_s(title
, 128, _TRUNCATE
, "%s | Debug Console", appName
);
242 SetConsoleTitleA(title
);
244 g_terminal_attached
= true;
248 if(g_terminal_attached
)
250 g_fileBuf_stdout
.reset(terminal_connect(stdout
, std::cout
));
251 g_fileBuf_stderr
.reset(terminal_connect(stderr
, std::cerr
));
253 atexit(terminal_shutdown
);
255 const HWND hwndConsole
= GetConsoleWindow();
256 if((hwndConsole
!= NULL
) && (hwndConsole
!= INVALID_HANDLE_VALUE
))
258 HMENU hMenu
= GetSystemMenu(hwndConsole
, 0);
259 EnableMenuItem(hMenu
, SC_CLOSE
, MF_BYCOMMAND
| MF_GRAYED
);
260 RemoveMenu(hMenu
, SC_CLOSE
, MF_BYCOMMAND
);
262 SetWindowPos (hwndConsole
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|SWP_FRAMECHANGED
);
263 SetWindowLong(hwndConsole
, GWL_STYLE
, GetWindowLong(hwndConsole
, GWL_STYLE
) & (~WS_MAXIMIZEBOX
) & (~WS_MINIMIZEBOX
));
264 SetWindowPos (hwndConsole
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|SWP_FRAMECHANGED
);
270 ///////////////////////////////////////////////////////////////////////////////
272 ///////////////////////////////////////////////////////////////////////////////
275 static const WORD COLOR_RED
= FOREGROUND_RED
| FOREGROUND_INTENSITY
;
276 static const WORD COLOR_YELLOW
= FOREGROUND_GREEN
| FOREGROUND_RED
| FOREGROUND_INTENSITY
;
277 static const WORD COLOR_WHITE
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
| FOREGROUND_INTENSITY
;
278 static const WORD COLOR_DEFAULT
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
280 static void set_terminal_color(FILE *const fp
, const WORD
&attributes
)
282 if(_isatty(_fileno(fp
)))
284 const HANDLE hConsole
= (HANDLE
)(_get_osfhandle(_fileno(fp
)));
285 if (VALID_HANLDE(hConsole
))
287 SetConsoleTextAttribute(hConsole
, attributes
);
292 ///////////////////////////////////////////////////////////////////////////////
294 ///////////////////////////////////////////////////////////////////////////////
296 static const char *const FORMAT
= "[%c][%s] %s\r\n";
297 static const char *const GURU_MEDITATION
= "\n\nGURU MEDITATION !!!\n\n";
299 static void write_to_logfile(QFile
*const file
, const int &type
, const char *const message
)
303 if (null_or_whitespace(message
))
305 return; /*don't write empty message to log file*/
308 static char timestamp
[32];
309 make_timestamp(timestamp
, 32);
315 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'C', timestamp
, message
);
318 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'W', timestamp
, message
);
321 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'I', timestamp
, message
);
327 if (clean_string(g_conOutBuff
) > 0)
329 file
->write(g_conOutBuff
);
335 static void write_to_debugger(const int &type
, const char *const message
)
339 if (null_or_whitespace(message
))
341 return; /*don't send empty message to debugger*/
344 static char timestamp
[32];
345 make_timestamp(timestamp
, 32);
351 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'C', timestamp
, message
);
354 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'W', timestamp
, message
);
357 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'I', timestamp
, message
);
363 if (clean_string(g_conOutBuff
) > 0)
365 OutputDebugStringA(g_conOutBuff
);
370 static void write_to_terminal(const int &type
, const char *const message
)
376 set_terminal_color(stderr
, COLOR_RED
);
377 fprintf(stderr
, GURU_MEDITATION
);
378 fprintf(stderr
, "%s\n", message
);
381 set_terminal_color(stderr
, COLOR_YELLOW
);
382 fprintf(stderr
, "%s\n", message
);
385 set_terminal_color(stderr
, COLOR_WHITE
);
386 fprintf(stderr
, "%s\n", message
);
393 void MUtils::Terminal::write(const int &type
, const char *const message
)
395 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
397 if(g_terminal_attached
)
399 write_to_terminal(type
, message
);
403 write_to_debugger(type
, message
);
406 if(!g_terminal_log_file
.isNull())
408 write_to_logfile(g_terminal_log_file
.data(), type
, message
);
412 ///////////////////////////////////////////////////////////////////////////////
414 ///////////////////////////////////////////////////////////////////////////////
416 void MUtils::Terminal::set_icon(const QIcon
&icon
)
418 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
420 if(g_terminal_attached
&& (!(icon
.isNull() || MUtils::OS::running_on_wine())))
422 if(const HICON hIcon
= (HICON
) MUtils::Win32Utils::qicon_to_hicon(&icon
, 16, 16))
424 typedef BOOL(__stdcall
*SetConsoleIconFun
)(HICON
);
425 bool success
= false;
426 if (const SetConsoleIconFun pSetConsoleIconFun
= MUtils::Win32Utils::resolve
<SetConsoleIconFun
>(QLatin1String("kernel32"), QLatin1String("SetConsoleIcon")))
428 const DWORD before
= GetLastError();
429 if (pSetConsoleIconFun(hIcon
))
435 const DWORD error
= GetLastError();
436 qWarning("SetConsoleIcon() has failed! [Error: 0x%08X]", error
);
441 const HWND hwndConsole
= GetConsoleWindow();
442 if ((hwndConsole
!= NULL
) && (hwndConsole
!= INVALID_HANDLE_VALUE
))
444 SendMessage(hwndConsole
, WM_SETICON
, ICON_SMALL
, LPARAM(hIcon
));
450 set_hicon(&g_terminal_icon
, hIcon
);