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>
This commit is contained in:
parent
505ed52cd4
commit
55f0738f16
@ -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},
|
||||
|
@ -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:
|
||||
|
@ -38,7 +38,9 @@ public:
|
||||
AppDataLocation,
|
||||
AppConfigLocation,
|
||||
PublicShareLocation,
|
||||
TemplatesLocation
|
||||
TemplatesLocation,
|
||||
StateLocation,
|
||||
GenericStateLocation
|
||||
};
|
||||
Q_ENUM(StandardLocation)
|
||||
|
||||
|
@ -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 ?
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)) {
|
||||
|
@ -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 }
|
||||
};
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user