mirror of
https://github.com/fltk/fltk.git
synced 2026-05-21 14:31:40 +08:00
Preferences XDG path (#377)
* Preferences: remove CamelCase from public interface. * Prefs: documentation. * Prefs: updating Doxygen comments * XDG conforming preferences path
This commit is contained in:
+38
-27
@@ -32,19 +32,19 @@
|
||||
used documents.
|
||||
|
||||
Preferences are organized in a hierarchy of groups. Every group can contain
|
||||
more groups and any number of kay/value pairs. Keys can be text strings
|
||||
more groups and any number of key/value pairs. Keys can be text strings
|
||||
containing ASCII letters, digits, periods, and underscores. Forward slashes
|
||||
in a key name are treated as subgroups, i.e the key 'window/width' would
|
||||
actually refere to the key 'width' inside the group 'window'.
|
||||
actually refer to the key 'width' inside the group 'window'.
|
||||
|
||||
Keys have usually a unique name within their group. Duplicate kays are
|
||||
possible though and can beaccessed using the index based functions.
|
||||
Keys usually have a unique name within their group. Duplicate keys are
|
||||
possible though and can be accessed using the index based functions.
|
||||
|
||||
A value should be an ASCII string. Control characters and utf8 sequences are
|
||||
stores as octal values. Long strings will wrap at the line ending and will be
|
||||
A value can be an UTF-8 string. Control characters and UTF-8 sequences are
|
||||
stores as octal values. Long strings are wrap at the line ending and will be
|
||||
reassembled when reading the file back.
|
||||
|
||||
Many shortcuts exist to set and get numerical values and binary data.
|
||||
Several methods allow setting and getting numerical values and binary data.
|
||||
|
||||
Preferences are stored in text files that can be edited manually if needed.
|
||||
The file format is easy to read and relatively forgiving. Preferences files
|
||||
@@ -60,16 +60,17 @@
|
||||
Preferences should no be used to store document data. The .prefs file should
|
||||
be kept small for performance reasons. One application can have multiple
|
||||
preferences files. Extensive binary data however should be stored in separate
|
||||
files: see \a Fl_Preferences::getUserdataPath() .
|
||||
files: see \a Fl_Preferences::get_userdata_path() .
|
||||
|
||||
Fl_Preferences are not thread-safe. They can temprorarily change the locale
|
||||
on some platforms during read an write access, which is alse observable in
|
||||
other threads of the same app.
|
||||
on some platforms during read an write access, which is also changes it
|
||||
temporarily in other threads of the same app.
|
||||
|
||||
Typically a preferences database is read at startup and close, and then writte
|
||||
again at app shutdown:
|
||||
```.cpp
|
||||
Typically a preferences database is read at startup, and then reopended and
|
||||
written at app shutdown:
|
||||
```
|
||||
int appWindowWidth, appWindowHeight;
|
||||
|
||||
void launch() {
|
||||
Fl_Preferences app(Fl_Preferences::USER_L, "matthiasm.com", "hello");
|
||||
// 'app' constructor will be called, reading data from .prefs file
|
||||
@@ -78,6 +79,7 @@
|
||||
window.get("height", appWindowHeight, 600);
|
||||
// 'app' destructor will be called, writing data to .prefs file
|
||||
}
|
||||
|
||||
void quit() {
|
||||
Fl_Preferences app(Fl_Preferences::USER_L, "matthiasm.com", "hello");
|
||||
Fl_Preferences window(app, "window");
|
||||
@@ -88,10 +90,11 @@
|
||||
|
||||
\see Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application )
|
||||
|
||||
As a special case, Fl_Preferences can be memeory mapped and not be associated
|
||||
As a special case, Fl_Preferences can be memory mapped and not be associated
|
||||
with a file on disk.
|
||||
|
||||
\see Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group )
|
||||
for more details on memory mapped preferences.
|
||||
|
||||
\note Starting with FLTK 1.3, preference databases are expected to
|
||||
be in UTF-8 encoding. Previous databases were stored in the
|
||||
@@ -121,14 +124,14 @@ public:
|
||||
UNKNOWN_ROOT_TYPE = -1, ///< Returned if storage could not be determined.
|
||||
SYSTEM = 0, ///< Preferences are used system-wide, deprecated, see SYSTEM_L
|
||||
USER, ///< Preferences apply only to the current user, deprecated, see USER_L
|
||||
MEMORY, ///< Returned if querying runtime prefs
|
||||
MEMORY, ///< Returned if querying memory mapped preferences
|
||||
ROOT_MASK = 0xFF, ///< masks for the values above
|
||||
CORE = 0x100, ///< OR'd by FLTK to read and write core library preferences and options
|
||||
CORE_SYSTEM = CORE|SYSTEM,
|
||||
CORE_USER = CORE|USER,
|
||||
C_LOCALE = 0x1000, ///< this flag should always be set, it makes sure that floating point values wre writte correctly independently of the current locale
|
||||
SYSTEM_L = SYSTEM|C_LOCALE, ///< Preferences are used system-wide
|
||||
USER_L = USER|C_LOCALE, ///< Preferences apply only to the current user
|
||||
SYSTEM_L = SYSTEM|C_LOCALE, ///< Preferences are used system-wide, locale independent
|
||||
USER_L = USER|C_LOCALE, ///< Preferences apply only to the current user, locale independent
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -140,7 +143,7 @@ public:
|
||||
*/
|
||||
typedef void *ID;
|
||||
|
||||
static const char *newUUID();
|
||||
static const char *new_UUID();
|
||||
|
||||
/** Set this, if no call to Fl_Preferences shall access the file sytem
|
||||
@see Fl_Preferences::file_access(unsigned int)
|
||||
@@ -208,15 +211,15 @@ public:
|
||||
|
||||
int groups();
|
||||
const char *group( int num_group );
|
||||
char groupExists( const char *key );
|
||||
char deleteGroup( const char *group );
|
||||
char deleteAllGroups();
|
||||
char group_exists( const char *key );
|
||||
char delete_group( const char *group );
|
||||
char delete_all_groups();
|
||||
|
||||
int entries();
|
||||
const char *entry( int index );
|
||||
char entryExists( const char *key );
|
||||
char deleteEntry( const char *entry );
|
||||
char deleteAllEntries();
|
||||
char entry_exists( const char *key );
|
||||
char delete_entry( const char *entry );
|
||||
char delete_all_entries();
|
||||
|
||||
char clear();
|
||||
|
||||
@@ -239,14 +242,22 @@ public:
|
||||
|
||||
int size( const char *entry );
|
||||
|
||||
char getUserdataPath( char *path, int pathlen );
|
||||
char get_userdata_path( char *path, int pathlen );
|
||||
|
||||
int flush();
|
||||
|
||||
int dirty();
|
||||
|
||||
// char export( const char *filename, Type fileFormat );
|
||||
// char import( const char *filename );
|
||||
/** \cond PRIVATE */
|
||||
static const char *newUUID() { return new_UUID(); }
|
||||
char groupExists( const char *key ) { return group_exists(key); }
|
||||
char deleteGroup( const char *group ) { return delete_group(group); }
|
||||
char deleteAllGroups() { return delete_all_groups(); }
|
||||
char entryExists( const char *key ) { return entry_exists(key); }
|
||||
char deleteEntry( const char *entry ) { return delete_entry(entry); }
|
||||
char deleteAllEntries() { return delete_all_entries(); }
|
||||
char getUserdataPath( char *path, int pathlen ) { return get_userdata_path(path, pathlen); }
|
||||
/** \endcond */
|
||||
|
||||
/**
|
||||
'Name' provides a simple method to create numerical or more complex
|
||||
|
||||
+37
-21
@@ -63,7 +63,7 @@ static int clocale_sscanf(const char *input, const char *format, ...)
|
||||
\return a pointer to a static buffer containing the new UUID in ASCII format.
|
||||
The buffer is overwritten during every call to this function!
|
||||
*/
|
||||
const char *Fl_Preferences::newUUID() {
|
||||
const char *Fl_Preferences::new_UUID() {
|
||||
Fl::system_driver()->newUUID(uuidBuffer);
|
||||
return uuidBuffer;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ unsigned int Fl_Preferences::file_access()
|
||||
of the pathname componennts. This can be used to check if a preferneces file
|
||||
already exists.
|
||||
|
||||
\param[out] buffer write the reulting path into this buffer
|
||||
\param[out] buffer write the resulting path into this buffer
|
||||
\param[in] buffer_size size of the `buffer` in bytes
|
||||
\param[in] root can be \c USER_L or \c SYSTEM_L for user specific or system
|
||||
wide preferences
|
||||
@@ -157,10 +157,16 @@ Fl_Preferences::Root Fl_Preferences::filename( char *buffer, size_t buffer_size,
|
||||
|
||||
|
||||
/**
|
||||
The constructor creates a group that manages name/value pairs and
|
||||
child groups. Groups are ready for reading and writing at any time.
|
||||
The root argument is either `Fl_Preferences::USER_L`
|
||||
or `Fl_Preferences::SYSTEM_L`.
|
||||
The constructor creates a group that manages key/value pairs and
|
||||
child groups.
|
||||
|
||||
Preferences can be stored per user using the root type
|
||||
`Fl_Preferences::USER_L`, or stored system-wide using
|
||||
`Fl_Preferences::SYSTEM_L`.
|
||||
|
||||
Groups and key/value pairs can be read and written randomly. Reading undefined
|
||||
values will return the default value. Writing undefined values will create
|
||||
all required groups and key/vlaue pairs.
|
||||
|
||||
This constructor creates the <i>base</i> instance for all following entries
|
||||
and reads the database from disk into memory if it exists.
|
||||
@@ -192,12 +198,22 @@ Fl_Preferences::Root Fl_Preferences::filename( char *buffer, size_t buffer_size,
|
||||
check the path member of the passwd struct returned by \c getpwuid(getuid()) .
|
||||
If all attempts fail, data will be stored in RAM only and be lost when the
|
||||
app exits.
|
||||
The filename and path is then constructed as
|
||||
<tt>\$(directory)/.fltk/\$(vendor)/\$(application).prefs</tt> .
|
||||
The \c SYSTEM directory is hardcoded as
|
||||
|
||||
The \c SYSTEM preferences filename is hardcoded as
|
||||
<tt>/etc/fltk/\$(vendor)/\$(application).prefs</tt> .
|
||||
|
||||
\par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path
|
||||
For backward compatibility, the old \c USER `.prefs` file naming scheme
|
||||
<tt>\$(directory)/.fltk/\$(vendor)/\$(application).prefs</tt> is checked first.
|
||||
If that file does not exist, the environment variable `$XDG_CONFIG_HOME` is
|
||||
read as a base directory. If `$XDG_CONFIG_HOME` not set, the base directory
|
||||
defaults to `$HOME/.config/`.
|
||||
|
||||
The user preferences will be stored in
|
||||
<tt>\$(directory)/\$(vendor)/\$(application).prefs</tt>, The user data path
|
||||
will be
|
||||
<tt>\$(directory)/\$(vendor)/\$(application)/</tt>
|
||||
|
||||
In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path
|
||||
would be empty, generating <tt>\$(vendor)/\$(application).prefs</tt>, which
|
||||
was used relative to the current working directory.
|
||||
|
||||
@@ -270,11 +286,11 @@ Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group ) {
|
||||
only in local memory and is not associated with a file on disk. The root type
|
||||
of this databse is set to `Fl_Preferences::MEMORY`.
|
||||
|
||||
* the memory database is \em not shared among multiple instances of the same app
|
||||
* memory databses are \em not thread safe
|
||||
* all data will be lost when the app quits
|
||||
- the memory database is \em not shared among multiple instances of the same app
|
||||
- memory databses are \em not thread safe
|
||||
- all data will be lost when the app quits
|
||||
|
||||
```{.cpp}
|
||||
```
|
||||
void some_function() {
|
||||
Fl_Preferences guide( NULL, "Guide" );
|
||||
guide.set("answer", 42);
|
||||
@@ -457,7 +473,7 @@ const char *Fl_Preferences::group( int num_group ) {
|
||||
\param[in] key name of group that is searched for
|
||||
\return 0 if no group by that name was found
|
||||
*/
|
||||
char Fl_Preferences::groupExists( const char *key ) {
|
||||
char Fl_Preferences::group_exists( const char *key ) {
|
||||
return node->search( key ) ? 1 : 0 ;
|
||||
}
|
||||
|
||||
@@ -470,7 +486,7 @@ char Fl_Preferences::groupExists( const char *key ) {
|
||||
\param[in] group name of the group to delete
|
||||
\return 0 if call failed
|
||||
*/
|
||||
char Fl_Preferences::deleteGroup( const char *group ) {
|
||||
char Fl_Preferences::delete_group( const char *group ) {
|
||||
Node *nd = node->search( group );
|
||||
if ( nd ) return nd->remove();
|
||||
return 0;
|
||||
@@ -479,7 +495,7 @@ char Fl_Preferences::deleteGroup( const char *group ) {
|
||||
/**
|
||||
Delete all groups.
|
||||
*/
|
||||
char Fl_Preferences::deleteAllGroups() {
|
||||
char Fl_Preferences::delete_all_groups() {
|
||||
node->deleteAllChildren();
|
||||
return 1;
|
||||
}
|
||||
@@ -511,7 +527,7 @@ const char *Fl_Preferences::entry( int index ) {
|
||||
\param[in] key name of entry that is searched for
|
||||
\return 0 if entry was not found
|
||||
*/
|
||||
char Fl_Preferences::entryExists( const char *key ) {
|
||||
char Fl_Preferences::entry_exists( const char *key ) {
|
||||
return node->getEntry( key )>=0 ? 1 : 0 ;
|
||||
}
|
||||
|
||||
@@ -523,14 +539,14 @@ char Fl_Preferences::entryExists( const char *key ) {
|
||||
\param[in] key name of entry to delete
|
||||
\return 0 if deleting the entry failed
|
||||
*/
|
||||
char Fl_Preferences::deleteEntry( const char *key ) {
|
||||
char Fl_Preferences::delete_entry( const char *key ) {
|
||||
return node->deleteEntry( key );
|
||||
}
|
||||
|
||||
/**
|
||||
Delete all entries.
|
||||
*/
|
||||
char Fl_Preferences::deleteAllEntries() {
|
||||
char Fl_Preferences::delete_all_entries() {
|
||||
node->deleteAllEntries();
|
||||
return 1;
|
||||
}
|
||||
@@ -1012,7 +1028,7 @@ int Fl_Preferences::size( const char *key ) {
|
||||
|
||||
\see Fl_Preferences::Fl_Preferences(Root, const char*, const char*)
|
||||
*/
|
||||
char Fl_Preferences::getUserdataPath( char *path, int pathlen ) {
|
||||
char Fl_Preferences::get_userdata_path( char *path, int pathlen ) {
|
||||
if ( rootNode )
|
||||
return rootNode->getPath( path, pathlen );
|
||||
return 0;
|
||||
|
||||
@@ -459,19 +459,19 @@ char *Fl_X11_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_P
|
||||
{
|
||||
static char *filename = 0L;
|
||||
if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX);
|
||||
const char *e;
|
||||
const char *home;
|
||||
switch (root&Fl_Preferences::ROOT_MASK) {
|
||||
case Fl_Preferences::USER:
|
||||
e = getenv("HOME");
|
||||
home = getenv("HOME");
|
||||
// make sure that $HOME is set to an existing directory
|
||||
if ( (e==0L) || (e[0]==0) || (::access(e, F_OK)==-1) ) {
|
||||
if ( (home==NULL) || (home[0]==0) || (::access(home, F_OK)==-1) ) {
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
e = pw->pw_dir;
|
||||
home = pw->pw_dir;
|
||||
}
|
||||
if ( (e==0L) || (e[0]==0) || (::access(e, F_OK)==-1) ) {
|
||||
return 0L;
|
||||
if ( (home==0L) || (home[0]==0) || (::access(home, F_OK)==-1) ) {
|
||||
return NULL;
|
||||
} else {
|
||||
strlcpy(filename, e, FL_PATH_MAX);
|
||||
strlcpy(filename, home, FL_PATH_MAX);
|
||||
if (filename[strlen(filename)-1] != '/')
|
||||
strlcat(filename, "/", FL_PATH_MAX);
|
||||
strlcat(filename, ".fltk/", FL_PATH_MAX);
|
||||
@@ -483,13 +483,50 @@ char *Fl_X11_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_P
|
||||
}
|
||||
|
||||
// Make sure that the parameters are not NULL
|
||||
if ( (vendor==0L) || (vendor[0]==0) )
|
||||
if ( (vendor==NULL) || (vendor[0]==0) )
|
||||
vendor = "unknown";
|
||||
if ( (application==0L) || (application[0]==0) )
|
||||
if ( (application==NULL) || (application[0]==0) )
|
||||
application = "unknown";
|
||||
|
||||
snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename),
|
||||
"%s/%s.prefs", vendor, application);
|
||||
|
||||
// If this is the SYSTEM path, we are done
|
||||
if ((root&Fl_Preferences::ROOT_MASK)!=Fl_Preferences::USER)
|
||||
return filename;
|
||||
|
||||
// If the legacy file exists, we are also done
|
||||
if (::access(filename, F_OK)==0)
|
||||
return filename;
|
||||
|
||||
// This is USER mode, and there is no legacy file. Create an XDG conforming path.
|
||||
// Check $XDG_CONFIG_HOME, and if it isn't set, default to $HOME/.config
|
||||
const char *xdg = getenv("XDG_CONFIG_HOME");
|
||||
if (xdg==NULL) {
|
||||
xdg = "~/.config";
|
||||
}
|
||||
filename[0] = 0;
|
||||
if (strncmp(xdg, "~/", 2)==0) {
|
||||
strlcpy(filename, home, FL_PATH_MAX);
|
||||
strlcat(filename, "/", FL_PATH_MAX);
|
||||
strlcat(filename, xdg+2, FL_PATH_MAX);
|
||||
} else if (strncmp(xdg, "$HOME/", 6)==0) {
|
||||
strlcpy(filename, home, FL_PATH_MAX);
|
||||
strlcat(filename, "/", FL_PATH_MAX);
|
||||
strlcat(filename, xdg+6, FL_PATH_MAX);
|
||||
} else if (strncmp(xdg, "${HOME}/", 8)==0) {
|
||||
strlcpy(filename, home, FL_PATH_MAX);
|
||||
strlcat(filename, "/", FL_PATH_MAX);
|
||||
strlcat(filename, xdg+8, FL_PATH_MAX);
|
||||
} else {
|
||||
strlcpy(filename, xdg, FL_PATH_MAX);
|
||||
}
|
||||
strlcat(filename, "/", FL_PATH_MAX);
|
||||
strlcat(filename, vendor, FL_PATH_MAX);
|
||||
strlcat(filename, "/", FL_PATH_MAX);
|
||||
strlcat(filename, application, FL_PATH_MAX);
|
||||
strlcat(filename, ".prefs", FL_PATH_MAX);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user