diff options
author | Jonathan Ketchker <iontankatchker@gmail.com> | 2023-08-27 12:45:04 +0300 |
---|---|---|
committer | Jonathan Ketchker <iontankatchker@gmail.com> | 2023-09-07 01:17:13 +0300 |
commit | 55f0738f1638356137e6bd60459dc186ceaaabd8 (patch) | |
tree | 6d60bde37dab7ce24e32fb81145254d09e6c65a1 | |
parent | 505ed52cd4dcef081d9868424057451bd1dce497 (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.qdocinc | 4 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths.cpp | 24 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths.h | 4 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_android.cpp | 3 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_haiku.cpp | 4 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_mac.mm | 11 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_unix.cpp | 19 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_win.cpp | 23 | ||||
-rw-r--r-- | src/tools/qtpaths/qtpaths.cpp | 2 | ||||
-rw-r--r-- | tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp | 44 |
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); |