From d42da03bf1601c24d5005811cb5327e32fb8d62c Mon Sep 17 00:00:00 2001 From: LoRd_MuldeR Date: Tue, 25 Nov 2014 22:32:20 +0100 Subject: [PATCH] Moved all terminal support functions into MUtilities library. --- MUtilities_VS2013.vcxproj | 2 + MUtilities_VS2013.vcxproj.filters | 6 + include/MUtils/Global.h | 8 +- include/MUtils/OSSupport.h | 3 + include/MUtils/Terminal.h | 45 ++++++ include/MUtils/Version.h | 14 +- src/Config.h | 34 ++++ src/CriticalSection_Win32.h | 2 + src/Global.cpp | 18 +-- src/OSSupport_Win32.cpp | 47 ++++++ src/Terminal_Win32.cpp | 325 ++++++++++++++++++++++++++++++++++++++ src/Version.cpp | 53 ++++++- 12 files changed, 527 insertions(+), 30 deletions(-) create mode 100644 include/MUtils/Terminal.h create mode 100644 src/Config.h create mode 100644 src/Terminal_Win32.cpp diff --git a/MUtilities_VS2013.vcxproj b/MUtilities_VS2013.vcxproj index 8aa4ecb..2158329 100644 --- a/MUtilities_VS2013.vcxproj +++ b/MUtilities_VS2013.vcxproj @@ -20,6 +20,7 @@ + @@ -30,6 +31,7 @@ + "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)tmp\$(ProjectName)\MOC_%(Filename).cpp" "%(FullPath)" diff --git a/MUtilities_VS2013.vcxproj.filters b/MUtilities_VS2013.vcxproj.filters index ff32638..d4fd3a3 100644 --- a/MUtilities_VS2013.vcxproj.filters +++ b/MUtilities_VS2013.vcxproj.filters @@ -45,6 +45,9 @@ Source Files + + Source Files + @@ -71,6 +74,9 @@ Public Headers + + Public Headers + diff --git a/include/MUtils/Global.h b/include/MUtils/Global.h index c235b88..4debf73 100644 --- a/include/MUtils/Global.h +++ b/include/MUtils/Global.h @@ -88,15 +88,11 @@ namespace MUtils MUTILS_API bool remove_file(const QString &fileName); MUTILS_API bool remove_directory(const QString &folderPath); - //Version - MUTILS_API const char* mutils_build_date(void); - MUTILS_API const char* mutils_build_time(void); - //Internal namespace Internal { - MUTILS_API int selfTest(const char *const date, const bool debug); - static const int g_selfTestRet = selfTest(__DATE__, MUTILS_DEBUG); + MUTILS_API int selfTest(const char *const buildKey, const bool debug); + static const int g_test = selfTest(__DATE__"@"__TIME__, MUTILS_DEBUG); } } diff --git a/include/MUtils/OSSupport.h b/include/MUtils/OSSupport.h index 3fce410..3c4cea5 100644 --- a/include/MUtils/OSSupport.h +++ b/include/MUtils/OSSupport.h @@ -100,6 +100,9 @@ namespace MUtils MUTILS_API void system_message_wrn(const wchar_t *const title, const wchar_t *const text); MUTILS_API void system_message_err(const wchar_t *const title, const wchar_t *const text); + //CLI Arguments + MUTILS_API const QStringList &arguments(void); + //Get the OS version MUTILS_API const Version::os_version_t &os_version(void); MUTILS_API const char *os_friendly_name(const MUtils::OS::Version::os_version_t &os_version); diff --git a/include/MUtils/Terminal.h b/include/MUtils/Terminal.h new file mode 100644 index 0000000..d2e40e4 --- /dev/null +++ b/include/MUtils/Terminal.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////// +// MuldeR's Utilities for Qt +// Copyright (C) 2004-2014 LoRd_MuldeR +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// http://www.gnu.org/licenses/lgpl-2.1.txt +////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +//MUtils +#include + +//Qt +#include +#include + +/////////////////////////////////////////////////////////////////////////////// + +namespace MUtils +{ + namespace Terminal + { + //Setup terminal + MUTILS_API void setup(const QStringList &argv, const bool forceEnabled); + + //Terminal output + MUTILS_API void write(const int &type, const char *const message); + } +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/include/MUtils/Version.h b/include/MUtils/Version.h index 8e47a62..4d7ef16 100644 --- a/include/MUtils/Version.h +++ b/include/MUtils/Version.h @@ -34,11 +34,17 @@ namespace MUtils class Version { public: - //Get Build Date - MUTILS_API static const QDate build_date(const char *const date_str = build_date_raw()); + //Get Library Version Numbers + MUTILS_API static const quint32 &lib_version_major(void); + MUTILS_API static const quint32 &lib_version_minor(void); - //Get Build Time - MUTILS_API static const QTime build_time(const char *const time_str = build_time_raw()); + //Get Library Build Date/Time + MUTILS_API static const QDate lib_build_date(void); + MUTILS_API static const QTime lib_build_time(void); + + //Get Application Build Date/Time + MUTILS_API static const QDate app_build_date(const char *const date_str = build_date_raw()); + MUTILS_API static const QTime app_build_time(const char *const time_str = build_time_raw()); //Compiler detection static const char *const compiler_version(void) diff --git a/src/Config.h b/src/Config.h new file mode 100644 index 0000000..f30581f --- /dev/null +++ b/src/Config.h @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////// +// MuldeR's Utilities for Qt +// Copyright (C) 2004-2014 LoRd_MuldeR +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// http://www.gnu.org/licenses/lgpl-2.1.txt +////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef MUTILS_INC_CONFIG +#error Please do *not* include CONFIG.H directly! +#endif + +/////////////////////////////////////////////////////////////////////////////// +// MUtilities Version Info +/////////////////////////////////////////////////////////////////////////////// + +#define VER_MUTILS_MAJOR 1 +#define VER_MUTILS_MINOR_HI 0 +#define VER_MUTILS_MINOR_LO 0 diff --git a/src/CriticalSection_Win32.h b/src/CriticalSection_Win32.h index 78bc2d4..e55b30e 100644 --- a/src/CriticalSection_Win32.h +++ b/src/CriticalSection_Win32.h @@ -22,8 +22,10 @@ #pragma once //Win32 API +#ifndef _INC_WINDOWS #define WIN32_LEAN_AND_MEAN 1 #include +#endif //_INC_WINDOWS /////////////////////////////////////////////////////////////////////////////// // CRITICAL SECTION diff --git a/src/Global.cpp b/src/Global.cpp index e889041..e94c440 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -302,28 +302,12 @@ void MUtils::init_process(QProcess &process, const QString &wokringDir, const bo } /////////////////////////////////////////////////////////////////////////////// -// LIB VERSION -/////////////////////////////////////////////////////////////////////////////// - -const char* MUtils::mutils_build_date(void) -{ - static const char *const BUILD_DATE = __DATE__; - return BUILD_DATE; -} - -const char* MUtils::mutils_build_time(void) -{ - static const char *const BUILD_TIME = __TIME__; - return BUILD_TIME; -} - -/////////////////////////////////////////////////////////////////////////////// // SELF-TEST /////////////////////////////////////////////////////////////////////////////// int MUtils::Internal::selfTest(const char *const date, const bool debug) { - if(strcmp(date, __DATE__) || (MUTILS_DEBUG != debug)) + if(strncmp(date, __DATE__"@"__TIME__, 14) || (MUTILS_DEBUG != debug)) { MUtils::OS::system_message_err(L"MUtils", L"FATAL ERROR: MUtils library version mismatch detected!"); abort(); diff --git a/src/OSSupport_Win32.cpp b/src/OSSupport_Win32.cpp index 5b201ae..8aab56a 100644 --- a/src/OSSupport_Win32.cpp +++ b/src/OSSupport_Win32.cpp @@ -32,6 +32,7 @@ #include #include #include +#include //Qt #include @@ -64,6 +65,52 @@ void MUtils::OS::system_message_err(const wchar_t *const title, const wchar_t *c } /////////////////////////////////////////////////////////////////////////////// +// FETCH CLI ARGUMENTS +/////////////////////////////////////////////////////////////////////////////// + +static QReadWriteLock g_arguments_lock; +static QScopedPointer g_arguments_list; + +const QStringList &MUtils::OS::arguments(void) +{ + QReadLocker readLock(&g_arguments_lock); + + //Already initialized? + if(!g_arguments_list.isNull()) + { + return (*(g_arguments_list.data())); + } + + readLock.unlock(); + QWriteLocker writeLock(&g_arguments_lock); + + //Still not initialized? + if(!g_arguments_list.isNull()) + { + return (*(g_arguments_list.data())); + } + + g_arguments_list.reset(new QStringList); + int nArgs = 0; + LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); + + if(NULL != szArglist) + { + for(int i = 0; i < nArgs; i++) + { + *(g_arguments_list.data()) << MUTILS_QSTR(szArglist[i]); + } + LocalFree(szArglist); + } + else + { + qWarning("CommandLineToArgvW() has failed !!!"); + } + + return (*(g_arguments_list.data())); +} + +/////////////////////////////////////////////////////////////////////////////// // OS VERSION DETECTION /////////////////////////////////////////////////////////////////////////////// diff --git a/src/Terminal_Win32.cpp b/src/Terminal_Win32.cpp new file mode 100644 index 0000000..298f7d6 --- /dev/null +++ b/src/Terminal_Win32.cpp @@ -0,0 +1,325 @@ +/////////////////////////////////////////////////////////////////////////////// +// LameXP - Audio Encoder Front-End +// Copyright (C) 2004-2014 LoRd_MuldeR +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version, but always including the *additional* +// restrictions defined in the "License.txt" file. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// http://www.gnu.org/licenses/gpl-2.0.txt +/////////////////////////////////////////////////////////////////////////////// + +#include + +//Internal +#include +#include +#include "CriticalSection_Win32.h" + +//Windows includes +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN 1 +#include + +//Qt +#include +#include + +//CRT +#include +#include +#include +#include +#include + +//Lock +static MUtils::Internal::CriticalSection g_terminal_lock; + +/////////////////////////////////////////////////////////////////////////////// +// HELPER FUNCTIONS +/////////////////////////////////////////////////////////////////////////////// + +static void make_timestamp(char *timestamp, const size_t &buffsize) +{ + time_t rawtime; + struct tm timeinfo; + + time(&rawtime); + if(localtime_s(&timeinfo, &rawtime) == 0) + { + strftime(timestamp, 32, "%H:%M:%S", &timeinfo); + } + else + { + timestamp[0] = '\0'; + } +} + +static const char *clean_str(char *str) +{ + //Clean + char *ptr = &str[0]; + while((*ptr) && isspace(*ptr)) + { + *(ptr++) = 0x20; + } + + //Trim left + while((*str) && isspace(*str)) + { + str++; + } + + //Trim right + size_t pos = strlen(str); + while(pos > 0) + { + if(isspace(str[--pos])) + { + str[pos] = '\0'; + continue; + } + break; + } + + return str; +} + +/////////////////////////////////////////////////////////////////////////////// +// TERMINAL SETUP +/////////////////////////////////////////////////////////////////////////////// + +static bool g_terminal_attached = false; +static QScopedPointer g_filebufStdOut; +static QScopedPointer g_filebufStdErr; +static QScopedPointer g_log_file; + +void MUtils::Terminal::setup(const QStringList &argv, const bool forceEnabled) +{ + MUtils::Internal::CSLocker lock(g_terminal_lock); + bool enableConsole = (MUTILS_DEBUG) || forceEnabled; + + if(_environ) + { + wchar_t *logfile = NULL; size_t logfile_len = 0; + if(!_wdupenv_s(&logfile, &logfile_len, L"MUTILS_LOGFILE")) + { + if(logfile && (logfile_len > 0)) + { + g_log_file.reset(new QFile(MUTILS_QSTR(logfile))); + if(g_log_file->open(QIODevice::WriteOnly)) + { + static const char MARKER[3] = { char(0xEF), char(0xBB), char(0xBF) }; + g_log_file->write(MARKER, 3); + } + free(logfile); + } + } + } + + if(!MUTILS_DEBUG) + { + for(int i = 0; i < argv.count(); i++) + { + if(!argv.at(i).compare("--console", Qt::CaseInsensitive)) + { + enableConsole = true; + } + else if(!argv.at(i).compare("--no-console", Qt::CaseInsensitive)) + { + enableConsole = false; + } + } + } + + if(enableConsole) + { + if(!g_terminal_attached) + { + if(AllocConsole() != FALSE) + { + SetConsoleCtrlHandler(NULL, TRUE); + SetConsoleTitle(L"LameXP - Audio Encoder Front-End | Debug Console"); + SetConsoleOutputCP(CP_UTF8); + g_terminal_attached = true; + } + } + + if(g_terminal_attached) + { + //------------------------------------------------------------------- + //See: http://support.microsoft.com/default.aspx?scid=kb;en-us;105305 + //------------------------------------------------------------------- + const int flags = _O_WRONLY | _O_U8TEXT; + const int hCrtStdOut = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), flags); + const int hCrtStdErr = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE ), flags); + FILE *const hfStdOut = (hCrtStdOut >= 0) ? _fdopen(hCrtStdOut, "wb") : NULL; + FILE *const hfStdErr = (hCrtStdErr >= 0) ? _fdopen(hCrtStdErr, "wb") : NULL; + if(hfStdOut) + { + *stdout = *hfStdOut; + g_filebufStdOut.reset(new std::filebuf(hfStdOut)); + std::cout.rdbuf(g_filebufStdOut.data()); + } + if(hfStdErr) + { + *stderr = *hfStdErr; + g_filebufStdErr.reset(new std::filebuf(hfStdErr)); + std::cerr.rdbuf(g_filebufStdErr.data()); + } + } + + HWND hwndConsole = GetConsoleWindow(); + if((hwndConsole != NULL) && (hwndConsole != INVALID_HANDLE_VALUE)) + { + HMENU hMenu = GetSystemMenu(hwndConsole, 0); + EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED); + RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + + SetWindowPos (hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); + SetWindowLong(hwndConsole, GWL_STYLE, GetWindowLong(hwndConsole, GWL_STYLE) & (~WS_MAXIMIZEBOX) & (~WS_MINIMIZEBOX)); + SetWindowPos (hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// TERMINAL COLORS +/////////////////////////////////////////////////////////////////////////////// + +//Colors +static const WORD COLOR_RED = FOREGROUND_RED | FOREGROUND_INTENSITY; +static const WORD COLOR_YELLOW = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; +static const WORD COLOR_WHITE = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; +static const WORD COLOR_DEFAULT= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + +static void set_terminal_color(FILE* file, const WORD &attributes) +{ + const HANDLE hConsole = (HANDLE)(_get_osfhandle(_fileno(file))); + if((hConsole != NULL) && (hConsole != INVALID_HANDLE_VALUE)) + { + SetConsoleTextAttribute(hConsole, attributes); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// WRITE TO TERMINAL +/////////////////////////////////////////////////////////////////////////////// + +static const char *const FORMAT = "[%c][%s] %s\r\n"; +static const char *const GURU_MEDITATION = "\n\nGURU MEDITATION !!!\n\n"; + +static void write_logfile_helper(QFile *const file, const int &type, const char *const message) +{ + static char input[1024], output[1024], timestamp[32]; + make_timestamp(timestamp, 32); + strncpy_s(input, 1024, message, _TRUNCATE); + const char *const temp = clean_str(input); + + switch(type) + { + case QtCriticalMsg: + case QtFatalMsg: + _snprintf_s(output, 1024, FORMAT, 'C', timestamp, temp); + break; + case QtWarningMsg: + _snprintf_s(output, 1024, FORMAT, 'W', timestamp, temp); + break; + default: + _snprintf_s(output, 1024, FORMAT, 'I', timestamp, temp); + break; + } + + file->write(output); + file->flush(); +} + +static void write_debugger_helper(const int &type, const char *const message) +{ + static char input[1024], output[1024], timestamp[32]; + make_timestamp(timestamp, 32); + strncpy_s(input, 1024, message, _TRUNCATE); + const char *const temp = clean_str(input); + + switch(type) + { + case QtCriticalMsg: + case QtFatalMsg: + _snprintf_s(output, 1024, FORMAT, 'C', timestamp, temp); + break; + case QtWarningMsg: + _snprintf_s(output, 1024, FORMAT, 'W', timestamp, temp); + break; + default: + _snprintf_s(output, 1024, FORMAT, 'I', timestamp, temp); + break; + } + + OutputDebugStringA(output); +} + +static void write_terminal_helper(const int &type, const char *const message) +{ + if(_isatty(_fileno(stderr))) + { + UINT oldOutputCP = GetConsoleOutputCP(); + if(oldOutputCP != CP_UTF8) SetConsoleOutputCP(CP_UTF8); + + switch(type) + { + case QtCriticalMsg: + case QtFatalMsg: + set_terminal_color(stderr, COLOR_RED); + fprintf(stderr, GURU_MEDITATION); + fprintf(stderr, "%s\n", message); + fflush(stderr); + break; + case QtWarningMsg: + set_terminal_color(stderr, COLOR_YELLOW); + fprintf(stderr, "%s\n", message); + fflush(stderr); + break; + default: + set_terminal_color(stderr, COLOR_WHITE); + fprintf(stderr, "%s\n", message); + fflush(stderr); + break; + } + + set_terminal_color(stderr, COLOR_DEFAULT); + if(oldOutputCP != CP_UTF8) + { + SetConsoleOutputCP(oldOutputCP); + } + } +} + +void MUtils::Terminal::write(const int &type, const char *const message) +{ + MUtils::Internal::CSLocker lock(g_terminal_lock); + + if(g_terminal_attached) + { + write_terminal_helper(type, message); + } + else + { + write_debugger_helper(type, message); + } + + if(!g_log_file.isNull()) + { + write_logfile_helper(g_log_file.data(), type, message); + } +} diff --git a/src/Version.cpp b/src/Version.cpp index e958ce9..34a3061 100644 --- a/src/Version.cpp +++ b/src/Version.cpp @@ -19,18 +19,22 @@ // http://www.gnu.org/licenses/lgpl-2.1.txt ////////////////////////////////////////////////////////////////////////////////// -#pragma once +#define MUTILS_INC_CONFIG 1 #include +//Internal #include #include +#include "Config.h" #ifdef _MSC_VER #define _snscanf(X, Y, Z, ...) _snscanf_s((X), (Y), (Z), __VA_ARGS__) #endif /////////////////////////////////////////////////////////////////////////////// +// HELPER FUNCTIONS +/////////////////////////////////////////////////////////////////////////////// static const char *g_months_lut[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; @@ -50,7 +54,7 @@ static int month2int(const char *str) return ret; } -const QDate MUtils::Version::build_date(const char *const date_str) +static const QDate decode_date_str(const char *const date_str) { bool ok = true; int date[3] = {0, 0, 0}; @@ -70,7 +74,7 @@ const QDate MUtils::Version::build_date(const char *const date_str) return QDate(date[0], date[1], date[2]); } -const QTime MUtils::Version::build_time(const char *const time_str) +static const QTime decode_time_str(const char *const time_str) { bool ok = true; int time[3] = {0, 0, 0}; @@ -89,3 +93,46 @@ const QTime MUtils::Version::build_time(const char *const time_str) } /////////////////////////////////////////////////////////////////////////////// +// LIB VERSION +/////////////////////////////////////////////////////////////////////////////// + +const QDate MUtils::Version::lib_build_date(void) +{ + static const char *const BUILD_DATE = __DATE__; + return decode_date_str(BUILD_DATE); +} + +const QTime MUtils::Version::lib_build_time(void) +{ + static const char *const BUILD_TIME = __TIME__; + return decode_time_str(BUILD_TIME); +} + +const quint32 &MUtils::Version::lib_version_major(void) +{ + static const quint32 VERSION_MAJOR = VER_MUTILS_MAJOR; + return VERSION_MAJOR; +} + +const quint32 &MUtils::Version::lib_version_minor(void) +{ + static const quint32 VERSION_MINOR = (10 * VER_MUTILS_MINOR_HI) + VER_MUTILS_MINOR_LO; + return VERSION_MINOR; +} + + +/////////////////////////////////////////////////////////////////////////////// +// APP VERSION +/////////////////////////////////////////////////////////////////////////////// + +const QDate MUtils::Version::app_build_date(const char *const date_str) +{ + return decode_date_str(date_str); +} + +const QTime MUtils::Version::app_build_time(const char *const time_str) +{ + return decode_time_str(time_str); +} + +/////////////////////////////////////////////////////////////////////////////// -- 2.11.0