From 082c3c480f2cccba44df5c13610ef9a933b8f9e1 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sun, 7 Apr 2024 22:30:37 -0700 Subject: [PATCH] core: add pragmas UseQApplication: use QApplication over QGuiApplication (for qqc2-desktop-style) NativeTextRendering: use NativeTextRendering over QtRendering for text Env VAR = VAL: define environment variables (usually qt ones) --- CMakeLists.txt | 4 +- src/core/main.cpp | 416 +++++++++++++++++++++++++++------------------- 2 files changed, 245 insertions(+), 175 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb68ab1..b5a6dff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,8 +57,8 @@ if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() -set(QT_DEPS Qt6::Gui Qt6::Qml Qt6::Quick Qt6::QuickControls2) -set(QT_FPDEPS Gui Qml Quick QuickControls2) +set(QT_DEPS Qt6::Gui Qt6::Qml Qt6::Quick Qt6::QuickControls2 Qt6::Widgets) +set(QT_FPDEPS Gui Qml Quick QuickControls2 Widgets) if (BUILD_TESTING) enable_testing() diff --git a/src/core/main.cpp b/src/core/main.cpp index 198011b..78b7008 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -1,11 +1,14 @@ #include "main.hpp" #include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -19,34 +22,41 @@ #include "rootwrapper.hpp" int qs_main(int argc, char** argv) { - const auto app = QGuiApplication(argc, argv); - QGuiApplication::setApplicationName("quickshell"); - QGuiApplication::setApplicationVersion("0.1.0 (" GIT_REVISION ")"); - - QCommandLineParser parser; - parser.addHelpOption(); - parser.addVersionOption(); - - // clang-format off - auto currentOption = QCommandLineOption("current", "Print information about the manifest and defaults."); - auto manifestOption = QCommandLineOption({"m", "manifest"}, "Path to a configuration manifest.", "path"); - auto configOption = QCommandLineOption({"c", "config"}, "Name of a configuration in the manifest.", "name"); - auto pathOption = QCommandLineOption({"p", "path"}, "Path to a configuration file.", "path"); - auto workdirOption = QCommandLineOption({"d", "workdir"}, "Initial working directory.", "path"); - // clang-format on - - parser.addOption(currentOption); - parser.addOption(manifestOption); - parser.addOption(configOption); - parser.addOption(pathOption); - parser.addOption(workdirOption); - parser.process(app); - QString configFilePath; - { - auto printCurrent = parser.isSet(currentOption); + QString workingDirectory; - // NOLINTBEGIN + auto useQApplication = false; + auto nativeTextRendering = false; + QHash envOverrides; + + { + const auto app = QCoreApplication(argc, argv); + QCoreApplication::setApplicationName("quickshell"); + QCoreApplication::setApplicationVersion("0.1.0 (" GIT_REVISION ")"); + + QCommandLineParser parser; + parser.addHelpOption(); + parser.addVersionOption(); + + // clang-format off + auto currentOption = QCommandLineOption("current", "Print information about the manifest and defaults."); + auto manifestOption = QCommandLineOption({"m", "manifest"}, "Path to a configuration manifest.", "path"); + auto configOption = QCommandLineOption({"c", "config"}, "Name of a configuration in the manifest.", "name"); + auto pathOption = QCommandLineOption({"p", "path"}, "Path to a configuration file.", "path"); + auto workdirOption = QCommandLineOption({"d", "workdir"}, "Initial working directory.", "path"); + // clang-format on + + parser.addOption(currentOption); + parser.addOption(manifestOption); + parser.addOption(configOption); + parser.addOption(pathOption); + parser.addOption(workdirOption); + parser.process(app); + + { + auto printCurrent = parser.isSet(currentOption); + + // NOLINTBEGIN #define CHECK(rname, name, level, label, expr) \ QString name = expr; \ if (rname.isEmpty() && !name.isEmpty()) { \ @@ -56,194 +66,250 @@ int qs_main(int argc, char** argv) { } #define OPTSTR(name) (name.isEmpty() ? "(unset)" : name.toStdString()) - // NOLINTEND - - QString basePath; - int basePathLevel = 0; - Q_UNUSED(basePathLevel); - { - // NOLINTBEGIN - // clang-format off - CHECK(basePath, envBasePath, 0, foundbase, qEnvironmentVariable("QS_BASE_PATH")); - CHECK(basePath, defaultBasePath, 0, foundbase, QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).filePath("quickshell")); - // clang-format on // NOLINTEND - if (printCurrent) { + QString basePath; + int basePathLevel = 0; + Q_UNUSED(basePathLevel); + { + // NOLINTBEGIN // clang-format off - std::cout << "Base path: " << OPTSTR(basePath) << "\n"; - std::cout << " - Environment (QS_BASE_PATH): " << OPTSTR(envBasePath) << "\n"; - std::cout << " - Default: " << OPTSTR(defaultBasePath) << "\n"; + CHECK(basePath, envBasePath, 0, foundbase, qEnvironmentVariable("QS_BASE_PATH")); + CHECK(basePath, defaultBasePath, 0, foundbase, QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).filePath("quickshell")); // clang-format on + // NOLINTEND + + if (printCurrent) { + // clang-format off + std::cout << "Base path: " << OPTSTR(basePath) << "\n"; + std::cout << " - Environment (QS_BASE_PATH): " << OPTSTR(envBasePath) << "\n"; + std::cout << " - Default: " << OPTSTR(defaultBasePath) << "\n"; + // clang-format on + } } - } - foundbase:; + foundbase:; - QString configPath; - int configPathLevel = 10; - { - // NOLINTBEGIN - CHECK(configPath, optionConfigPath, 0, foundpath, parser.value(pathOption)); - CHECK(configPath, envConfigPath, 1, foundpath, qEnvironmentVariable("QS_CONFIG_PATH")); - // NOLINTEND + QString configPath; + int configPathLevel = 10; + { + // NOLINTBEGIN + CHECK(configPath, optionConfigPath, 0, foundpath, parser.value(pathOption)); + CHECK(configPath, envConfigPath, 1, foundpath, qEnvironmentVariable("QS_CONFIG_PATH")); + // NOLINTEND - if (printCurrent) { + if (printCurrent) { + // clang-format off + std::cout << "\nConfig path: " << OPTSTR(configPath) << "\n"; + std::cout << " - Option: " << OPTSTR(optionConfigPath) << "\n"; + std::cout << " - Environment (QS_CONFIG_PATH): " << OPTSTR(envConfigPath) << "\n"; + // clang-format on + } + } + foundpath:; + + QString manifestPath; + int manifestPathLevel = 10; + { + // NOLINTBEGIN // clang-format off - std::cout << "\nConfig path: " << OPTSTR(configPath) << "\n"; - std::cout << " - Option: " << OPTSTR(optionConfigPath) << "\n"; - std::cout << " - Environment (QS_CONFIG_PATH): " << OPTSTR(envConfigPath) << "\n"; + CHECK(manifestPath, optionManifestPath, 0, foundmf, parser.value(manifestOption)); + CHECK(manifestPath, envManifestPath, 1, foundmf, qEnvironmentVariable("QS_MANIFEST")); + CHECK(manifestPath, defaultManifestPath, 2, foundmf, QDir(basePath).filePath("manifest.conf")); // clang-format on + // NOLINTEND + + if (printCurrent) { + // clang-format off + std::cout << "\nManifest path: " << OPTSTR(manifestPath) << "\n"; + std::cout << " - Option: " << OPTSTR(optionManifestPath) << "\n"; + std::cout << " - Environment (QS_MANIFEST): " << OPTSTR(envManifestPath) << "\n"; + std::cout << " - Default: " << OPTSTR(defaultManifestPath) << "\n"; + // clang-format on + } } - } - foundpath:; + foundmf:; - QString manifestPath; - int manifestPathLevel = 10; - { - // NOLINTBEGIN - // clang-format off - CHECK(manifestPath, optionManifestPath, 0, foundmf, parser.value(manifestOption)); - CHECK(manifestPath, envManifestPath, 1, foundmf, qEnvironmentVariable("QS_MANIFEST")); - CHECK(manifestPath, defaultManifestPath, 2, foundmf, QDir(basePath).filePath("manifest.conf")); - // clang-format on - // NOLINTEND + QString configName; + int configNameLevel = 10; + { + // NOLINTBEGIN + CHECK(configName, optionConfigName, 0, foundname, parser.value(configOption)); + CHECK(configName, envConfigName, 1, foundname, qEnvironmentVariable("QS_CONFIG_NAME")); + // NOLINTEND - if (printCurrent) { - // clang-format off - std::cout << "\nManifest path: " << OPTSTR(manifestPath) << "\n"; - std::cout << " - Option: " << OPTSTR(optionManifestPath) << "\n"; - std::cout << " - Environment (QS_MANIFEST): " << OPTSTR(envManifestPath) << "\n"; - std::cout << " - Default: " << OPTSTR(defaultManifestPath) << "\n"; - // clang-format on + if (printCurrent) { + // clang-format off + std::cout << "\nConfig name: " << OPTSTR(configName) << "\n"; + std::cout << " - Option: " << OPTSTR(optionConfigName) << "\n"; + std::cout << " - Environment (QS_CONFIG_NAME): " << OPTSTR(envConfigName) << "\n\n"; + // clang-format on + } } - } - foundmf:; + foundname:; - QString configName; - int configNameLevel = 10; - { - // NOLINTBEGIN - CHECK(configName, optionConfigName, 0, foundname, parser.value(configOption)); - CHECK(configName, envConfigName, 1, foundname, qEnvironmentVariable("QS_CONFIG_NAME")); - // NOLINTEND - - if (printCurrent) { - // clang-format off - std::cout << "\nConfig name: " << OPTSTR(configName) << "\n"; - std::cout << " - Option: " << OPTSTR(optionConfigName) << "\n"; - std::cout << " - Environment (QS_CONFIG_NAME): " << OPTSTR(envConfigName) << "\n\n"; - // clang-format on + if (configPathLevel == 0 && configNameLevel == 0) { + qCritical() << "Pass only one of --path or --config"; + return -1; } - } - foundname:; - if (configPathLevel == 0 && configNameLevel == 0) { - qFatal() << "Pass only one of --path or --config"; - return -1; - } + if (!configPath.isEmpty() && configPathLevel <= configNameLevel) { + configFilePath = configPath; + } else if (!configName.isEmpty()) { + if (!manifestPath.isEmpty()) { + auto file = QFile(manifestPath); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + auto stream = QTextStream(&file); + while (!stream.atEnd()) { + auto line = stream.readLine(); + if (line.trimmed().startsWith("#")) continue; + if (line.trimmed().isEmpty()) continue; - if (!configPath.isEmpty() && configPathLevel <= configNameLevel) { - configFilePath = configPath; - } else if (!configName.isEmpty()) { - if (!manifestPath.isEmpty()) { - auto file = QFile(manifestPath); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - auto stream = QTextStream(&file); - while (!stream.atEnd()) { - auto line = stream.readLine(); - if (line.trimmed().startsWith("#")) continue; - if (line.trimmed().isEmpty()) continue; + auto split = line.split('='); + if (split.length() != 2) { + qCritical() << "manifest line not in expected format 'name = relativepath':" + << line; + return -1; + } - auto split = line.split('='); - if (split.length() != 2) { - qFatal() << "manifest line not in expected format 'name = relativepath':" << line; - return -1; + if (split[0].trimmed() == configName) { + configFilePath = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed()); + goto haspath; // NOLINT + } } - if (split[0].trimmed() == configName) { - configFilePath = QDir(QFileInfo(file).canonicalPath()).filePath(split[1].trimmed()); + qCritical() << "configuration" << configName << "not found in manifest" << manifestPath; + return -1; + } else if (manifestPathLevel < 2) { + qCritical() << "cannot open config manifest at" << manifestPath; + return -1; + } + } + + { + auto basePathInfo = QFileInfo(basePath); + if (!basePathInfo.exists()) { + qCritical() << "base path does not exist:" << basePath; + return -1; + } else if (!QFileInfo(basePathInfo.canonicalFilePath()).isDir()) { + qCritical() << "base path is not a directory" << basePath; + return -1; + } + + auto dir = QDir(basePath); + for (auto& entry: dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { + if (entry == configName) { + configFilePath = dir.filePath(entry); goto haspath; // NOLINT } } - qFatal() << "configuration" << configName << "not found in manifest" << manifestPath; - return -1; - } else if (manifestPathLevel < 2) { - qFatal() << "cannot open config manifest at" << manifestPath; + qCritical() << "no directory named " << configName << "found in base path" << basePath; return -1; } + haspath:; + } else { + configFilePath = basePath; } - { - auto basePathInfo = QFileInfo(basePath); - if (!basePathInfo.exists()) { - qFatal() << "base path does not exist:" << basePath; - return -1; - } else if (!QFileInfo(basePathInfo.canonicalFilePath()).isDir()) { - qFatal() << "base path is not a directory" << basePath; - return -1; - } - - auto dir = QDir(basePath); - for (auto& entry: dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { - if (entry == configName) { - configFilePath = dir.filePath(entry); - goto haspath; // NOLINT - } - } - - qFatal() << "no directory named " << configName << "found in base path" << basePath; + auto configFile = QFileInfo(configFilePath); + if (!configFile.exists()) { + qCritical() << "config path does not exist:" << configFilePath; return -1; } - haspath:; - } else { - configFilePath = basePath; - } - auto configFile = QFileInfo(configFilePath); - if (!configFile.exists()) { - qFatal() << "config path does not exist:" << configFilePath; - return -1; - } + if (configFile.isDir()) { + configFilePath = QDir(configFilePath).filePath("shell.qml"); + } - if (configFile.isDir()) { - configFilePath = QDir(configFilePath).filePath("shell.qml"); - } + configFile = QFileInfo(configFilePath); + if (!configFile.exists()) { + qCritical() << "no shell.qml found in config path:" << configFilePath; + return -1; + } else if (configFile.isDir()) { + qCritical() << "shell.qml is a directory:" << configFilePath; + return -1; + } - configFile = QFileInfo(configFilePath); - if (!configFile.exists()) { - qFatal() << "no shell.qml found in config path:" << configFilePath; - return -1; - } else if (configFile.isDir()) { - qFatal() << "shell.qml is a directory:" << configFilePath; - return -1; - } - - configFilePath = QFileInfo(configFilePath).canonicalFilePath(); - configFile = QFileInfo(configFilePath); - if (!configFile.exists()) { - qFatal() << "config file does not exist:" << configFilePath; - return -1; - } else if (configFile.isDir()) { - qFatal() << "config file is a directory:" << configFilePath; - return -1; - } + configFilePath = QFileInfo(configFilePath).canonicalFilePath(); + configFile = QFileInfo(configFilePath); + if (!configFile.exists()) { + qCritical() << "config file does not exist:" << configFilePath; + return -1; + } else if (configFile.isDir()) { + qCritical() << "config file is a directory:" << configFilePath; + return -1; + } #undef CHECK #undef OPTSTR - qInfo() << "config file path:" << configFilePath; + qInfo() << "config file path:" << configFilePath; - if (printCurrent) return 0; + if (printCurrent) return 0; + } + + if (!QFile(configFilePath).exists()) { + qCritical() << "config file does not exist"; + return -1; + } + + if (parser.isSet(workdirOption)) { + workingDirectory = parser.value(workdirOption); + } + + auto file = QFile(configFilePath); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + qCritical() << "could not open config file"; + return -1; + } + + auto stream = QTextStream(&file); + while (!stream.atEnd()) { + auto line = stream.readLine().trimmed(); + if (line.startsWith("//@ pragma ")) { + auto pragma = line.sliced(11).trimmed(); + + if (pragma == "UseQApplication") useQApplication = true; + else if (pragma == "NativeTextRendering") nativeTextRendering = true; + else if (pragma.startsWith("Env ")) { + auto envPragma = pragma.sliced(4); + auto splitIdx = envPragma.indexOf('='); + + if (splitIdx == -1) { + qCritical() << "Env pragma" << pragma << "not in the form 'VAR = VALUE'"; + return -1; + } + + auto var = envPragma.sliced(0, splitIdx).trimmed(); + auto val = envPragma.sliced(splitIdx + 1).trimmed(); + envOverrides.insert(var, val); + } else { + qCritical() << "Unrecognized pragma" << pragma; + return -1; + } + } else if (line.startsWith("import")) break; + } + + file.close(); } - if (!QFile(configFilePath).exists()) { - qCritical() << "config file does not exist"; - return -1; + for (auto [var, val]: envOverrides.asKeyValueRange()) { + qputenv(var.toUtf8(), val.toUtf8()); } - if (parser.isSet(workdirOption)) { - QDir::setCurrent(parser.value(workdirOption)); + QCoreApplication* app = nullptr; + + if (useQApplication) { + app = new QApplication(argc, argv); + } else { + app = new QGuiApplication(argc, argv); + } + + QCoreApplication::setApplicationName("quickshell"); + QCoreApplication::setApplicationVersion("0.1.0 (" GIT_REVISION ")"); + + if (!workingDirectory.isEmpty()) { + QDir::setCurrent(workingDirectory); } QuickshellPlugin::initPlugins(); @@ -252,8 +318,12 @@ int qs_main(int argc, char** argv) { // Use a fully transparent window with a colored rect. QQuickWindow::setDefaultAlphaBuffer(true); + if (nativeTextRendering) { + QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering); + } + auto root = RootWrapper(configFilePath); QGuiApplication::setQuitOnLastWindowClosed(false); - return QGuiApplication::exec(); + return QCoreApplication::exec(); }