README.doc libprefs.so documentation file 98-04-28 libprefs@mindcontrol.org
It is a shared library that will take care of preferences for your application. You just call the library functions as documented, and your preferences will magically be saved wherever is the right place to put them. It is also a set of two regular (static) libraries that will allow your application to run without libprefs being available, but will utilize it if it is.
To install it, just open a Terminal window, "cd" to the folder where libprefs.so is found (and this file, presumably) and type "make". Alternately, you can just rename the correct version of libprefs.so according to your machine type to "libprefs.so" and move it into your shared library folder -- typically, into /boot/home/config/lib.
A shared library with a well-defined, C-callable API provides the best mixture of stability, convenience and features. A server, as has been suggested by some people, has several drawbacks, including:
Of course, it is quite possible to write an implementation of libprefs.so that uses a server, and anyone interested in putting preferences in a server is encouraged to do so! All you have to do is to implement the function prototypes exported by libprefs.so to talk to your server, and all applications who already use libprefs will be able to talk to your server!
The purpose is twofold:
You should have gotten at least three files in addition to this README:
libprefs.so, SampleApp and listprefs. Copy/move the libprefs.so file
into the system shared library folder /system/lib or your home shared
library folder /boot/home/config/lib. Run the SampleApp. Move the
window it puts up. Close the window (quitting the app). Run the app
again. If the window pops up where you moved it to, not in the initial
position, everything is working correctly.
Another cool trick is starting two copies of the application, moving
the windows to different parts of the screen, closing one window, and
watching the automagic update mechanism move the other window to where
the first window used to be. This is just a demonstration of the "live"
update feature of the preferences system and is not intended as a human
interface guideline :-) Depending on what the default polling time is
for the libprefs library, it may take a few seconds before the window
position is updated.
Now run
If you are a software developer who wants an easy way to save and load
settings from your application, this section is for you.
Since this is a simple, C-callable API, anyone is free to implement it
anyway they wish to manage preferences. I hope to see several,
compatible implementations of libprefs.so so that users have a choise.
Possible extensions to libprefs.so may include automatic preferences
edit window creation, since preferences have types that may be easily
translated to control kinds (at least for some types). However, as
applications often place their own consistency demands on settings,
this may not turn out to be viable.
If you're building a C++ application, include the file Preferences.h and
link with (on PPC) libprefs.so or (on x86) libprefs.so.LIB. (libprefs.h
is used by Preferences.h)
If you're building a C application, or prefer the C interface, include
the file libprefs.h and link with libprefs.so (or libprefs.so.LIB on x86).
If you don't want to require the libprefs shared library, you can instead
link with smallprefs.a or bigprefs.a. Smallprefs.a will look for libprefs
when you first call a preferences function, and will return an error if
it cannot find it (but at least your application runs). Bigprefs.a will
look for libprefs when you first call a preferences function, but if it
is not found, it will store your preferences using a "default" method.
IMPORTANT NOTE: if you're using smallprefs.a or bigprefs.a on x86, you
have to to be empty in your prefix file or in your make
file COPTIONS. If you don't, you will get link errors, because the linker
will think that you're trying to import libprefs as a shared library.
This allows your application to be completely oblivious to the presence
or absence of libprefs, but still let it take advantage of any newer
version of libprefs that the user may install. Needless to say, this is
the preferred method of using libprefs, but costs about 6 kB of overhead,
so it may not be desirable for small programs.
There are two classes that you should use: Preferences, and PreferenceSet.
Preferences serves as an identification card for your application; it
determines what collection of settings will be used.
PreferenceSet serves as a repository for the actual settings. You can
create several PreferenceSets with different names, and save/load them
independently. You can also specify, on a per set basis, whether the
preferences will apply to all runners of this application (common) or just
to the current user (user-specific). Once BeOS supports multiple users,
the selection of what user preferences to use will be automatic.
Since a PreferenceSet requires a Preferences object to be created, you
should first create a Preferences object. Pass it the part of your MIME
signature that comes after the slash - slashes are not allowed in the
input application name. Creating a Preferences object initializes the
preferences library and sets everything up for further use. You can test
for whether initialization succeeded by calling InitCheck().
To actually get or set preferences, you need a PreferenceSet object.
Create it with the Preferences object you created before as argument, as
well as the name you give this set of preferences (no slashes allowed) and
the scope of the settings (common or user-specific). You can see if
initialization went well by calling InitCheck(). Please note that even if
your program has not run before and there is no data for your program with
the current settings set name, initialization will go well. You will
discover wheter there is data when you try to read it.
To read previously saved data, call PreferenceSet::GetData(). This will
return an error if there is no data, or set its reference parameters to a
pointer to the actual data, and the size and type of that data. Do not
change the data pointed to by this pointer directly.
To write new or changed preferences, use PreferenceSet::SetData(). This will
write the data you pass it under the name you specify, removing any previous
setting with the same name. However, it will NOT save these changes to disk;
you will have to call PreferenceSet::Save() to explicitly commit any changes
made to a PreferenceSet object. Also, calling SetData() makes all previous
pointers returned by GetData() to go stale.
To remove a setting that is no longer applicable to your needs, use the
PreferenceSet::Delete() function.
You can also get a peek at the BMessage that libprefs.so is using internally
to store your data - any changes you make to this BMessage are "live" but
will not be written to permanent storage until you Save() the set. Use the
BMessage* conversion operator, or GetMessage() to get at the BMessage.
Make sure you delete all PreferenceSet objects before you delete the
Preferences object that they relate to, or bad things may happen when you try
to get/set preferences from a PreferenceSet that has no associated
Preferences object.
To iterate over all applications that participate in the preferences system,
you should use the C API until suitable C++ wrappers can be written.
Call PREFVersion to determine what the current version of the preferences
library is, and what version is needed of you to understand to talk to the
library.
Call PREFInit() to create a connection between your application and the
preferneces system. Pass in your MIME signature without the "application/"
part - slashes are not acceptable in the application argument string.
out_handle should be a PREFHandle that will be written to by libprefs.so.
Call PREFShutdown() to release all resources and memory used by a currently
active preferences session. Do not use any PREFData items created with that
PREFHandle after calling PREFShutdown(). Dispose all PREFData items created
with that PREFHandle before calling PREFShutdown() on that Handle.
Call PREFRemoveApp() to remove all preferences stored for an application
from disk. This is great if your app supports a "deinstall" option.
Call PREFLoadSet() to locate and load settings data for a specific named set
of preferences in your application. If that set does not exist, it will be
created.
Call PREFSaveSet() to save any changes performed to a PREFData (preferences
set). This is required to make changes "stick" - neither PREFSetData() nor
PREFDisposeSet() will do it for you.
Call PREFDisposeSet() when you're done with a set of preferences to
deallocate all resources and memory used by that set.
Call PREFRemoveSet() to remove a specific preferences set from disk; this
is useful if you save preferences organized into groups, and let the user
create and delete groups manually.
Use PREFSetData() to store data into a PREFData preferences set. Use
PREFDelete() to remove a named setting from a set, and use GetData() and
GetString() to read the stored data back. Please note that GetData() returns
a pointer to the actual stored data, and thus you should not write directly
into that buffer (nor should you try to use it after you've called
PREFDisposeSet() on the PREFData it came from).
Use PREFGetData() to read back stored settings from a PREFData set. This
will return an actual pointer to a copy of the data in memory, and thus you
should read from the returned pointer, but not modify what it points to.
libprefs.so does reference counting of PREFData items; thus, if you call
PREFLoadSet() on a set that is already loaded, that set will be returned,
and a reference count will be increased. The set will not go away until
all calls to PREFLoadSet() are balanced with a call to PREFDisposeSet()
each.
PREFBeginTransaction() blocks out other users of the preference system so
they can't read from or write to settings on disk until you call
PREFEndTransaction(). Needless to say, you should not stay in a transaction
for a long period of time. Transactions only protect you from SOMEONE ELSE.
If you want to protect the thread library from your own threading issues,
you should put a separate lock around your preferences, since it is OK to
call PREFSetData() or PREFSaveSet() from another thread while a first
thread has a PREFTransaction going. The prefs library itself, however, is
internally protected, so you can't screw up internal state to the point of
crashing even if you try to call the library from two different places at
once.
You can use PREFListen() to tell the library that you want to be informed
when it notes that changes to your preferences have been saved by someone
other than you. Upon noticing this, it will call the callback you pass into
it, from a thread within the preferences library. Call PREFListen() with a
callback function of NULL to stop listening for a specific PREFData. If you
think that a callback straight into your application is dangerous, you can
have your callback function just write a message on a port, or post a
BMessage to yourself.
Typically, you'll want to call PREFReloadSet() in response to receiving
one of these callbacks. PREFReloadSet() updates the in-memory copy of
a preferences set with what is on the disk. This can be useful to revert
settings changes, too. Calling PREFReloadSet() will make data pointers
returned by PREFGetData() go stale.
PREFGetMessage() is only available from C++ when calling the C interface.
It allows you to get direct access to the BMessage that any implementation
of libprefs.so should use to internally store your data. Any changes to
this message will be reflected when you PREFSaveSet() the set the message
came from. However, do not use the inherent capability of BMessage to
store more than one data item under the same name, as there will be no way
to get at the additional data items from the C interface or C++ wrappers.
PREFListApplications() can be used to iterate over all applications that
have stored preferences using libprefs.so. The cookie should be 0 for the
first call to PREFListApplication(), and should not be touched thereafter
(it is used internally by the library to keep track of what the "next"
item is). Call PREFDisposeApplicationCookie() on the "cookie" value once
you're done iterating to release any memory internally allocated by
libprefs.so.
PREFListSets() and PREFDisposeSetCookie() is used to iterate over specific
sets an application has stored, specific to the current user or common, in
much the same way that PREFListApplications() and
PREFDisposeApplicationCookie() are used. Don't forget to dispose the
cookie!
PREFListData() and PREFDisposeDataCookie() help you find out what actual
data items are stored within a preferences set. Don't forget to dispose the
cookie when you're done iterating! Please note that this cookie, like the
other cookies, may or may not change between calls to PREFListData(), even
if the library updates its notion of what information to return next.
Developer information
How can libprefs.so be extended in the future?
How do I use it from my application?
#define P_IMPEXP
Description of the C++ API
Description of the C API