mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-04-29 22:24:26 +02:00
Console colors and log invocation location (#3654)
This commit is contained in:
commit
0e00c28450
@ -125,6 +125,7 @@
|
||||
#include <FileSystem.h>
|
||||
#include <LocalPeer.h>
|
||||
|
||||
#include <QStringLiteral>
|
||||
#include <stdlib.h>
|
||||
#include <sys.h>
|
||||
#include "SysInfo.h"
|
||||
@ -153,11 +154,16 @@
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <QStyleHints>
|
||||
#include "WindowsConsole.h"
|
||||
#include "console/WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
#include "console/Console.h"
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
@ -165,6 +171,63 @@ static const QLatin1String liveCheckFile("live.check");
|
||||
|
||||
PixmapCache* PixmapCache::s_instance = nullptr;
|
||||
|
||||
static bool isANSIColorConsole;
|
||||
|
||||
static QString defaultLogFormat = QStringLiteral(
|
||||
"%{time process}"
|
||||
" "
|
||||
"%{if-debug}Debug:%{endif}"
|
||||
"%{if-info}Info:%{endif}"
|
||||
"%{if-warning}Warning:%{endif}"
|
||||
"%{if-critical}Critical:%{endif}"
|
||||
"%{if-fatal}Fatal:%{endif}"
|
||||
" "
|
||||
"%{if-category}[%{category}] %{endif}"
|
||||
"%{message}"
|
||||
" "
|
||||
"(%{function}:%{line})");
|
||||
|
||||
#define ansi_reset "\x1b[0m"
|
||||
#define ansi_bold "\x1b[1m"
|
||||
#define ansi_reset_bold "\x1b[22m"
|
||||
#define ansi_faint "\x1b[2m"
|
||||
#define ansi_italic "\x1b[3m"
|
||||
#define ansi_red_fg "\x1b[31m"
|
||||
#define ansi_green_fg "\x1b[32m"
|
||||
#define ansi_yellow_fg "\x1b[33m"
|
||||
#define ansi_blue_fg "\x1b[34m"
|
||||
#define ansi_purple_fg "\x1b[35m"
|
||||
#define ansi_inverse "\x1b[7m"
|
||||
|
||||
// clang-format off
|
||||
static QString ansiLogFormat = QStringLiteral(
|
||||
ansi_faint "%{time process}" ansi_reset
|
||||
" "
|
||||
"%{if-debug}" ansi_bold ansi_green_fg "D:" ansi_reset "%{endif}"
|
||||
"%{if-info}" ansi_bold ansi_blue_fg "I:" ansi_reset "%{endif}"
|
||||
"%{if-warning}" ansi_bold ansi_yellow_fg "W:" ansi_reset_bold "%{endif}"
|
||||
"%{if-critical}" ansi_bold ansi_red_fg "C:" ansi_reset_bold "%{endif}"
|
||||
"%{if-fatal}" ansi_bold ansi_inverse ansi_red_fg "F:" ansi_reset_bold "%{endif}"
|
||||
" "
|
||||
"%{if-category}" ansi_bold "[%{category}]" ansi_reset_bold " %{endif}"
|
||||
"%{message}"
|
||||
" "
|
||||
ansi_reset ansi_faint "(%{function}:%{line})" ansi_reset
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
#undef ansi_inverse
|
||||
#undef ansi_purple_fg
|
||||
#undef ansi_blue_fg
|
||||
#undef ansi_yellow_fg
|
||||
#undef ansi_green_fg
|
||||
#undef ansi_red_fg
|
||||
#undef ansi_italic
|
||||
#undef ansi_faint
|
||||
#undef ansi_bold
|
||||
#undef ansi_reset_bold
|
||||
#undef ansi_reset
|
||||
|
||||
namespace {
|
||||
|
||||
/** This is used so that we can output to the log file in addition to the CLI. */
|
||||
@ -173,11 +236,24 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
|
||||
static std::mutex loggerMutex;
|
||||
const std::lock_guard<std::mutex> lock(loggerMutex); // synchronized, QFile logFile is not thread-safe
|
||||
|
||||
if (isANSIColorConsole) {
|
||||
// ensure default is set for log file
|
||||
qSetMessagePattern(defaultLogFormat);
|
||||
}
|
||||
|
||||
QString out = qFormatLogMessage(type, context, msg);
|
||||
out += QChar::LineFeed;
|
||||
|
||||
APPLICATION->logFile->write(out.toUtf8());
|
||||
APPLICATION->logFile->flush();
|
||||
|
||||
if (isANSIColorConsole) {
|
||||
// format ansi for console;
|
||||
qSetMessagePattern(ansiLogFormat);
|
||||
out = qFormatLogMessage(type, context, msg);
|
||||
out += QChar::LineFeed;
|
||||
}
|
||||
|
||||
QTextStream(stderr) << out.toLocal8Bit();
|
||||
fflush(stderr);
|
||||
}
|
||||
@ -218,8 +294,18 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
// attach the parent console if stdout not already captured
|
||||
if (AttachWindowsConsole()) {
|
||||
consoleAttached = true;
|
||||
if (auto err = EnableAnsiSupport(); !err) {
|
||||
isANSIColorConsole = true;
|
||||
} else {
|
||||
std::cout << "Error setting up ansi console" << err.message() << std::endl;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (console::isConsole()) {
|
||||
isANSIColorConsole = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
setOrganizationName(BuildConfig.LAUNCHER_NAME);
|
||||
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
|
||||
setApplicationName(BuildConfig.LAUNCHER_NAME);
|
||||
@ -448,27 +534,14 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
return;
|
||||
}
|
||||
qInstallMessageHandler(appDebugOutput);
|
||||
|
||||
qSetMessagePattern(
|
||||
"%{time process}"
|
||||
" "
|
||||
"%{if-debug}D%{endif}"
|
||||
"%{if-info}I%{endif}"
|
||||
"%{if-warning}W%{endif}"
|
||||
"%{if-critical}C%{endif}"
|
||||
"%{if-fatal}F%{endif}"
|
||||
" "
|
||||
"|"
|
||||
" "
|
||||
"%{if-category}[%{category}]: %{endif}"
|
||||
"%{message}");
|
||||
qSetMessagePattern(defaultLogFormat);
|
||||
|
||||
bool foundLoggingRules = false;
|
||||
|
||||
auto logRulesFile = QStringLiteral("qtlogging.ini");
|
||||
auto logRulesPath = FS::PathCombine(dataPath, logRulesFile);
|
||||
|
||||
qDebug() << "Testing" << logRulesPath << "...";
|
||||
qInfo() << "Testing" << logRulesPath << "...";
|
||||
foundLoggingRules = QFile::exists(logRulesPath);
|
||||
|
||||
// search the dataPath()
|
||||
@ -476,7 +549,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) {
|
||||
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
|
||||
if (!logRulesPath.isEmpty()) {
|
||||
qDebug() << "Found" << logRulesPath << "...";
|
||||
qInfo() << "Found" << logRulesPath << "...";
|
||||
foundLoggingRules = true;
|
||||
}
|
||||
}
|
||||
@ -487,28 +560,28 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
#else
|
||||
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
|
||||
#endif
|
||||
qDebug() << "Testing" << logRulesPath << "...";
|
||||
qInfo() << "Testing" << logRulesPath << "...";
|
||||
foundLoggingRules = QFile::exists(logRulesPath);
|
||||
}
|
||||
|
||||
if (foundLoggingRules) {
|
||||
// load and set logging rules
|
||||
qDebug() << "Loading logging rules from:" << logRulesPath;
|
||||
qInfo() << "Loading logging rules from:" << logRulesPath;
|
||||
QSettings loggingRules(logRulesPath, QSettings::IniFormat);
|
||||
loggingRules.beginGroup("Rules");
|
||||
QStringList rule_names = loggingRules.childKeys();
|
||||
QStringList rules;
|
||||
qDebug() << "Setting log rules:";
|
||||
qInfo() << "Setting log rules:";
|
||||
for (auto rule_name : rule_names) {
|
||||
auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString());
|
||||
rules.append(rule);
|
||||
qDebug() << " " << rule;
|
||||
qInfo() << " " << rule;
|
||||
}
|
||||
auto rules_str = rules.join("\n");
|
||||
QLoggingCategory::setFilterRules(rules_str);
|
||||
}
|
||||
|
||||
qDebug() << "<> Log initialized.";
|
||||
qInfo() << "<> Log initialized.";
|
||||
}
|
||||
|
||||
{
|
||||
@ -525,33 +598,33 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
|
||||
{
|
||||
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
|
||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
||||
qDebug() << "Compiled for : " << BuildConfig.systemID();
|
||||
qDebug() << "Compiled by : " << BuildConfig.compilerID();
|
||||
qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
|
||||
qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
|
||||
qInfo() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
|
||||
qInfo() << "Version : " << BuildConfig.printableVersionString();
|
||||
qInfo() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||
qInfo() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||
qInfo() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
||||
qInfo() << "Compiled for : " << BuildConfig.systemID();
|
||||
qInfo() << "Compiled by : " << BuildConfig.compilerID();
|
||||
qInfo() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
|
||||
qInfo() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
|
||||
if (adjustedBy.size()) {
|
||||
qDebug() << "Work dir before adjustment : " << origcwdPath;
|
||||
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
|
||||
qDebug() << "Adjusted by : " << adjustedBy;
|
||||
qInfo() << "Work dir before adjustment : " << origcwdPath;
|
||||
qInfo() << "Work dir after adjustment : " << QDir::currentPath();
|
||||
qInfo() << "Adjusted by : " << adjustedBy;
|
||||
} else {
|
||||
qDebug() << "Work dir : " << QDir::currentPath();
|
||||
qInfo() << "Work dir : " << QDir::currentPath();
|
||||
}
|
||||
qDebug() << "Binary path : " << binPath;
|
||||
qDebug() << "Application root path : " << m_rootPath;
|
||||
qInfo() << "Binary path : " << binPath;
|
||||
qInfo() << "Application root path : " << m_rootPath;
|
||||
if (!m_instanceIdToLaunch.isEmpty()) {
|
||||
qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
|
||||
qInfo() << "ID of instance to launch : " << m_instanceIdToLaunch;
|
||||
}
|
||||
if (!m_serverToJoin.isEmpty()) {
|
||||
qDebug() << "Address of server to join :" << m_serverToJoin;
|
||||
qInfo() << "Address of server to join :" << m_serverToJoin;
|
||||
} else if (!m_worldToJoin.isEmpty()) {
|
||||
qDebug() << "Name of the world to join :" << m_worldToJoin;
|
||||
qInfo() << "Name of the world to join :" << m_worldToJoin;
|
||||
}
|
||||
qDebug() << "<> Paths set.";
|
||||
qInfo() << "<> Paths set.";
|
||||
}
|
||||
|
||||
if (m_liveCheck) {
|
||||
@ -818,7 +891,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
PixmapCache::setInstance(new PixmapCache(this));
|
||||
|
||||
qDebug() << "<> Settings loaded.";
|
||||
qInfo() << "<> Settings loaded.";
|
||||
}
|
||||
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
@ -834,7 +907,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
QString user = settings()->get("ProxyUser").toString();
|
||||
QString pass = settings()->get("ProxyPass").toString();
|
||||
updateProxySettings(proxyTypeStr, addr, port, user, pass);
|
||||
qDebug() << "<> Network done.";
|
||||
qInfo() << "<> Network done.";
|
||||
}
|
||||
|
||||
// load translations
|
||||
@ -842,8 +915,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_translations.reset(new TranslationsModel("translations"));
|
||||
auto bcp47Name = m_settings->get("Language").toString();
|
||||
m_translations->selectLanguage(bcp47Name);
|
||||
qDebug() << "Your language is" << bcp47Name;
|
||||
qDebug() << "<> Translations loaded.";
|
||||
qInfo() << "Your language is" << bcp47Name;
|
||||
qInfo() << "<> Translations loaded.";
|
||||
}
|
||||
|
||||
// Instance icons
|
||||
@ -854,7 +927,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_icons.reset(new IconList(instFolders, setting->get().toString()));
|
||||
connect(setting.get(), &Setting::SettingChanged,
|
||||
[this](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
|
||||
qDebug() << "<> Instance icons initialized.";
|
||||
qInfo() << "<> Instance icons initialized.";
|
||||
}
|
||||
|
||||
// Themes
|
||||
@ -866,25 +939,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
// instance path: check for problems with '!' in instance path and warn the user in the log
|
||||
// and remember that we have to show him a dialog when the gui starts (if it does so)
|
||||
QString instDir = InstDirSetting->get().toString();
|
||||
qDebug() << "Instance path : " << instDir;
|
||||
qInfo() << "Instance path : " << instDir;
|
||||
if (FS::checkProblemticPathJava(QDir(instDir))) {
|
||||
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
|
||||
}
|
||||
m_instances.reset(new InstanceList(m_settings, instDir, this));
|
||||
connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
|
||||
qDebug() << "Loading Instances...";
|
||||
qInfo() << "Loading Instances...";
|
||||
m_instances->loadList();
|
||||
qDebug() << "<> Instances loaded.";
|
||||
qInfo() << "<> Instances loaded.";
|
||||
}
|
||||
|
||||
// and accounts
|
||||
{
|
||||
m_accounts.reset(new AccountList(this));
|
||||
qDebug() << "Loading accounts...";
|
||||
qInfo() << "Loading accounts...";
|
||||
m_accounts->setListFilePath("accounts.json", true);
|
||||
m_accounts->loadList();
|
||||
m_accounts->fillQueue();
|
||||
qDebug() << "<> Accounts loaded.";
|
||||
qInfo() << "<> Accounts loaded.";
|
||||
}
|
||||
|
||||
// init the http meta cache
|
||||
@ -905,7 +978,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||
m_metacache->addBase("java", QDir("cache/java").absolutePath());
|
||||
m_metacache->Load();
|
||||
qDebug() << "<> Cache initialized.";
|
||||
qInfo() << "<> Cache initialized.";
|
||||
}
|
||||
|
||||
// now we have network, download translation updates
|
||||
|
@ -99,7 +99,7 @@ set(CORE_SOURCES
|
||||
MTPixmapCache.h
|
||||
)
|
||||
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
||||
set(CORE_SOURCES
|
||||
set(CORE_SOURCES
|
||||
${CORE_SOURCES}
|
||||
|
||||
# MangoHud
|
||||
@ -589,8 +589,8 @@ set(ATLAUNCHER_SOURCES
|
||||
)
|
||||
|
||||
set(LINKEXE_SOURCES
|
||||
WindowsConsole.cpp
|
||||
WindowsConsole.h
|
||||
console/WindowsConsole.h
|
||||
console/WindowsConsole.cpp
|
||||
|
||||
filelink/FileLink.h
|
||||
filelink/FileLink.cpp
|
||||
@ -659,6 +659,14 @@ set(PRISMUPDATER_SOURCES
|
||||
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set(PRISMUPDATER_SOURCES
|
||||
console/WindowsConsole.h
|
||||
console/WindowsConsole.cpp
|
||||
${PRISMUPDATER_SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
||||
######## Logging categories ########
|
||||
|
||||
ecm_qt_declare_logging_category(CORE_SOURCES
|
||||
@ -786,6 +794,9 @@ SET(LAUNCHER_SOURCES
|
||||
SysInfo.h
|
||||
SysInfo.cpp
|
||||
|
||||
# console utils
|
||||
console/Console.h
|
||||
|
||||
# GUI - general utilities
|
||||
DesktopServices.h
|
||||
DesktopServices.cpp
|
||||
@ -1154,7 +1165,7 @@ SET(LAUNCHER_SOURCES
|
||||
)
|
||||
|
||||
if (NOT Apple)
|
||||
set(LAUNCHER_SOURCES
|
||||
set(LAUNCHER_SOURCES
|
||||
${LAUNCHER_SOURCES}
|
||||
|
||||
ui/dialogs/UpdateAvailableDialog.h
|
||||
@ -1164,8 +1175,8 @@ endif()
|
||||
|
||||
if(WIN32)
|
||||
set(LAUNCHER_SOURCES
|
||||
WindowsConsole.cpp
|
||||
WindowsConsole.h
|
||||
console/WindowsConsole.h
|
||||
console/WindowsConsole.cpp
|
||||
${LAUNCHER_SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
@ -428,7 +428,7 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr>&
|
||||
|
||||
QList<InstanceId> InstanceList::discoverInstances()
|
||||
{
|
||||
qDebug() << "Discovering instances in" << m_instDir;
|
||||
qInfo() << "Discovering instances in" << m_instDir;
|
||||
QList<InstanceId> out;
|
||||
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
|
||||
while (iter.hasNext()) {
|
||||
@ -447,7 +447,7 @@ QList<InstanceId> InstanceList::discoverInstances()
|
||||
}
|
||||
auto id = dirInfo.fileName();
|
||||
out.append(id);
|
||||
qDebug() << "Found instance ID" << id;
|
||||
qInfo() << "Found instance ID" << id;
|
||||
}
|
||||
instanceSet = QSet<QString>(out.begin(), out.end());
|
||||
m_instancesProbed = true;
|
||||
@ -464,7 +464,7 @@ InstanceList::InstListError InstanceList::loadList()
|
||||
if (existingIds.contains(id)) {
|
||||
auto instPair = existingIds[id];
|
||||
existingIds.remove(id);
|
||||
qDebug() << "Should keep and soft-reload" << id;
|
||||
qInfo() << "Should keep and soft-reload" << id;
|
||||
} else {
|
||||
InstancePtr instPtr = loadInstance(id);
|
||||
if (instPtr) {
|
||||
|
33
launcher/console/Console.h
Normal file
33
launcher/console/Console.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <ostream>
|
||||
#if defined Q_OS_WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
namespace console {
|
||||
|
||||
inline bool isConsole()
|
||||
{
|
||||
#if defined Q_OS_WIN32
|
||||
DWORD procIDs[2];
|
||||
DWORD maxCount = 2;
|
||||
DWORD result = GetConsoleProcessList((LPDWORD)procIDs, maxCount);
|
||||
return result > 1;
|
||||
#else
|
||||
if (isatty(fileno(stdout))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace console
|
@ -16,13 +16,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "WindowsConsole.h"
|
||||
#include <system_error>
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
void RedirectHandle(DWORD handle, FILE* stream, const char* mode)
|
||||
@ -126,3 +131,29 @@ bool AttachWindowsConsole()
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code EnableAnsiSupport()
|
||||
{
|
||||
// ref: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||
// Using `CreateFileW("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected
|
||||
HANDLE console_handle = CreateFileW(L"CONOUT$", FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
|
||||
if (console_handle == INVALID_HANDLE_VALUE) {
|
||||
return std::error_code(GetLastError(), std::system_category());
|
||||
}
|
||||
|
||||
// ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode
|
||||
DWORD console_mode;
|
||||
if (0 == GetConsoleMode(console_handle, &console_mode)) {
|
||||
return std::error_code(GetLastError(), std::system_category());
|
||||
}
|
||||
|
||||
// VT processing not already enabled?
|
||||
if ((console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) {
|
||||
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
|
||||
if (0 == SetConsoleMode(console_handle, console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
|
||||
return std::error_code(GetLastError(), std::system_category());
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
@ -21,5 +21,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <system_error>
|
||||
|
||||
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr);
|
||||
bool AttachWindowsConsole();
|
||||
std::error_code EnableAnsiSupport();
|
@ -37,7 +37,10 @@
|
||||
#include <sys.h>
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include "WindowsConsole.h"
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include "console/WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
@ -46,11 +46,8 @@
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include "console/WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
@ -87,113 +84,13 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
|
||||
}
|
||||
}
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
|
||||
// taken from https://stackoverflow.com/a/25927081
|
||||
// getting a proper output to console with redirection support on windows is apparently hell
|
||||
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr)
|
||||
{
|
||||
// Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been
|
||||
// observed that the file number of our standard handle file objects can be assigned internally to a value of -2
|
||||
// when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our
|
||||
// call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value
|
||||
// before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to
|
||||
// use the "nul" device, which will place them into a valid state, after which we can redirect them to our target
|
||||
// using the "_dup2" function.
|
||||
if (bindStdIn) {
|
||||
FILE* dummyFile;
|
||||
freopen_s(&dummyFile, "nul", "r", stdin);
|
||||
}
|
||||
if (bindStdOut) {
|
||||
FILE* dummyFile;
|
||||
freopen_s(&dummyFile, "nul", "w", stdout);
|
||||
}
|
||||
if (bindStdErr) {
|
||||
FILE* dummyFile;
|
||||
freopen_s(&dummyFile, "nul", "w", stderr);
|
||||
}
|
||||
|
||||
// Redirect unbuffered stdin from the current standard input handle
|
||||
if (bindStdIn) {
|
||||
HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (stdHandle != INVALID_HANDLE_VALUE) {
|
||||
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
|
||||
if (fileDescriptor != -1) {
|
||||
FILE* file = _fdopen(fileDescriptor, "r");
|
||||
if (file != NULL) {
|
||||
int dup2Result = _dup2(_fileno(file), _fileno(stdin));
|
||||
if (dup2Result == 0) {
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect unbuffered stdout to the current standard output handle
|
||||
if (bindStdOut) {
|
||||
HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (stdHandle != INVALID_HANDLE_VALUE) {
|
||||
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
|
||||
if (fileDescriptor != -1) {
|
||||
FILE* file = _fdopen(fileDescriptor, "w");
|
||||
if (file != NULL) {
|
||||
int dup2Result = _dup2(_fileno(file), _fileno(stdout));
|
||||
if (dup2Result == 0) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect unbuffered stderr to the current standard error handle
|
||||
if (bindStdErr) {
|
||||
HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
if (stdHandle != INVALID_HANDLE_VALUE) {
|
||||
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
|
||||
if (fileDescriptor != -1) {
|
||||
FILE* file = _fdopen(fileDescriptor, "w");
|
||||
if (file != NULL) {
|
||||
int dup2Result = _dup2(_fileno(file), _fileno(stderr));
|
||||
if (dup2Result == 0) {
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the
|
||||
// standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In
|
||||
// versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything
|
||||
// has been read from or written to the targets or not.
|
||||
if (bindStdIn) {
|
||||
std::wcin.clear();
|
||||
std::cin.clear();
|
||||
}
|
||||
if (bindStdOut) {
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
}
|
||||
if (bindStdErr) {
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{
|
||||
#if defined Q_OS_WIN32
|
||||
// attach the parent console if stdout not already captured
|
||||
auto stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
if (stdout_type == FILE_TYPE_CHAR || stdout_type == FILE_TYPE_UNKNOWN) {
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
BindCrtHandlesToStdHandles(true, true, true);
|
||||
if (AttachWindowsConsole()) {
|
||||
consoleAttached = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
setOrganizationName(BuildConfig.LAUNCHER_NAME);
|
||||
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
|
||||
|
Loading…
x
Reference in New Issue
Block a user