diff --git a/src/launch/command.cpp b/src/launch/command.cpp index b8f1d151..eb944ec7 100644 --- a/src/launch/command.cpp +++ b/src/launch/command.cpp @@ -15,10 +15,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -36,6 +38,44 @@ using qs::ipc::IpcClient; namespace { +QList configBaseDirs() { + auto configHome = qEnvironmentVariable("XDG_CONFIG_HOME"); + if (configHome.isEmpty()) { + auto home = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + configHome = QDir(home).filePath(".config"); + } + + auto configDirs = qEnvironmentVariable("XDG_CONFIG_DIRS").split(':', Qt::SkipEmptyParts); + if (configDirs.isEmpty()) { + configDirs.append("/etc/xdg"); + } + + configDirs.prepend(configHome); + + for (auto& dir: configDirs) { + dir.append("/quickshell"); + } + + return configDirs; +} + +QString locateNamedConfig(const QString& name) { + for (const auto& baseDir: configBaseDirs()) { + auto shellPath = QDir(baseDir).filePath("shell.qml"); + auto hasShell = QFileInfo(shellPath).isFile(); + + if (hasShell) { + if (name == "default") return shellPath; + else continue; // skip subfolders if shell.qml is present in folder + } + + shellPath = QDir(QDir(baseDir).filePath(name)).filePath("shell.qml"); + if (QFileInfo(shellPath).isFile()) return shellPath; + } + + return QString(); +} + int locateConfigFile(CommandState& cmd, QString& path) { if (!cmd.config.path->isEmpty()) { path = *cmd.config.path; @@ -48,6 +88,11 @@ int locateConfigFile(CommandState& cmd, QString& path) { } if (!manifestPath.isEmpty()) { + qWarning( + ) << "Config manifests (manifest.conf) are deprecated and will be removed in a future " + "release."; + qWarning() << "Consider using symlinks to a subfolder of quickshell's XDG config dirs."; + auto file = QFile(manifestPath); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { auto stream = QTextStream(&file); @@ -78,13 +123,23 @@ int locateConfigFile(CommandState& cmd, QString& path) { return -1; } } else { - auto configDir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); + const auto& name = cmd.config.name->isEmpty() ? "default" : *cmd.config.name; + path = locateNamedConfig(name); - if (cmd.config.name->isEmpty()) { - path = configDir.path(); - } else { - path = configDir.filePath(*cmd.config.name); + if (path.isEmpty()) { + if (name == "default") { + qCCritical(logBare + ) << "Could not find \"default\" config directory or shell.qml in any valid config path."; + } else { + qCCritical(logBare) << "Could not find" << name + << "config directory in any valid config path."; + } + + return -1; } + + path = QFileInfo(path).canonicalFilePath(); + return 0; } } @@ -98,7 +153,6 @@ int locateConfigFile(CommandState& cmd, QString& path) { } path = QFileInfo(path).canonicalFilePath(); - return 0; } diff --git a/src/launch/parsecommand.cpp b/src/launch/parsecommand.cpp index 1edbf01e..e49ded75 100644 --- a/src/launch/parsecommand.cpp +++ b/src/launch/parsecommand.cpp @@ -17,26 +17,38 @@ int parseCommand(int argc, char** argv, CommandState& state) { }; auto addConfigSelection = [&](CLI::App* cmd, bool withNewestOption = false) { - auto* group = cmd->add_option_group("Config Selection") - ->description("If no options in this group are specified,\n" - "$XDG_CONFIG_HOME/quickshell/shell.qml will be used."); + auto* group = + cmd->add_option_group("Config Selection") + ->description( + "Quickshell detects configurations as named directories under each XDG config " + "directory as `/quickshell//shell.qml`.\n\n" + "If `/quickshell/shell.qml` exists, it will be registered as the " + "'default' configuration, and no subdirectories will be considered. " + "If --config is not passed, 'default' will be assumed.\n\n" + "Alternatively, a config can be selected by path with --path.\n\n" + "Examples:\n" + "- `~/.config/quickshell/shell.qml` can be run with `qs`\n" + "- `/etc/xdg/quickshell/myconfig/shell.qml` can be run with `qs -c myconfig`\n" + "- `~/myshell/shell.qml` can be run with `qs -p ~/myshell`\n" + "- `~/myshell/randomfile.qml` can be run with `qs -p ~/myshell/randomfile.qml`" + ); auto* path = group->add_option("-p,--path", state.config.path) - ->description("Path to a QML file.") + ->description("Path to a QML file or config folder.") ->envname("QS_CONFIG_PATH"); + group->add_option("-c,--config", state.config.name) + ->description("Name of a quickshell configuration to run.") + ->envname("QS_CONFIG_NAME") + ->excludes(path); + group->add_option("-m,--manifest", state.config.manifest) - ->description("Path to a quickshell manifest.\n" + ->description("[DEPRECATED] Path to a quickshell manifest.\n" + "If a manifest is specified, configs named by -c will point to its entries.\n" "Defaults to $XDG_CONFIG_HOME/quickshell/manifest.conf") ->envname("QS_MANIFEST") ->excludes(path); - group->add_option("-c,--config", state.config.name) - ->description("Name of a quickshell configuration to run.\n" - "If -m is specified, this is a configuration in the manifest,\n" - "otherwise it is the name of a folder in $XDG_CONFIG_HOME/quickshell.") - ->envname("QS_CONFIG_NAME"); - if (withNewestOption) { group->add_flag("-n,--newest", state.config.newest) ->description("Operate on the most recently launched instance instead of the oldest"); @@ -64,7 +76,7 @@ int parseCommand(int argc, char** argv, CommandState& state) { group->add_flag("--no-color", state.log.noColor) ->description("Disables colored logging.\n" - "Colored logging can also be disabled by specifying a non empty value\n" + "Colored logging can also be disabled by specifying a non empty value " "for the NO_COLOR environment variable."); group->add_flag("--log-times", state.log.timestamp) @@ -87,7 +99,7 @@ int parseCommand(int argc, char** argv, CommandState& state) { group->add_option("-i,--id", state.instance.id) ->description("The instance id to operate on.\n" - "You may also use a substring the id as long as it is unique,\n" + "You may also use a substring the id as long as it is unique, " "for example \"abc\" will select \"abcdefg\"."); group->add_option("--pid", state.instance.pid)