Skip to content

Commit cb195d4

Browse files
committed
launch: look for configs in all XDG config dirs
1 parent b898592 commit cb195d4

File tree

2 files changed

+85
-19
lines changed

2 files changed

+85
-19
lines changed

src/launch/command.cpp

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
#include <qjsonarray.h>
1616
#include <qjsondocument.h>
1717
#include <qjsonobject.h>
18+
#include <qlist.h>
1819
#include <qlogging.h>
1920
#include <qloggingcategory.h>
2021
#include <qnamespace.h>
2122
#include <qstandardpaths.h>
23+
#include <qtenvironmentvariables.h>
2224
#include <qtversion.h>
2325
#include <unistd.h>
2426

@@ -36,6 +38,44 @@ using qs::ipc::IpcClient;
3638

3739
namespace {
3840

41+
QList<QString> configBaseDirs() {
42+
auto configHome = qEnvironmentVariable("XDG_CONFIG_HOME");
43+
if (configHome.isEmpty()) {
44+
auto home = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
45+
configHome = QDir(home).filePath(".config");
46+
}
47+
48+
auto configDirs = qEnvironmentVariable("XDG_CONFIG_DIRS").split(':', Qt::SkipEmptyParts);
49+
if (configDirs.isEmpty()) {
50+
configDirs.append("/etc/xdg");
51+
}
52+
53+
configDirs.prepend(configHome);
54+
55+
for (auto& dir: configDirs) {
56+
dir.append("/quickshell");
57+
}
58+
59+
return configDirs;
60+
}
61+
62+
QString locateNamedConfig(const QString& name) {
63+
for (const auto& baseDir: configBaseDirs()) {
64+
auto shellPath = QDir(baseDir).filePath("shell.qml");
65+
auto hasShell = QFileInfo(shellPath).isFile();
66+
67+
if (hasShell) {
68+
if (name == "default") return shellPath;
69+
else continue; // skip subfolders if shell.qml is present in folder
70+
}
71+
72+
shellPath = QDir(QDir(baseDir).filePath(name)).filePath("shell.qml");
73+
if (QFileInfo(shellPath).isFile()) return shellPath;
74+
}
75+
76+
return QString();
77+
}
78+
3979
int locateConfigFile(CommandState& cmd, QString& path) {
4080
if (!cmd.config.path->isEmpty()) {
4181
path = *cmd.config.path;
@@ -48,6 +88,11 @@ int locateConfigFile(CommandState& cmd, QString& path) {
4888
}
4989

5090
if (!manifestPath.isEmpty()) {
91+
qWarning(
92+
) << "Config manifests (manifest.conf) are deprecated and will be removed in a future "
93+
"release.";
94+
qWarning() << "Consider using symlinks to a subfolder of quickshell's XDG config dirs.";
95+
5196
auto file = QFile(manifestPath);
5297
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
5398
auto stream = QTextStream(&file);
@@ -78,13 +123,23 @@ int locateConfigFile(CommandState& cmd, QString& path) {
78123
return -1;
79124
}
80125
} else {
81-
auto configDir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
126+
const auto& name = cmd.config.name->isEmpty() ? "default" : *cmd.config.name;
127+
path = locateNamedConfig(name);
128+
129+
if (path.isEmpty()) {
130+
if (name == "default") {
131+
qCCritical(logBare
132+
) << "Could not find \"default\" config directory or shell.qml in any valid config path.";
133+
} else {
134+
qCCritical(logBare) << "Could not find" << name
135+
<< "config directory in any valid config path.";
136+
}
82137

83-
if (cmd.config.name->isEmpty()) {
84-
path = configDir.path();
85-
} else {
86-
path = configDir.filePath(*cmd.config.name);
138+
return -1;
87139
}
140+
141+
path = QFileInfo(path).canonicalFilePath();
142+
return 0;
88143
}
89144
}
90145

@@ -98,7 +153,6 @@ int locateConfigFile(CommandState& cmd, QString& path) {
98153
}
99154

100155
path = QFileInfo(path).canonicalFilePath();
101-
102156
return 0;
103157
}
104158

src/launch/parsecommand.cpp

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,38 @@ int parseCommand(int argc, char** argv, CommandState& state) {
1717
};
1818

1919
auto addConfigSelection = [&](CLI::App* cmd, bool withNewestOption = false) {
20-
auto* group = cmd->add_option_group("Config Selection")
21-
->description("If no options in this group are specified,\n"
22-
"$XDG_CONFIG_HOME/quickshell/shell.qml will be used.");
20+
auto* group =
21+
cmd->add_option_group("Config Selection")
22+
->description(
23+
"Quickshell detects configurations as named directories under each XDG config "
24+
"directory as `<xdg dir>/quickshell/<config name>/shell.qml`.\n\n"
25+
"If `<xdg dir>/quickshell/shell.qml` exists, it will be registered as the "
26+
"'default' configuration, and no subdirectories will be considered. "
27+
"If --config is not passed, 'default' will be assumed.\n\n"
28+
"Alternatively, a config can be selected by path with --path.\n\n"
29+
"Examples:\n"
30+
"- `~/.config/quickshell/shell.qml` can be run with `qs`\n"
31+
"- `/etc/xdg/quickshell/myconfig/shell.qml` can be run with `qs -c myconfig`\n"
32+
"- `~/myshell/shell.qml` can be run with `qs -p ~/myshell`\n"
33+
"- `~/myshell/randomfile.qml` can be run with `qs -p ~/myshell/randomfile.qml`"
34+
);
2335

2436
auto* path = group->add_option("-p,--path", state.config.path)
25-
->description("Path to a QML file.")
37+
->description("Path to a QML file or config folder.")
2638
->envname("QS_CONFIG_PATH");
2739

40+
group->add_option("-c,--config", state.config.name)
41+
->description("Name of a quickshell configuration to run.")
42+
->envname("QS_CONFIG_NAME")
43+
->excludes(path);
44+
2845
group->add_option("-m,--manifest", state.config.manifest)
29-
->description("Path to a quickshell manifest.\n"
46+
->description("[DEPRECATED] Path to a quickshell manifest.\n"
47+
"If a manifest is specified, configs named by -c will point to its entries.\n"
3048
"Defaults to $XDG_CONFIG_HOME/quickshell/manifest.conf")
3149
->envname("QS_MANIFEST")
3250
->excludes(path);
3351

34-
group->add_option("-c,--config", state.config.name)
35-
->description("Name of a quickshell configuration to run.\n"
36-
"If -m is specified, this is a configuration in the manifest,\n"
37-
"otherwise it is the name of a folder in $XDG_CONFIG_HOME/quickshell.")
38-
->envname("QS_CONFIG_NAME");
39-
4052
if (withNewestOption) {
4153
group->add_flag("-n,--newest", state.config.newest)
4254
->description("Operate on the most recently launched instance instead of the oldest");
@@ -64,7 +76,7 @@ int parseCommand(int argc, char** argv, CommandState& state) {
6476

6577
group->add_flag("--no-color", state.log.noColor)
6678
->description("Disables colored logging.\n"
67-
"Colored logging can also be disabled by specifying a non empty value\n"
79+
"Colored logging can also be disabled by specifying a non empty value "
6880
"for the NO_COLOR environment variable.");
6981

7082
group->add_flag("--log-times", state.log.timestamp)
@@ -87,7 +99,7 @@ int parseCommand(int argc, char** argv, CommandState& state) {
8799

88100
group->add_option("-i,--id", state.instance.id)
89101
->description("The instance id to operate on.\n"
90-
"You may also use a substring the id as long as it is unique,\n"
102+
"You may also use a substring the id as long as it is unique, "
91103
"for example \"abc\" will select \"abcdefg\".");
92104

93105
group->add_option("--pid", state.instance.pid)

0 commit comments

Comments
 (0)