1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2015 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
;
75 ///////////////////////////////////////////////////////////////////////////////
77 ///////////////////////////////////////////////////////////////////////////////
79 static inline void make_timestamp(char *timestamp
, const size_t &buffsize
)
85 if(localtime_s(&timeinfo
, &rawtime
) == 0)
87 strftime(timestamp
, buffsize
, "%H:%M:%S", &timeinfo
);
95 static inline bool null_or_whitespace(const char *const str
)
102 if (!(isspace(str
[pos
]) || iscntrl(str
[pos
])))
112 static inline size_t clean_string(char *const str
)
114 bool space_flag
= true;
115 size_t src
= 0, out
= 0;
119 if (isspace(str
[src
]) || iscntrl(str
[src
])) /*replace any space-sequence with a single space character*/
128 else /*otherwise we'll just copy over the current character*/
139 if (space_flag
&& (out
> 0)) /*trim trailing space, if any*/
148 ///////////////////////////////////////////////////////////////////////////////
150 ///////////////////////////////////////////////////////////////////////////////
152 static inline std::filebuf
*terminal_connect(FILE *const fs
, std::ostream
&os
)
154 std::filebuf
*result
= NULL
;
156 if (freopen_s(&temp
, "CONOUT$", "wb", fs
) == 0)
158 os
.rdbuf(result
= new std::filebuf(temp
));
163 static void terminal_shutdown(void)
165 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
167 if (g_terminal_attached
)
169 g_fileBuf_stdout
.reset();
170 g_fileBuf_stderr
.reset();
172 if(stdout
) freopen_s(&temp
[0], "NUL", "wb", stdout
);
173 if(stderr
) freopen_s(&temp
[1], "NUL", "wb", stderr
);
175 g_terminal_attached
= false;
179 void MUtils::Terminal::setup(int &argc
, char **argv
, const char* const appName
, const bool forceEnabled
)
181 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
182 bool enableConsole
= (MUTILS_DEBUG
) || forceEnabled
;
186 wchar_t *logfile
= NULL
; size_t logfile_len
= 0;
187 if(!_wdupenv_s(&logfile
, &logfile_len
, L
"MUTILS_LOGFILE"))
189 if(logfile
&& (logfile_len
> 0))
191 g_terminal_log_file
.reset(new QFile(MUTILS_QSTR(logfile
)));
192 if(g_terminal_log_file
->open(QIODevice::WriteOnly
))
194 static const char MARKER
[3] = { char(0xEF), char(0xBB), char(0xBF) };
195 g_terminal_log_file
->write(MARKER
, 3);
204 for(int i
= 0; i
< argc
; i
++)
206 if(!stricmp(argv
[i
], "--console"))
208 enableConsole
= true;
210 else if(!stricmp(argv
[i
], "--no-console"))
212 enableConsole
= false;
219 if(!g_terminal_attached
)
221 if(AllocConsole() != FALSE
)
223 SetConsoleOutputCP(CP_UTF8
);
224 SetConsoleCtrlHandler(NULL
, TRUE
);
225 if(appName
&& appName
[0])
228 _snprintf_s(title
, 128, _TRUNCATE
, "%s | Debug Console", appName
);
229 SetConsoleTitleA(title
);
231 g_terminal_attached
= true;
235 if(g_terminal_attached
)
237 g_fileBuf_stdout
.reset(terminal_connect(stdout
, std::cout
));
238 g_fileBuf_stderr
.reset(terminal_connect(stderr
, std::cerr
));
240 atexit(terminal_shutdown
);
242 const HWND hwndConsole
= GetConsoleWindow();
243 if((hwndConsole
!= NULL
) && (hwndConsole
!= INVALID_HANDLE_VALUE
))
245 HMENU hMenu
= GetSystemMenu(hwndConsole
, 0);
246 EnableMenuItem(hMenu
, SC_CLOSE
, MF_BYCOMMAND
| MF_GRAYED
);
247 RemoveMenu(hMenu
, SC_CLOSE
, MF_BYCOMMAND
);
249 SetWindowPos (hwndConsole
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|SWP_FRAMECHANGED
);
250 SetWindowLong(hwndConsole
, GWL_STYLE
, GetWindowLong(hwndConsole
, GWL_STYLE
) & (~WS_MAXIMIZEBOX
) & (~WS_MINIMIZEBOX
));
251 SetWindowPos (hwndConsole
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|SWP_FRAMECHANGED
);
257 ///////////////////////////////////////////////////////////////////////////////
259 ///////////////////////////////////////////////////////////////////////////////
262 static const WORD COLOR_RED
= FOREGROUND_RED
| FOREGROUND_INTENSITY
;
263 static const WORD COLOR_YELLOW
= FOREGROUND_GREEN
| FOREGROUND_RED
| FOREGROUND_INTENSITY
;
264 static const WORD COLOR_WHITE
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
| FOREGROUND_INTENSITY
;
265 static const WORD COLOR_DEFAULT
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
267 static void set_terminal_color(FILE *const fp
, const WORD
&attributes
)
269 if(_isatty(_fileno(fp
)))
271 const HANDLE hConsole
= (HANDLE
)(_get_osfhandle(_fileno(fp
)));
272 if (VALID_HANLDE(hConsole
))
274 SetConsoleTextAttribute(hConsole
, attributes
);
279 ///////////////////////////////////////////////////////////////////////////////
281 ///////////////////////////////////////////////////////////////////////////////
283 static const char *const FORMAT
= "[%c][%s] %s\r\n";
284 static const char *const GURU_MEDITATION
= "\n\nGURU MEDITATION !!!\n\n";
286 static void write_to_logfile(QFile
*const file
, const int &type
, const char *const message
)
290 if (null_or_whitespace(message
))
292 return; /*don't write empty message to log file*/
295 static char timestamp
[32];
296 make_timestamp(timestamp
, 32);
302 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'C', timestamp
, message
);
305 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'W', timestamp
, message
);
308 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'I', timestamp
, message
);
314 if (clean_string(g_conOutBuff
) > 0)
316 file
->write(g_conOutBuff
);
322 static void write_to_debugger(const int &type
, const char *const message
)
326 if (null_or_whitespace(message
))
328 return; /*don't send empty message to debugger*/
331 static char timestamp
[32];
332 make_timestamp(timestamp
, 32);
338 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'C', timestamp
, message
);
341 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'W', timestamp
, message
);
344 len
= _snprintf_s(g_conOutBuff
, BUFF_SIZE
, _TRUNCATE
, FORMAT
, 'I', timestamp
, message
);
350 if (clean_string(g_conOutBuff
) > 0)
352 OutputDebugStringA(g_conOutBuff
);
357 static void write_to_terminal(const int &type
, const char *const message
)
363 set_terminal_color(stderr
, COLOR_RED
);
364 fprintf(stderr
, GURU_MEDITATION
);
365 fprintf(stderr
, "%s\n", message
);
368 set_terminal_color(stderr
, COLOR_YELLOW
);
369 fprintf(stderr
, "%s\n", message
);
372 set_terminal_color(stderr
, COLOR_WHITE
);
373 fprintf(stderr
, "%s\n", message
);
380 void MUtils::Terminal::write(const int &type
, const char *const message
)
382 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
384 if(g_terminal_attached
)
386 write_to_terminal(type
, message
);
390 write_to_debugger(type
, message
);
393 if(!g_terminal_log_file
.isNull())
395 write_to_logfile(g_terminal_log_file
.data(), type
, message
);
399 ///////////////////////////////////////////////////////////////////////////////
401 ///////////////////////////////////////////////////////////////////////////////
403 void MUtils::Terminal::set_icon(const QIcon
&icon
)
405 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
407 if(g_terminal_attached
&& (!(icon
.isNull() || MUtils::OS::running_on_wine())))
409 typedef DWORD(__stdcall
*SetConsoleIconFun
)(HICON
);
410 if(const SetConsoleIconFun setConsoleIconFun
= MUtils::Win32Utils::resolve
<SetConsoleIconFun
>(QLatin1String("kernel32"), QLatin1String("SetConsoleIcon")))
412 if(HICON hIcon
= (HICON
) MUtils::Win32Utils::qicon_to_hicon(icon
, 16, 16))
414 setConsoleIconFun(hIcon
);