README.doc
libprefs.so documentation file
98-04-28
libprefs@mindcontrol.org

What is libprefs.so?

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.

Why a shared library?

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:

  • it is harder to install and maintain - more things can go wrong.
  • it needs to be started in the UserBootscript, which means that the user has to edit shell scripts to use it.
  • f the supporting library that talks to the server starts the server if it's not already running, the server will be started with the access privileges of the current user, which may not be what you want in a multi-user server.
  • a server that uses BMessages for messaging requires a C++ application on the other side. Thus, a server-based approach will lock out anyone who wants a pure C program - or someone who does not want to use a server.
  • 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!

    What is the purpose of libprefs.so?

    The purpose is twofold:

  • first, this library has been somewhat tested, and seems to perform as documented. Thus, you can use it in your application to reduce the hassle of getting and setting preferences.
  • second, this library is intended to focus the debate over preferences management currently raging in the Be developer community. Since the source to this library is freely available, it can be used as a basis for future development. I have no problem with releasing necessary parts of the library into the public domain, should that become necessary. e-mail me for details.
  • it seems that there is enough adoption of the libprefs.so API among available applications that it's pretty much become the accepted de facto standard API. I have to thank everybody who contributed feedback for helping me make sure the API works for real needs.
  • How do I install it?

    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 listprefs from the command line. It should list at least the sample application with one preferences set, since that got created when the sample app was run. You may also have gotten make files and source files to build the preferences library and to build programs using the library. It should become apparent from the following discussion how to use these if you are a program developer. If you just got libprefs because you wanted to use a program that requires it, you can stop here.

    Developer information

    If you are a software developer who wants an easy way to save and load settings from your application, this section is for you.

    How can libprefs.so be extended in the future?

    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.

    How do I use it from my application?

    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

    #define P_IMPEXP

    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.

    Description of the C++ API

    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.

    Description of the C API

    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.