OSDN Git Service

Moved code to set up the console icon into the MUtilities library. Also added the...
[mutilities/MUtilities.git] / src / Terminal_Win32.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
10 //
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.
15 //
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.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 //Windows includes
24 #define NOMINMAX
25 #define WIN32_LEAN_AND_MEAN 1
26 #include <Windows.h>
27
28
29 //Internal
30 #include <MUtils/Terminal.h>
31 #include <MUtils/Global.h>
32 #include <MUtils/OSSupport.h>
33 #include "Utils_Win32.h"
34 #include "CriticalSection_Win32.h"
35
36 //Qt
37 #include <QFile>
38 #include <QStringList>
39 #include <QIcon>
40 #include <QLibrary>
41
42 //CRT
43 #include <iostream>
44 #include <fstream>
45 #include <io.h>
46 #include <fcntl.h>
47 #include <ctime>
48
49 //Lock
50 static MUtils::Internal::CriticalSection g_terminal_lock;
51
52 #ifdef _MSC_VER
53 #define stricmp(X,Y) _stricmp((X),(Y))
54 #endif
55
56 ///////////////////////////////////////////////////////////////////////////////
57 // HELPER FUNCTIONS
58 ///////////////////////////////////////////////////////////////////////////////
59
60 static void make_timestamp(char *timestamp, const size_t &buffsize)
61 {
62         time_t rawtime;
63         struct tm timeinfo;
64
65         time(&rawtime);
66         if(localtime_s(&timeinfo, &rawtime) == 0)
67         {
68                 strftime(timestamp, 32, "%H:%M:%S", &timeinfo);
69         }
70         else
71         {
72                 timestamp[0] = '\0';
73         }
74 }
75
76 static const char *clean_str(char *str)
77 {
78         //Clean
79         char *ptr = &str[0];
80         while((*ptr) && isspace(*ptr))
81         {
82                 *(ptr++) = 0x20;
83         }
84
85         //Trim left
86         while((*str) && isspace(*str))
87         {
88                 str++;
89         }
90
91         //Trim right
92         size_t pos = strlen(str);
93         while(pos > 0)
94         {
95                 if(isspace(str[--pos]))
96                 {
97                         str[pos] = '\0';
98                         continue;
99                 }
100                 break;
101         }
102
103         return str;
104 }
105
106 ///////////////////////////////////////////////////////////////////////////////
107 // TERMINAL SETUP
108 ///////////////////////////////////////////////////////////////////////////////
109
110 static bool g_terminal_attached = false;
111 static QScopedPointer<std::filebuf> g_filebufStdOut;
112 static QScopedPointer<std::filebuf> g_filebufStdErr;
113 static QScopedPointer<QFile> g_log_file;
114
115 void MUtils::Terminal::setup(int &argc, char **argv, const bool forceEnabled)
116 {
117         MUtils::Internal::CSLocker lock(g_terminal_lock);
118         bool enableConsole = (MUTILS_DEBUG) || forceEnabled;
119
120         if(_environ)
121         {
122                 wchar_t *logfile = NULL; size_t logfile_len = 0;
123                 if(!_wdupenv_s(&logfile, &logfile_len, L"MUTILS_LOGFILE"))
124                 {
125                         if(logfile && (logfile_len > 0))
126                         {
127                                 g_log_file.reset(new QFile(MUTILS_QSTR(logfile)));
128                                 if(g_log_file->open(QIODevice::WriteOnly))
129                                 {
130                                         static const char MARKER[3] = { char(0xEF), char(0xBB), char(0xBF) };
131                                         g_log_file->write(MARKER, 3);
132                                 }
133                                 free(logfile);
134                         }
135                 }
136         }
137
138         if(!MUTILS_DEBUG)
139         {
140                 for(int i = 0; i < argc; i++)
141                 {
142                         if(!stricmp(argv[i], "--console"))
143                         {
144                                 enableConsole = true;
145                         }
146                         else if(!stricmp(argv[i], "--no-console"))
147                         {
148                                 enableConsole = false;
149                         }
150                 }
151         }
152
153         if(enableConsole)
154         {
155                 if(!g_terminal_attached)
156                 {
157                         if(AllocConsole() != FALSE)
158                         {
159                                 SetConsoleCtrlHandler(NULL, TRUE);
160                                 SetConsoleTitle(L"LameXP - Audio Encoder Front-End | Debug Console");
161                                 SetConsoleOutputCP(CP_UTF8);
162                                 g_terminal_attached = true;
163                         }
164                 }
165                 
166                 if(g_terminal_attached)
167                 {
168                         //-------------------------------------------------------------------
169                         //See: http://support.microsoft.com/default.aspx?scid=kb;en-us;105305
170                         //-------------------------------------------------------------------
171                         const int flags = _O_WRONLY | _O_U8TEXT;
172                         const int hCrtStdOut = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), flags);
173                         const int hCrtStdErr = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE ), flags);
174                         FILE *const hfStdOut = (hCrtStdOut >= 0) ? _fdopen(hCrtStdOut, "wb") : NULL;
175                         FILE *const hfStdErr = (hCrtStdErr >= 0) ? _fdopen(hCrtStdErr, "wb") : NULL;
176                         if(hfStdOut)
177                         {
178                                 *stdout = *hfStdOut;
179                                 g_filebufStdOut.reset(new std::filebuf(hfStdOut));
180                                 std::cout.rdbuf(g_filebufStdOut.data());
181                         }
182                         if(hfStdErr)
183                         {
184                                 *stderr = *hfStdErr;
185                                 g_filebufStdErr.reset(new std::filebuf(hfStdErr));
186                                 std::cerr.rdbuf(g_filebufStdErr.data());
187                         }
188
189                         const HWND hwndConsole = GetConsoleWindow();
190                         if((hwndConsole != NULL) && (hwndConsole != INVALID_HANDLE_VALUE))
191                         {
192                                 HMENU hMenu = GetSystemMenu(hwndConsole, 0);
193                                 EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
194                                 RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
195
196                                 SetWindowPos (hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
197                                 SetWindowLong(hwndConsole, GWL_STYLE, GetWindowLong(hwndConsole, GWL_STYLE) & (~WS_MAXIMIZEBOX) & (~WS_MINIMIZEBOX));
198                                 SetWindowPos (hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
199                         }
200                 }
201         }
202 }
203
204 ///////////////////////////////////////////////////////////////////////////////
205 // TERMINAL COLORS
206 ///////////////////////////////////////////////////////////////////////////////
207
208 //Colors
209 static const WORD COLOR_RED    = FOREGROUND_RED | FOREGROUND_INTENSITY;
210 static const WORD COLOR_YELLOW = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
211 static const WORD COLOR_WHITE  = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
212 static const WORD COLOR_DEFAULT= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
213
214 static void set_terminal_color(FILE* file, const WORD &attributes)
215 {
216         const HANDLE hConsole = (HANDLE)(_get_osfhandle(_fileno(file)));
217         if((hConsole != NULL) && (hConsole != INVALID_HANDLE_VALUE))
218         {
219                 SetConsoleTextAttribute(hConsole, attributes);
220         }
221 }
222
223 ///////////////////////////////////////////////////////////////////////////////
224 // WRITE TO TERMINAL
225 ///////////////////////////////////////////////////////////////////////////////
226
227 static const char *const FORMAT = "[%c][%s] %s\r\n";
228 static const char *const GURU_MEDITATION = "\n\nGURU MEDITATION !!!\n\n";
229
230 static void write_logfile_helper(QFile *const file, const int &type, const char *const message)
231 {
232         static char input[1024], output[1024], timestamp[32];
233         make_timestamp(timestamp, 32);
234         strncpy_s(input, 1024, message, _TRUNCATE);
235         const char *const temp = clean_str(input);
236
237         switch(type)
238         {
239         case QtCriticalMsg:
240         case QtFatalMsg:
241                 _snprintf_s(output, 1024, FORMAT, 'C', timestamp, temp);
242                 break;
243         case QtWarningMsg:
244                 _snprintf_s(output, 1024, FORMAT, 'W', timestamp, temp);
245                 break;
246         default:
247                 _snprintf_s(output, 1024, FORMAT, 'I', timestamp, temp);
248                 break;
249         }
250
251         file->write(output);
252         file->flush();
253 }
254
255 static void write_debugger_helper(const int &type, const char *const message)
256 {
257         static char input[1024], output[1024], timestamp[32];
258         make_timestamp(timestamp, 32);
259         strncpy_s(input, 1024, message, _TRUNCATE);
260         const char *const temp = clean_str(input);
261
262         switch(type)
263         {
264         case QtCriticalMsg:
265         case QtFatalMsg:
266                 _snprintf_s(output, 1024, FORMAT, 'C', timestamp, temp);
267                 break;
268         case QtWarningMsg:
269                 _snprintf_s(output, 1024, FORMAT, 'W', timestamp, temp);
270                 break;
271         default:
272                 _snprintf_s(output, 1024, FORMAT, 'I', timestamp, temp);
273                 break;
274         }
275
276         OutputDebugStringA(output);
277 }
278
279 static void write_terminal_helper(const int &type, const char *const message)
280 {
281         if(_isatty(_fileno(stderr)))
282         {
283                 UINT oldOutputCP = GetConsoleOutputCP();
284                 if(oldOutputCP != CP_UTF8) SetConsoleOutputCP(CP_UTF8);
285
286                 switch(type)
287                 {
288                 case QtCriticalMsg:
289                 case QtFatalMsg:
290                         set_terminal_color(stderr, COLOR_RED);
291                         fprintf(stderr, GURU_MEDITATION);
292                         fprintf(stderr, "%s\n", message);
293                         fflush(stderr);
294                         break;
295                 case QtWarningMsg:
296                         set_terminal_color(stderr, COLOR_YELLOW);
297                         fprintf(stderr, "%s\n", message);
298                         fflush(stderr);
299                         break;
300                 default:
301                         set_terminal_color(stderr, COLOR_WHITE);
302                         fprintf(stderr, "%s\n", message);
303                         fflush(stderr);
304                         break;
305                 }
306         
307                 set_terminal_color(stderr, COLOR_DEFAULT);
308                 if(oldOutputCP != CP_UTF8)
309                 {
310                         SetConsoleOutputCP(oldOutputCP);
311                 }
312         }
313 }
314
315 void MUtils::Terminal::write(const int &type, const char *const message)
316 {
317         MUtils::Internal::CSLocker lock(g_terminal_lock);
318
319         if(g_terminal_attached)
320         {
321                 write_terminal_helper(type, message);
322         }
323         else
324         {
325                 write_debugger_helper(type, message);
326         }
327
328         if(!g_log_file.isNull())
329         {
330                 write_logfile_helper(g_log_file.data(), type, message);
331         }
332 }
333
334 ///////////////////////////////////////////////////////////////////////////////
335 // TERMINAL ICON
336 ///////////////////////////////////////////////////////////////////////////////
337
338 void MUtils::Terminal::set_icon(const QIcon &icon)
339 {
340         MUtils::Internal::CSLocker lock(g_terminal_lock);
341
342         if(g_terminal_attached && (!(icon.isNull() || MUtils::OS::running_on_wine())))
343         {
344                 QLibrary kernel32("kernel32.dll");
345                 if(kernel32.load())
346                 {
347                         typedef DWORD (__stdcall *SetConsoleIconFun)(HICON);
348                         if(SetConsoleIconFun SetConsoleIconPtr = (SetConsoleIconFun) kernel32.resolve("SetConsoleIcon"))
349                         {
350                                 if(HICON hIcon = qicon_to_hicon(icon, 16, 16))
351                                 {
352                                         SetConsoleIconPtr(hIcon);
353                                         DestroyIcon(hIcon);
354                                 }
355                         }
356                 }
357         }
358 }