Lagrange [work/v1.11]

XDG: Check XDG_CONFIG_HOME and the user's download directory

=> 3190f16e20bfe03d83cead40d4dc220d619ec924

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a3687e9c..095a279f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,6 +31,7 @@ option (ENABLE_KERNING          "Enable kerning in font renderer (slower)" ON)
 option (ENABLE_RESOURCE_EMBED   "Embed resources inside the executable" OFF)
 option (ENABLE_WINDOWPOS_FIX    "Set position after showing window (workaround for SDL bug)" OFF)
 option (ENABLE_IDLE_SLEEP       "While idle, sleep in the main thread instead of waiting for events" ON)
+option (ENABLE_DOWNLOAD_EDIT    "Allow changing the Downloads directory" ON)
 
 include (BuildType.cmake)
 include (res/Embed.cmake)
@@ -226,6 +227,9 @@ endif ()
 if (ENABLE_IDLE_SLEEP)
     target_compile_definitions (app PUBLIC LAGRANGE_IDLE_SLEEP=1)
 endif ()
+if (ENABLE_DOWNLOAD_EDIT)
+    target_compile_definitions (app PUBLIC LAGRANGE_DOWNLOAD_EDIT=1)
+endif ()
 target_link_libraries (app PUBLIC the_Foundation::the_Foundation)
 target_link_libraries (app PUBLIC ${SDL2_LDFLAGS})
 if (APPLE)
diff --git a/src/app.c b/src/app.c
index 10e6d4ee..195b3b43 100644
--- a/src/app.c
+++ b/src/app.c
@@ -83,17 +83,17 @@ static const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange";
 #endif
 #if defined (iPlatformLinux) || defined (iPlatformOther)
 #define EMB_BIN  "../../share/lagrange/resources.lgr"
-static const char *dataDir_App_ = "~/.config/lagrange";
+static const char *defaultDataDir_App_ = "~/.config/lagrange";
 #endif
 #if defined (LAGRANGE_EMB_BIN) /* specified in build config */
 #  undef EMB_BIN
 #  define EMB_BIN LAGRANGE_EMB_BIN
 #endif
 #define EMB_BIN2 "../resources.lgr" /* fallback from build/executable dir */
-static const char *prefsFileName_App_    = "prefs.cfg";
-static const char *oldStateFileName_App_ = "state.binary";
-static const char *stateFileName_App_    = "state.lgr";
-static const char *downloadDir_App_      = "~/Downloads";
+static const char *prefsFileName_App_      = "prefs.cfg";
+static const char *oldStateFileName_App_   = "state.binary";
+static const char *stateFileName_App_      = "state.lgr";
+static const char *defaultDownloadDir_App_ = "~/Downloads";
 
 static const int idleThreshold_App_ = 1000; /* ms */
 
@@ -213,14 +213,42 @@ static iString *serializePrefs_App_(const iApp *d) {
     return str;
 }
 
+static const char *dataDir_App_(void) {
+#if defined (iPlatformLinux) || defined (iPlatformOther)
+    const char *configHome = getenv("XDG_CONFIG_HOME");
+    if (configHome) {
+        return concatPath_CStr(configHome, "lagrange");
+    }
+#endif
+    return defaultDataDir_App_;
+}
+
+static const char *downloadDir_App_(void) {
+#if defined (iPlatformLinux) || defined (iPlatformOther)
+    /* Parse user-dirs.dirs using the `xdg-user-dir` tool. */
+    iProcess *proc = iClob(new_Process());
+    setArguments_Process(
+        proc, iClob(newStringsCStr_StringList("/usr/bin/env", "xdg-user-dir", "DOWNLOAD", NULL)));
+    if (start_Process(proc)) {
+        iString *path = collect_String(newLocal_String(collect_Block(
+            readOutputUntilClosed_Process(proc))));
+        trim_String(path);
+        if (!isEmpty_String(path)) {
+            return cstr_String(path);
+        }
+    }
+#endif
+    return defaultDownloadDir_App_;
+}
+
 static const iString *prefsFileName_(void) {
-    return collect_String(concatCStr_Path(&iStringLiteral(dataDir_App_), prefsFileName_App_));
+    return collectNewCStr_String(concatPath_CStr(dataDir_App_(), prefsFileName_App_));
 }
 
 static void loadPrefs_App_(iApp *d) {
     iUnused(d);
     /* Create the data dir if it doesn't exist yet. */
-    makeDirs_Path(collectNewCStr_String(dataDir_App_));
+    makeDirs_Path(collectNewCStr_String(dataDir_App_()));
     iFile *f = new_File(prefsFileName_());
     if (open_File(f, readOnly_FileMode | text_FileMode)) {
         iString *str = readString_File(f);
@@ -267,8 +295,8 @@ static const char *magicTabDocument_App_ = "tabd";
 
 static iBool loadState_App_(iApp *d) {
     iUnused(d);
-    const char *oldPath = concatPath_CStr(dataDir_App_, oldStateFileName_App_);
-    const char *path    = concatPath_CStr(dataDir_App_, stateFileName_App_);
+    const char *oldPath = concatPath_CStr(dataDir_App_(), oldStateFileName_App_);
+    const char *path    = concatPath_CStr(dataDir_App_(), stateFileName_App_);
     iFile *f = iClob(newCStr_File(fileExistsCStr_FileInfo(path) ? path : oldPath));
     if (open_File(f, readOnly_FileMode)) {
         char magic[4];
@@ -323,7 +351,7 @@ iObjectList *listDocuments_App(void) {
 static void saveState_App_(const iApp *d) {
     iUnused(d);
     trimCache_App();
-    iFile *f = newCStr_File(concatPath_CStr(dataDir_App_, stateFileName_App_));
+    iFile *f = newCStr_File(concatPath_CStr(dataDir_App_(), stateFileName_App_));
     if (open_File(f, writeOnly_FileMode)) {
         writeData_File(f, magicState_App_, 4);
         writeU32_File(f, latest_FileVersion); /* version */
@@ -348,7 +376,7 @@ static uint32_t checkAsleep_App_(uint32_t interval, void *param) {
 #endif
 
 static void init_App_(iApp *d, int argc, char **argv) {
-    const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_));
+    const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_()));
     d->isFinishedLaunching = iFalse;
     d->launchCommands      = new_StringList();
     iZap(d->lastDropTime);
@@ -385,12 +413,12 @@ static void init_App_(iApp *d, int argc, char **argv) {
     }
 #endif
     init_Prefs(&d->prefs);
-    setCStr_String(&d->prefs.downloadDir, downloadDir_App_);
+    setCStr_String(&d->prefs.downloadDir, downloadDir_App_());
     d->isRunning         = iFalse;
     d->window            = NULL;
     set_Atomic(&d->pendingRefresh, iFalse);
     d->mimehooks         = new_MimeHooks();
-    d->certs             = new_GmCerts(dataDir_App_);
+    d->certs             = new_GmCerts(dataDir_App_());
     d->visited           = new_Visited();
     d->bookmarks         = new_Bookmarks();
     d->tabEnum           = 0; /* generates unique IDs for tab pages */
@@ -405,10 +433,10 @@ static void init_App_(iApp *d, int argc, char **argv) {
 #endif
     init_Keys();
     loadPrefs_App_(d);
-    load_Keys(dataDir_App_);
-    load_Visited(d->visited, dataDir_App_);
-    load_Bookmarks(d->bookmarks, dataDir_App_);
-    load_MimeHooks(d->mimehooks, dataDir_App_);
+    load_Keys(dataDir_App_());
+    load_Visited(d->visited, dataDir_App_());
+    load_Bookmarks(d->bookmarks, dataDir_App_());
+    load_MimeHooks(d->mimehooks, dataDir_App_());
     if (isFirstRun) {
         /* Create the default bookmarks for a quick start. */
         add_Bookmarks(d->bookmarks,
@@ -434,7 +462,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
     }
 #endif
     d->window = new_Window(d->initialWindowRect);
-    init_Feeds(dataDir_App_);
+    init_Feeds(dataDir_App_());
     /* Widget state init. */
     processEvents_App(postedEventsOnly_AppEventMode);
     if (!loadState_App_(d)) {
@@ -469,13 +497,13 @@ static void init_App_(iApp *d, int argc, char **argv) {
 static void deinit_App(iApp *d) {
     saveState_App_(d);
     deinit_Feeds();
-    save_Keys(dataDir_App_);
+    save_Keys(dataDir_App_());
     deinit_Keys();
     savePrefs_App_(d);
     deinit_Prefs(&d->prefs);
-    save_Bookmarks(d->bookmarks, dataDir_App_);
+    save_Bookmarks(d->bookmarks, dataDir_App_());
     delete_Bookmarks(d->bookmarks);
-    save_Visited(d->visited, dataDir_App_);
+    save_Visited(d->visited, dataDir_App_());
     delete_Visited(d->visited);
     delete_GmCerts(d->certs);
     save_MimeHooks(d->mimehooks);
@@ -494,7 +522,7 @@ const iString *execPath_App(void) {
 }
 
 const iString *dataDir_App(void) {
-    return collect_String(cleanedCStr_Path(dataDir_App_));
+    return collect_String(cleanedCStr_Path(dataDir_App_()));
 }
 
 const iString *downloadDir_App(void) {
@@ -854,8 +882,10 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
     if (equal_Command(cmd, "prefs.dismiss") || equal_Command(cmd, "preferences")) {
         setUiScale_Window(get_Window(),
                           toFloat_String(text_InputWidget(findChild_Widget(d, "prefs.uiscale"))));
+#if defined (LAGRANGE_DOWNLOAD_EDIT)
         postCommandf_App("downloads path:%s",
                          cstr_String(text_InputWidget(findChild_Widget(d, "prefs.downloads"))));
+#endif
         postCommandf_App("window.retain arg:%d",
                          isSelected_Widget(findChild_Widget(d, "prefs.retainwindow")));
         postCommandf_App("smoothscroll arg:%d",
@@ -1449,7 +1479,7 @@ iBool handleCommand_App(const char *cmd) {
         return iTrue;
     }
     else if (equal_Command(cmd, "bookmarks.changed")) {
-        save_Bookmarks(d->bookmarks, dataDir_App_);
+        save_Bookmarks(d->bookmarks, dataDir_App_());
         return iFalse;
     }
     else if (equal_Command(cmd, "feeds.refresh")) {
@@ -1468,7 +1498,7 @@ iBool handleCommand_App(const char *cmd) {
         return iFalse;
     }
     else if (equal_Command(cmd, "visited.changed")) {
-        save_Visited(d->visited, dataDir_App_);
+        save_Visited(d->visited, dataDir_App_());
         return iFalse;
     }
     else if (equal_Command(cmd, "ident.new")) {
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 3f799e3c..1674040b 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -223,6 +223,7 @@ static void updateBuffered_InputWidget_(iInputWidget *d) {
 }
 
 void setText_InputWidget(iInputWidget *d, const iString *text) {
+    if (!d) return;
     if (d->inFlags & isUrl_InputWidgetFlag) {
         /* If user wants URLs encoded, also Punycode the domain. */
         if (!prefs_App()->decodeUserVisibleURLs) {
diff --git a/src/ui/util.c b/src/ui/util.c
index 4d5ed916..d64a93b6 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1023,6 +1023,7 @@ static void makeTwoColumnHeading_(const char *title, iWidget *headings, iWidget
 }
 
 static void expandInputFieldWidth_(iInputWidget *input) {
+    if (!input) return;
     iWidget *page = as_Widget(input)->parent->parent->parent->parent; /* tabs > page > values > input */
     as_Widget(input)->rect.size.x =
         right_Rect(bounds_Widget(page)) - left_Rect(bounds_Widget(constAs_Widget(input)));
@@ -1056,8 +1057,10 @@ iWidget *makePreferences_Widget(void) {
     iWidget *headings, *values;
     /* General preferences. */ {
         appendTwoColumnPage_(tabs, "General", '1', &headings, &values);
+#if defined (LAGRANGE_DOWNLOAD_EDIT)
         addChild_Widget(headings, iClob(makeHeading_Widget("Downloads folder:")));
         setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.downloads");
+#endif
         addChild_Widget(headings, iClob(makeHeading_Widget("Show URL on hover:")));
         addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink")));
         addChild_Widget(headings, iClob(makeHeading_Widget("Smooth scrolling:")));
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.11/cdiff/3190f16e20bfe03d83cead40d4dc220d619ec924
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
52.033967 milliseconds
Gemini-to-HTML Time
0.501963 milliseconds

This content has been proxied by September (ba2dc).