summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Ketchker <iontankatchker@gmail.com>2023-08-27 12:45:04 +0300
committerJonathan Ketchker <iontankatchker@gmail.com>2023-09-07 01:17:13 +0300
commit55f0738f1638356137e6bd60459dc186ceaaabd8 (patch)
tree6d60bde37dab7ce24e32fb81145254d09e6c65a1
parent505ed52cd4dcef081d9868424057451bd1dce497 (diff)
Add StateLocation & GenericStateLocation to StandardLocation
The latest XDG spec (0.8) defines XDG_STATE_HOME that does not exist in QStandardPaths::StandardLocation. Some Linux distributions clean XDG_CACHE_HOME on restart which makes XDG_STATE_HOME useful as a path for saving application state. This commit adds StateLocation and GenericStateLocation to serve as a StandardLocation for XDG_STATE_HOME for all platforms. This commit also updates docs and tests to fit the new changes. [ChangeLog][QStandardPaths] Added StateLocation & GenericStateLocation to StandardLocation Change-Id: I470602466c37f085062cc64d15ea243711728fa5 Reviewed-by: David Faure <david.faure@kdab.com> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--doc/global/includes/standardpath/functiondocs.qdocinc4
-rw-r--r--src/corelib/io/qstandardpaths.cpp24
-rw-r--r--src/corelib/io/qstandardpaths.h4
-rw-r--r--src/corelib/io/qstandardpaths_android.cpp3
-rw-r--r--src/corelib/io/qstandardpaths_haiku.cpp4
-rw-r--r--src/corelib/io/qstandardpaths_mac.mm11
-rw-r--r--src/corelib/io/qstandardpaths_unix.cpp19
-rw-r--r--src/corelib/io/qstandardpaths_win.cpp23
-rw-r--r--src/tools/qtpaths/qtpaths.cpp2
-rw-r--r--tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp44
10 files changed, 130 insertions, 8 deletions
diff --git a/doc/global/includes/standardpath/functiondocs.qdocinc b/doc/global/includes/standardpath/functiondocs.qdocinc
index c0f4763b90..5a008414c8 100644
--- a/doc/global/includes/standardpath/functiondocs.qdocinc
+++ b/doc/global/includes/standardpath/functiondocs.qdocinc
@@ -71,11 +71,13 @@
It affects the locations into which test programs might write
files: \c GenericDataLocation, \c AppDataLocation, \c ConfigLocation,
\c GenericConfigLocation, \c AppConfigLocation,
+ \c StateLocation, \c GenericStateLocation,
\c GenericCacheLocation, and \c CacheLocation. Other locations
are not affected.
On Unix, \c XDG_DATA_HOME is set to \c{~/.qttest/share},
- \c XDG_CONFIG_HOME is set to \c{~/.qttest/config}, and
+ \c XDG_CONFIG_HOME is set to \c{~/.qttest/config},
+ \c XDG_STATE_HOME is set \c{~/.qttest/state} and
\c XDG_CACHE_HOME is set to \c{~/.qttest/cache}.
On macOS, data goes to \c{~/.qttest/Application Support},
diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp
index 9a14ae0717..925a48dc94 100644
--- a/src/corelib/io/qstandardpaths.cpp
+++ b/src/corelib/io/qstandardpaths.cpp
@@ -126,6 +126,12 @@ using namespace Qt::StringLiterals;
template files can be stored. This is a generic value. Note that the returned path may be
empty if the system has no concept of a templates location.
This enum value was added in Qt 6.4.
+ \value [since 6.7] StateLocation Returns a directory location where user-specific application
+ state data files should be written. This is an application-specific directory,
+ and the returned path is never empty.
+ \value [since 6.7] GenericStateLocation Returns a directory location where shared state data files
+ across applications should be written. This value might be generic or application-specific,
+ but the returned path is never empty.
The following table gives examples of paths on different operating systems.
The first path is the writable path (unless noted). Other, additional
@@ -166,6 +172,9 @@ using namespace Qt::StringLiterals;
\row \li CacheLocation
\li "~/Library/Caches/<APPNAME>", "/Library/Caches/<APPNAME>"
\li "C:/Users/<USER>/AppData/Local/<APPNAME>/cache"
+ \row \li StateLocation
+ \li "~/Library/Preferences/<APPNAME>/State"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>/State", "C:/ProgramData/<APPNAME>/State"
\row \li GenericDataLocation
\li "~/Library/Application Support", "/Library/Application Support"
\li "C:/Users/<USER>/AppData/Local", "C:/ProgramData", "<APPDIR>", "<APPDIR>/data"
@@ -184,6 +193,9 @@ using namespace Qt::StringLiterals;
\row \li GenericCacheLocation
\li "~/Library/Caches", "/Library/Caches"
\li "C:/Users/<USER>/AppData/Local/cache"
+ \row \li GenericStateLocation
+ \li "~/Library/Preferences/State"
+ \li "C:/Users/<USER>/AppData/Local/State", "C:/ProgramData/State"
\row \li AppDataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
\li "C:/Users/<USER>/AppData/Roaming/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data", "<APPDIR>/data/<APPNAME>"
@@ -222,6 +234,8 @@ using namespace Qt::StringLiterals;
\li "~/.local/share/<APPNAME>", "/usr/local/share/<APPNAME>", "/usr/share/<APPNAME>"
\row \li CacheLocation
\li "~/.cache/<APPNAME>"
+ \row \li StateLocation
+ \li "~/.local/state/<APPNAME>"
\row \li GenericDataLocation
\li "~/.local/share", "/usr/local/share", "/usr/share"
\row \li RuntimeLocation
@@ -234,6 +248,8 @@ using namespace Qt::StringLiterals;
\li "~/Downloads"
\row \li GenericCacheLocation
\li "~/.cache"
+ \row \li GenericStateLocation
+ \li "~/.local/state"
\row \li AppDataLocation
\li "~/.local/share/<APPNAME>", "/usr/local/share/<APPNAME>", "/usr/share/<APPNAME>"
\row \li AppConfigLocation
@@ -279,6 +295,10 @@ using namespace Qt::StringLiterals;
\row \li CacheLocation
\li "<APPROOT>/cache", "<USER>/<APPNAME>/cache"
\li "<APPROOT>/Library/Caches"
+ \row \li StateLocation
+ \li "<APPROOT>/files/state"
+ \row \li GenericStateLocation (there is shared state)
+ \li "<APPROOT>/files/state"
\row \li GenericDataLocation
\li "<USER>" [*] or "<USER>/<APPNAME>/files"
\li "<APPROOT>/Library/Application Support"
@@ -549,6 +569,8 @@ QString QStandardPaths::displayName(StandardLocation type)
return QCoreApplication::translate("QStandardPaths", "Application Data");
case CacheLocation:
return QCoreApplication::translate("QStandardPaths", "Cache");
+ case StateLocation:
+ return QCoreApplication::translate("QStandardPaths", "State");
case GenericDataLocation:
return QCoreApplication::translate("QStandardPaths", "Shared Data");
case RuntimeLocation:
@@ -559,6 +581,8 @@ QString QStandardPaths::displayName(StandardLocation type)
return QCoreApplication::translate("QStandardPaths", "Shared Configuration");
case GenericCacheLocation:
return QCoreApplication::translate("QStandardPaths", "Shared Cache");
+ case GenericStateLocation:
+ return QCoreApplication::translate("QStandardPaths", "Shared State");
case DownloadLocation:
return QCoreApplication::translate("QStandardPaths", "Download");
case AppDataLocation:
diff --git a/src/corelib/io/qstandardpaths.h b/src/corelib/io/qstandardpaths.h
index ca1e37d92c..3997957d25 100644
--- a/src/corelib/io/qstandardpaths.h
+++ b/src/corelib/io/qstandardpaths.h
@@ -38,7 +38,9 @@ public:
AppDataLocation,
AppConfigLocation,
PublicShareLocation,
- TemplatesLocation
+ TemplatesLocation,
+ StateLocation,
+ GenericStateLocation
};
Q_ENUM(StandardLocation)
diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp
index e058f379c2..f605752ffa 100644
--- a/src/corelib/io/qstandardpaths_android.cpp
+++ b/src/corelib/io/qstandardpaths_android.cpp
@@ -195,6 +195,9 @@ QString QStandardPaths::writableLocation(StandardLocation type)
case QStandardPaths::ConfigLocation:
case QStandardPaths::AppConfigLocation:
return getFilesDir() + testDir() + "/settings"_L1;
+ case QStandardPaths::StateLocation:
+ case QStandardPaths::GenericStateLocation:
+ return getFilesDir() + testDir() + "/state"_L1;
case QStandardPaths::GenericDataLocation:
{
return QAndroidApplication::sdkVersion() >= 30 ?
diff --git a/src/corelib/io/qstandardpaths_haiku.cpp b/src/corelib/io/qstandardpaths_haiku.cpp
index 6122e5f6f9..93eba134f3 100644
--- a/src/corelib/io/qstandardpaths_haiku.cpp
+++ b/src/corelib/io/qstandardpaths_haiku.cpp
@@ -120,8 +120,10 @@ QString QStandardPaths::writableLocation(StandardLocation type)
return haikuAppStandardPath(B_USER_CACHE_DIRECTORY);
case GenericCacheLocation:
return haikuStandardPath(B_USER_CACHE_DIRECTORY);
- case ConfigLocation: // fall through
+ case ConfigLocation:
case AppConfigLocation:
+ case StateLocation:
+ case GenericStateLocation:
return haikuAppStandardPath(B_USER_SETTINGS_DIRECTORY);
case GenericConfigLocation:
return haikuStandardPath(B_USER_SETTINGS_DIRECTORY);
diff --git a/src/corelib/io/qstandardpaths_mac.mm b/src/corelib/io/qstandardpaths_mac.mm
index 5a41ae8e92..2acbe92736 100644
--- a/src/corelib/io/qstandardpaths_mac.mm
+++ b/src/corelib/io/qstandardpaths_mac.mm
@@ -120,6 +120,12 @@ static QString baseWritableLocation(QStandardPaths::StandardLocation type,
case QStandardPaths::AppConfigLocation:
path = pathForDirectory(NSLibraryDirectory, mask) + "/Preferences"_L1;
break;
+ case QStandardPaths::StateLocation:
+ if (appendOrgAndApp) { break; }
+ Q_FALLTHROUGH();
+ case QStandardPaths::GenericStateLocation:
+ path = pathForDirectory(NSLibraryDirectory, mask) + "/Preferences/State"_L1;
+ break;
default:
path = pathForDirectory(dir, mask);
break;
@@ -133,6 +139,11 @@ static QString baseWritableLocation(QStandardPaths::StandardLocation type,
case QStandardPaths::CacheLocation:
appendOrganizationAndApp(path);
break;
+ case QStandardPaths::StateLocation:
+ path = pathForDirectory(NSLibraryDirectory, mask) + "/Preferences"_L1;
+ appendOrganizationAndApp(path);
+ path += "/State"_L1;
+ break;
default:
break;
}
diff --git a/src/corelib/io/qstandardpaths_unix.cpp b/src/corelib/io/qstandardpaths_unix.cpp
index 64d8a3fa49..e38f670895 100644
--- a/src/corelib/io/qstandardpaths_unix.cpp
+++ b/src/corelib/io/qstandardpaths_unix.cpp
@@ -196,6 +196,25 @@ QString QStandardPaths::writableLocation(StandardLocation type)
appendOrganizationAndApp(xdgCacheHome);
return xdgCacheHome;
}
+ case StateLocation:
+ case GenericStateLocation:
+ {
+ QString xdgStateHome;
+ if (isTestModeEnabled()) {
+ xdgStateHome = QDir::homePath() + "/.qttest/state"_L1;
+ } else {
+ // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
+ xdgStateHome = QFile::decodeName(qgetenv("XDG_STATE_HOME"));
+ if (!xdgStateHome.startsWith(u'/'))
+ xdgStateHome.clear(); // spec says relative paths should be ignored
+
+ if (xdgStateHome.isEmpty())
+ xdgStateHome = QDir::homePath() + "/.local/state"_L1;
+ }
+ if (type == QStandardPaths::StateLocation)
+ appendOrganizationAndApp(xdgStateHome);
+ return xdgStateHome;
+ }
case AppDataLocation:
case AppLocalDataLocation:
case GenericDataLocation:
diff --git a/src/corelib/io/qstandardpaths_win.cpp b/src/corelib/io/qstandardpaths_win.cpp
index 13b8fe224a..805ce65a5a 100644
--- a/src/corelib/io/qstandardpaths_win.cpp
+++ b/src/corelib/io/qstandardpaths_win.cpp
@@ -105,8 +105,10 @@ static GUID writableSpecialFolderId(QStandardPaths::StandardLocation type)
FOLDERID_LocalAppData, // AppConfigLocation ("Local" path)
FOLDERID_Public, // PublicShareLocation
FOLDERID_Templates, // TemplatesLocation
+ GUID(), // StateLocation
+ GUID(), // GenericStateLocation
};
- static_assert(sizeof(folderIds) / sizeof(folderIds[0]) == size_t(QStandardPaths::TemplatesLocation + 1));
+ static_assert(sizeof(folderIds) / sizeof(folderIds[0]) == size_t(QStandardPaths::GenericStateLocation + 1));
// folders for low integrity processes
static const GUID folderIds_li[] = {
@@ -130,6 +132,8 @@ static GUID writableSpecialFolderId(QStandardPaths::StandardLocation type)
FOLDERID_LocalAppDataLow,// AppConfigLocation ("Local" path)
FOLDERID_Public, // PublicShareLocation
FOLDERID_Templates, // TemplatesLocation
+ GUID(), // StateLocation
+ GUID(), // GenericStateLocation
};
static_assert(sizeof(folderIds_li) == sizeof(folderIds));
@@ -184,6 +188,23 @@ QString QStandardPaths::writableLocation(StandardLocation type)
result = QDir::tempPath();
break;
+ case StateLocation:
+ result = sHGetKnownFolderPath(writableSpecialFolderId(AppLocalDataLocation));
+ if (!result.isEmpty()) {
+ appendTestMode(result);
+ appendOrganizationAndApp(result);
+ result += "/State"_L1;
+ }
+ break;
+
+ case GenericStateLocation:
+ result = sHGetKnownFolderPath(writableSpecialFolderId(GenericDataLocation));
+ if (!result.isEmpty()) {
+ appendTestMode(result);
+ result += "/State"_L1;
+ }
+ break;
+
default:
result = sHGetKnownFolderPath(writableSpecialFolderId(type));
if (!result.isEmpty() && isConfigLocation(type)) {
diff --git a/src/tools/qtpaths/qtpaths.cpp b/src/tools/qtpaths/qtpaths.cpp
index db101efca5..a840f08ba6 100644
--- a/src/tools/qtpaths/qtpaths.cpp
+++ b/src/tools/qtpaths/qtpaths.cpp
@@ -71,12 +71,14 @@ static const StringEnum lookupTableData[] = {
{ "GenericCacheLocation", QStandardPaths::GenericCacheLocation, false },
{ "GenericConfigLocation", QStandardPaths::GenericConfigLocation, false },
{ "GenericDataLocation", QStandardPaths::GenericDataLocation, false },
+ { "GenericStateLocation", QStandardPaths::GenericStateLocation, false },
{ "HomeLocation", QStandardPaths::HomeLocation, false },
{ "MoviesLocation", QStandardPaths::MoviesLocation, false },
{ "MusicLocation", QStandardPaths::MusicLocation, false },
{ "PicturesLocation", QStandardPaths::PicturesLocation, false },
{ "PublicShareLocation", QStandardPaths::PublicShareLocation, false },
{ "RuntimeLocation", QStandardPaths::RuntimeLocation, false },
+ { "StateLocation", QStandardPaths::StateLocation, true },
{ "TemplatesLocation", QStandardPaths::TemplatesLocation, false },
{ "TempLocation", QStandardPaths::TempLocation, false }
};
diff --git a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
index b59cf2bf3a..cc81b6ac7a 100644
--- a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
+++ b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
@@ -27,7 +27,7 @@
using namespace Qt::StringLiterals;
// Update this when adding new enum values; update enumNames too
-static const int MaxStandardLocation = QStandardPaths::AppConfigLocation;
+static const int MaxStandardLocation = QStandardPaths::GenericStateLocation;
static QString genericCacheLoc()
{
@@ -38,6 +38,15 @@ static QString cacheLoc()
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
}
+static QString genericStateLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::GenericStateLocation);
+}
+static QString stateLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::StateLocation);
+}
+
static QString genericDataLoc()
{
return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
@@ -98,14 +107,17 @@ private:
qputenv("XDG_CONFIG_DIRS", QFile::encodeName(m_globalConfigDir));
m_localAppDir = m_localAppTempDir.path();
m_globalAppDir = m_globalAppTempDir.path();
+ m_stateDir = m_stateTempDir.path();
qputenv("XDG_DATA_HOME", QFile::encodeName(m_localAppDir));
qputenv("XDG_DATA_DIRS", QFile::encodeName(m_globalAppDir));
+ qputenv("XDG_STATE_HOME", QFile::encodeName(m_stateDir));
}
void setDefaultLocations() {
qputenv("XDG_CONFIG_HOME", nullptr);
qputenv("XDG_CONFIG_DIRS", nullptr);
qputenv("XDG_DATA_HOME", nullptr);
qputenv("XDG_DATA_DIRS", nullptr);
+ qputenv("XDG_STATE_HOME", nullptr);
}
#endif
@@ -120,6 +132,8 @@ private:
QTemporaryDir m_localAppTempDir;
QString m_globalAppDir;
QTemporaryDir m_globalAppTempDir;
+ QString m_stateDir;
+ QTemporaryDir m_stateTempDir;
};
static const char * const enumNames[MaxStandardLocation + 1 - int(QStandardPaths::DesktopLocation)] = {
@@ -141,7 +155,11 @@ static const char * const enumNames[MaxStandardLocation + 1 - int(QStandardPaths
"GenericCacheLocation",
"GenericConfigLocation",
"AppDataLocation",
- "AppConfigLocation"
+ "AppConfigLocation",
+ "PublicShareLocation",
+ "TemplatesLocation",
+ "StateLocation",
+ "GenericStateLocation"
};
void tst_qstandardpaths::initTestCase()
@@ -160,6 +178,7 @@ void tst_qstandardpaths::initTestCase()
QVERIFY2(m_globalConfigTempDir.isValid(), qPrintable(m_globalConfigTempDir.errorString()));
QVERIFY2(m_localAppTempDir.isValid(), qPrintable(m_localAppTempDir.errorString()));
QVERIFY2(m_globalAppTempDir.isValid(), qPrintable(m_globalAppTempDir.errorString()));
+ QVERIFY2(m_stateTempDir.isValid(), qPrintable(m_stateTempDir.errorString()));
}
void tst_qstandardpaths::dump()
@@ -205,6 +224,8 @@ void tst_qstandardpaths::testDefaultLocations()
QCOMPARE(genericDataDirs.at(0), expectedDataHome);
QCOMPARE(genericDataDirs.at(1), QString::fromLatin1("/usr/local/share"));
QCOMPARE(genericDataDirs.at(2), QString::fromLatin1("/usr/share"));
+ const QString expectedGenericStateLocation = QDir::homePath() + QString::fromLatin1("/.local/state");
+ QCOMPARE(genericStateLoc(), expectedGenericStateLocation);
#endif
}
@@ -283,25 +304,34 @@ void tst_qstandardpaths::enableTestMode()
// CacheLocation should be "GenericCacheLocation/organization-name/app-name"
QCOMPARE(cacheLoc(), cacheDir + "/tst_qstandardpaths"_L1);
+ // *StateLocation
+ const QString stateDir = qttestDir + QLatin1String("/state");
+ QCOMPARE(genericStateLoc(), stateDir);
+ const QStringList stateDirs = QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation);
+ QCOMPARE(stateDirs, QStringList() << stateDir);
+ // StateLocation should be "GenericStateLocation/organization-name/app-name"
+ QCOMPARE(stateLoc(), stateDir + "/tst_qstandardpaths"_L1);
+
QCoreApplication::setOrganizationName("Qt");
QCOMPARE(appConfigLoc(), configDir + "/Qt/tst_qstandardpaths"_L1);
QCOMPARE(appDataLoc(), dataDir + "/Qt/tst_qstandardpaths"_L1);
QCOMPARE(appLocalDataLoc(), dataDir + "/Qt/tst_qstandardpaths"_L1);
QCOMPARE(cacheLoc(), cacheDir + "/Qt/tst_qstandardpaths"_L1);
- QCOMPARE(cacheLoc(), cacheDir + "/Qt/tst_qstandardpaths"_L1);
+ QCOMPARE(stateLoc(), stateDir + "/Qt/tst_qstandardpaths"_L1);
QCoreApplication::setApplicationName("QtTest");
QCOMPARE(appConfigLoc(), configDir + "/Qt/QtTest"_L1);
QCOMPARE(appDataLoc(), dataDir + "/Qt/QtTest"_L1);
QCOMPARE(appLocalDataLoc(), dataDir + "/Qt/QtTest"_L1);
- QCoreApplication::setApplicationName("QtTest");
QCOMPARE(cacheLoc(), cacheDir + "/Qt/QtTest"_L1);
+ QCOMPARE(stateLoc(), stateDir + "/Qt/QtTest"_L1);
// Check these are unaffected by org/app names
QCOMPARE(genericConfigLoc(), configDir);
QCOMPARE(configLoc(), configDir);
QCOMPARE(genericDataLoc(), dataDir);
QCOMPARE(genericCacheLoc(), cacheDir);
+ QCOMPARE(genericStateLoc(), stateDir);
#endif
// On all platforms, we want to ensure that the writableLocation is different in test mode and real mode.
@@ -315,6 +345,8 @@ void tst_qstandardpaths::enableTestMode()
testLocations.insert(QStandardPaths::GenericConfigLocation, genericConfigLoc());
testLocations.insert(QStandardPaths::CacheLocation, cacheLoc());
testLocations.insert(QStandardPaths::GenericCacheLocation, genericCacheLoc());
+ testLocations.insert(QStandardPaths::StateLocation, stateLoc());
+ testLocations.insert(QStandardPaths::GenericStateLocation, genericStateLoc());
// On Windows, what should "Program Files" become, in test mode?
//testLocations.insert(QStandardPaths::ApplicationsLocation, QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation));
@@ -807,6 +839,10 @@ void tst_qstandardpaths::testXdgPathCleanup()
const QString cacheDir = cacheLoc();
QCOMPARE_NE(cacheDir, relative);
+ qputenv("XDG_STATE_HOME", relative.toLatin1());
+ const QString stateDir = stateLoc();
+ QCOMPARE_NE(stateDir, relative);
+
qputenv("XDG_DATA_HOME", relative.toLatin1());
const QString localDataDir = genericDataLoc();
QCOMPARE_NE(localDataDir, relative);