Skip to content

Commit 0c2f8d5

Browse files
committed
Support XDG Base directory specification for Linux
Ref: 1. https://specifications.freedesktop.org/basedir-spec/latest 2. aria2/aria2@8bc1d37
1 parent 1b2dd29 commit 0c2f8d5

File tree

7 files changed

+41
-10
lines changed

7 files changed

+41
-10
lines changed

libaegisub/common/path.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ constexpr std::string_view tokens[] = {
2929
"?script",
3030
"?temp",
3131
"?user",
32-
"?video"
32+
"?video",
33+
"?state"
3334
};
3435

3536
int find_token(std::string_view str) {
@@ -44,6 +45,7 @@ int find_token(std::string_view str) {
4445
case 't' + 'p': idx = 5; break;
4546
case 'u' + 'r': idx = 6; break;
4647
case 'v' + 'e': idx = 7; break;
48+
case 's' + 't': idx = 8; break;
4749
default: return -1;
4850
}
4951
return str.starts_with(tokens[idx]) ? idx : -1;

libaegisub/unix/path.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ std::string home_dir() {
4141
throw agi::EnvironmentError("Could not get home directory. Make sure HOME is set.");
4242
}
4343

44+
std::string xdg_dir(std::string_view token, agi::fs::path const& fallback) {
45+
const char *env = getenv(token.data());
46+
if (env) return env;
47+
return fallback.string();
48+
}
49+
4450
#ifdef APPIMAGE_BUILD
4551
std::string exe_dir() {
4652
char *exe, *dir;
@@ -70,12 +76,32 @@ namespace agi {
7076
void Path::FillPlatformSpecificPaths() {
7177
#ifndef __APPLE__
7278
agi::fs::path home = home_dir();
73-
SetToken("?user", home/".aegisub");
74-
SetToken("?local", home/".aegisub");
79+
agi::fs::path old_root = home/".aegisub";
80+
81+
agi::fs::path xdg_config = xdg_dir("XDG_CONFIG_HOME", home/".config");
82+
agi::fs::path xdg_cache = xdg_dir("XDG_CACHE_HOME", home/"cache");
83+
agi::fs::path xdg_state = xdg_dir("XDG_STATE_HOME", home/".local/state");
84+
agi::fs::path xdg_data = xdg_dir("XDG_DATA_HOME", home/".local/share");
85+
86+
if (agi::fs::DirectoryExists(old_root)) {
87+
SetToken("?user", old_root);
88+
SetToken("?local", old_root);
89+
SetToken("?state", old_root);
90+
} else {
91+
SetToken("?user", xdg_config/"aegisub");
92+
SetToken("?local", xdg_cache/"aegisub");
93+
SetToken("?state", xdg_state/"aegisub");
94+
}
7595

7696
#ifdef APPIMAGE_BUILD
7797
agi::fs::path data = exe_dir();
78-
if (data == "") data = home/".aegisub";
98+
if (data == "") {
99+
if (agi::fs::DirectoryExists(old_root)) {
100+
data = old_root;
101+
} else {
102+
data = xdg_data/"aegisub"
103+
}
104+
}
79105
SetToken("?data", data);
80106
SetToken("?dictionary", Decode("?data/dictionaries"));
81107
#else
@@ -87,6 +113,7 @@ void Path::FillPlatformSpecificPaths() {
87113
agi::fs::path app_support = agi::util::GetApplicationSupportDirectory();
88114
SetToken("?user", app_support/"Aegisub");
89115
SetToken("?local", app_support/"Aegisub");
116+
SetToken("?state", app_support/"Aegisub");
90117
SetToken("?data", agi::util::GetBundleSharedSupportDirectory());
91118
SetToken("?dictionary", Decode("?data/dictionaries"));
92119
#endif

libaegisub/windows/path_win.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ void Path::FillPlatformSpecificPaths() {
4545

4646
SetToken("?user", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub");
4747
SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA)/"Aegisub");
48+
SetToken("?state", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub");
4849

4950
std::wstring filename(MAX_PATH + 1, L'\0');
5051
while (static_cast<DWORD>(filename.size()) == GetModuleFileNameW(nullptr, &filename[0], filename.size()))

src/dialog_autosave.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ DialogAutosave::DialogAutosave(wxWindow *parent)
9292
std::map<wxString, AutosaveFile> files_map;
9393
Populate(files_map, OPT_GET("Path/Auto/Save")->GetString(), ".AUTOSAVE.ass", "%s");
9494
Populate(files_map, OPT_GET("Path/Auto/Backup")->GetString(), ".ORIGINAL.ass", _("%s [ORIGINAL BACKUP]"));
95-
Populate(files_map, "?user/recovered", ".ass", _("%s [RECOVERED]"));
95+
Populate(files_map, "?state/recovered", ".ass", _("%s [RECOVERED]"));
9696

9797
for (auto& file : files_map | boost::adaptors::map_values)
9898
files.emplace_back(std::move(file));

src/dialog_shift_times.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ static wxString get_history_string(json::Object &obj) {
133133
DialogShiftTimes::DialogShiftTimes(agi::Context *context)
134134
: wxDialog(context->parent, -1, _("Shift Times"))
135135
, context(context)
136-
, history_filename(config::path->Decode("?user/shift_history.json"))
136+
, history_filename(config::path->Decode("?state/shift_history.json"))
137137
, timecodes_loaded_slot(context->project->AddTimecodesListener(&DialogShiftTimes::OnTimecodesLoaded, this))
138138
, selected_set_changed_slot(context->selectionController->AddSelectionListener(&DialogShiftTimes::OnSelectedSetChanged, this))
139139
{

src/libresrc/default_config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@
285285

286286
"Path" : {
287287
"Auto" : {
288-
"Backup" : "?user/autoback",
289-
"Save" : "?user/autosave"
288+
"Backup" : "?state/autoback",
289+
"Save" : "?state/autosave"
290290
},
291291
"Automation" : {
292292
"Autoload" : "?user/automation/autoload/|?data/automation/autoload/",

src/main.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ bool AegisubApp::OnInit() {
154154
// Local config, make ?user mean ?data so all user settings are placed in install dir
155155
config::path->SetToken("?user", config::path->Decode("?data"));
156156
config::path->SetToken("?local", config::path->Decode("?data"));
157+
config::path->SetToken("?state", config::path->Decode("?data"));
157158
crash_writer::Initialize(config::path->Decode("?user"));
158159
} catch (agi::fs::FileSystemError const&) {
159160
// File doesn't exist or we can't read it
@@ -162,7 +163,7 @@ bool AegisubApp::OnInit() {
162163
#endif
163164

164165
StartupLog("Create log writer");
165-
auto path_log = config::path->Decode("?user/log/");
166+
auto path_log = config::path->Decode("?state/log/");
166167
agi::fs::CreateDirectory(path_log);
167168
agi::log::log->Subscribe(std::make_unique<agi::log::JsonEmitter>(path_log));
168169
CleanCache(path_log, "*.json", 10, 100);
@@ -373,7 +374,7 @@ void AegisubApp::UnhandledException(bool stackWalk) {
373374
auto c = frame->context.get();
374375
if (!c || !c->ass || !c->subsController) continue;
375376

376-
path = config::path->Decode("?user/recovered");
377+
path = config::path->Decode("?state/recovered");
377378
agi::fs::CreateDirectory(path);
378379

379380
auto filename = c->subsController->Filename().stem();

0 commit comments

Comments
 (0)