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 ///////////////////////////////////////////////////////////////////////////////
54 ///////////////////////////////////////////////////////////////////////////////
56 static void make_timestamp(char *timestamp
, const size_t &buffsize
)
62 if(localtime_s(&timeinfo
, &rawtime
) == 0)
64 strftime(timestamp
, 32, "%H:%M:%S", &timeinfo
);
72 static const char *clean_str(char *str
)
76 while((*ptr
) && isspace(*ptr
))
82 while((*str
) && isspace(*str
))
88 size_t pos
= strlen(str
);
91 if(isspace(str
[--pos
]))
102 ///////////////////////////////////////////////////////////////////////////////
104 ///////////////////////////////////////////////////////////////////////////////
106 #define REPLACE_STANDARD_STREAM(TYPE, HANDLE) do \
108 const int fd_##TYPE = _open_osfhandle((intptr_t)GetStdHandle(HANDLE), flags); \
109 FILE *const file_##TYPE = (fd_##TYPE >= 0) ? _fdopen(fd_##TYPE, "wb") : NULL; \
112 g_terminal_backup_file_##TYPE = *(std##TYPE); \
113 *(std##TYPE) = *(file_##TYPE); \
114 g_terminal_filebuf_##TYPE.reset(new std::filebuf(file_##TYPE)); \
115 g_terminal_backup_fbuf_##TYPE = std::c##TYPE.rdbuf(); \
116 std::c##TYPE.rdbuf(g_terminal_filebuf_##TYPE.data()); \
121 #define RESTORE_STANDARD_STREAM(TYPE) do \
123 if(!g_terminal_filebuf_##TYPE.isNull()) \
125 *(std##TYPE) = g_terminal_backup_file_##TYPE; \
126 std::c##TYPE.rdbuf(g_terminal_backup_fbuf_##TYPE); \
127 g_terminal_filebuf_##TYPE.reset(NULL); \
132 ///////////////////////////////////////////////////////////////////////////////
133 // TERMINAL VARIABLES
134 ///////////////////////////////////////////////////////////////////////////////
137 static MUtils::Internal::CriticalSection g_terminal_lock
;
139 //Terminal replacement streams
140 static bool g_terminal_attached
= false;
141 static QScopedPointer
<std::filebuf
> g_terminal_filebuf_out
;
142 static QScopedPointer
<std::filebuf
> g_terminal_filebuf_err
;
144 //Backup of original streams
145 static FILE g_terminal_backup_file_out
;
146 static FILE g_terminal_backup_file_err
;
147 static std::streambuf
* g_terminal_backup_fbuf_out
;
148 static std::streambuf
* g_terminal_backup_fbuf_err
;
151 static QScopedPointer
<QFile
> g_terminal_log_file
;
153 ///////////////////////////////////////////////////////////////////////////////
155 ///////////////////////////////////////////////////////////////////////////////
157 static void terminal_restore(void)
159 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
161 if(g_terminal_attached
)
163 RESTORE_STANDARD_STREAM(out
);
164 RESTORE_STANDARD_STREAM(err
);
166 g_terminal_attached
= false;
170 ///////////////////////////////////////////////////////////////////////////////
172 ///////////////////////////////////////////////////////////////////////////////
174 void MUtils::Terminal::setup(int &argc
, char **argv
, const char* const appName
, const bool forceEnabled
)
176 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
177 bool enableConsole
= (MUTILS_DEBUG
) || forceEnabled
;
181 wchar_t *logfile
= NULL
; size_t logfile_len
= 0;
182 if(!_wdupenv_s(&logfile
, &logfile_len
, L
"MUTILS_LOGFILE"))
184 if(logfile
&& (logfile_len
> 0))
186 g_terminal_log_file
.reset(new QFile(MUTILS_QSTR(logfile
)));
187 if(g_terminal_log_file
->open(QIODevice::WriteOnly
))
189 static const char MARKER
[3] = { char(0xEF), char(0xBB), char(0xBF) };
190 g_terminal_log_file
->write(MARKER
, 3);
199 for(int i
= 0; i
< argc
; i
++)
201 if(!stricmp(argv
[i
], "--console"))
203 enableConsole
= true;
205 else if(!stricmp(argv
[i
], "--no-console"))
207 enableConsole
= false;
214 if(!g_terminal_attached
)
216 if(AllocConsole() != FALSE
)
218 SetConsoleOutputCP(CP_UTF8
);
219 SetConsoleCtrlHandler(NULL
, TRUE
);
220 if(appName
&& appName
[0])
223 _snprintf_s(title
, 128, _TRUNCATE
, "%s | Debug Console", appName
);
224 SetConsoleTitleA(title
);
226 g_terminal_attached
= true;
230 if(g_terminal_attached
)
232 //-------------------------------------------------------------------
233 //See: http://support.microsoft.com/default.aspx?scid=kb;en-us;105305
234 //-------------------------------------------------------------------
235 const int flags
= _O_WRONLY
| _O_U8TEXT
;
236 REPLACE_STANDARD_STREAM(out
, STD_OUTPUT_HANDLE
);
237 REPLACE_STANDARD_STREAM(err
, STD_ERROR_HANDLE
);
238 atexit(terminal_restore
);
240 const HWND hwndConsole
= GetConsoleWindow();
241 if((hwndConsole
!= NULL
) && (hwndConsole
!= INVALID_HANDLE_VALUE
))
243 HMENU hMenu
= GetSystemMenu(hwndConsole
, 0);
244 EnableMenuItem(hMenu
, SC_CLOSE
, MF_BYCOMMAND
| MF_GRAYED
);
245 RemoveMenu(hMenu
, SC_CLOSE
, MF_BYCOMMAND
);
247 SetWindowPos (hwndConsole
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|SWP_FRAMECHANGED
);
248 SetWindowLong(hwndConsole
, GWL_STYLE
, GetWindowLong(hwndConsole
, GWL_STYLE
) & (~WS_MAXIMIZEBOX
) & (~WS_MINIMIZEBOX
));
249 SetWindowPos (hwndConsole
, HWND_TOP
, 0, 0, 0, 0, SWP_NOMOVE
|SWP_NOSIZE
|SWP_NOZORDER
|SWP_FRAMECHANGED
);
255 ///////////////////////////////////////////////////////////////////////////////
257 ///////////////////////////////////////////////////////////////////////////////
260 static const WORD COLOR_RED
= FOREGROUND_RED
| FOREGROUND_INTENSITY
;
261 static const WORD COLOR_YELLOW
= FOREGROUND_GREEN
| FOREGROUND_RED
| FOREGROUND_INTENSITY
;
262 static const WORD COLOR_WHITE
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
| FOREGROUND_INTENSITY
;
263 static const WORD COLOR_DEFAULT
= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
265 static void set_terminal_color(FILE* file
, const WORD
&attributes
)
267 const HANDLE hConsole
= (HANDLE
)(_get_osfhandle(_fileno(file
)));
268 if((hConsole
!= NULL
) && (hConsole
!= INVALID_HANDLE_VALUE
))
270 SetConsoleTextAttribute(hConsole
, attributes
);
274 ///////////////////////////////////////////////////////////////////////////////
276 ///////////////////////////////////////////////////////////////////////////////
278 static const char *const FORMAT
= "[%c][%s] %s\r\n";
279 static const char *const GURU_MEDITATION
= "\n\nGURU MEDITATION !!!\n\n";
281 static void write_logfile_helper(QFile
*const file
, const int &type
, const char *const message
)
283 static char input
[1024], output
[1024], timestamp
[32];
284 make_timestamp(timestamp
, 32);
285 strncpy_s(input
, 1024, message
, _TRUNCATE
);
286 const char *const temp
= clean_str(input
);
292 _snprintf_s(output
, 1024, FORMAT
, 'C', timestamp
, temp
);
295 _snprintf_s(output
, 1024, FORMAT
, 'W', timestamp
, temp
);
298 _snprintf_s(output
, 1024, FORMAT
, 'I', timestamp
, temp
);
306 static void write_debugger_helper(const int &type
, const char *const message
)
308 static char input
[1024], output
[1024], timestamp
[32];
309 make_timestamp(timestamp
, 32);
310 strncpy_s(input
, 1024, message
, _TRUNCATE
);
311 const char *const temp
= clean_str(input
);
317 _snprintf_s(output
, 1024, FORMAT
, 'C', timestamp
, temp
);
320 _snprintf_s(output
, 1024, FORMAT
, 'W', timestamp
, temp
);
323 _snprintf_s(output
, 1024, FORMAT
, 'I', timestamp
, temp
);
327 OutputDebugStringA(output
);
330 static void write_terminal_helper(const int &type
, const char *const message
)
332 if(_isatty(_fileno(stderr
)))
334 UINT oldOutputCP
= GetConsoleOutputCP();
335 if(oldOutputCP
!= CP_UTF8
) SetConsoleOutputCP(CP_UTF8
);
341 set_terminal_color(stderr
, COLOR_RED
);
342 fprintf(stderr
, GURU_MEDITATION
);
343 fprintf(stderr
, "%s\n", message
);
347 set_terminal_color(stderr
, COLOR_YELLOW
);
348 fprintf(stderr
, "%s\n", message
);
352 set_terminal_color(stderr
, COLOR_WHITE
);
353 fprintf(stderr
, "%s\n", message
);
358 set_terminal_color(stderr
, COLOR_DEFAULT
);
359 if(oldOutputCP
!= CP_UTF8
)
361 SetConsoleOutputCP(oldOutputCP
);
366 void MUtils::Terminal::write(const int &type
, const char *const message
)
368 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
370 if(g_terminal_attached
)
372 write_terminal_helper(type
, message
);
376 write_debugger_helper(type
, message
);
379 if(!g_terminal_log_file
.isNull())
381 write_logfile_helper(g_terminal_log_file
.data(), type
, message
);
385 ///////////////////////////////////////////////////////////////////////////////
387 ///////////////////////////////////////////////////////////////////////////////
389 void MUtils::Terminal::set_icon(const QIcon
&icon
)
391 MUtils::Internal::CSLocker
lock(g_terminal_lock
);
393 if(g_terminal_attached
&& (!(icon
.isNull() || MUtils::OS::running_on_wine())))
395 QLibrary
kernel32("kernel32.dll");
398 typedef DWORD (__stdcall
*SetConsoleIconFun
)(HICON
);
399 if(SetConsoleIconFun SetConsoleIconPtr
= (SetConsoleIconFun
) kernel32
.resolve("SetConsoleIcon"))
401 if(HICON hIcon
= qicon_to_hicon(icon
, 16, 16))
403 SetConsoleIconPtr(hIcon
);