Haiku BDeskbar: Adding and Removing Desktop Replicants
Integrate an archivable Haiku view into Deskbar with BDeskbar, verify item identity, handle service failures, and respect cross-process lifetime.
BDeskbar is the Interface Kit API for querying Deskbar state and adding or removing items. For an application that wants to place a status view in the desktop panel, it provides a bridge to the Deskbar process. The call crosses a process boundary: adding a BView archives it and sends that archive to Deskbar, while adding an add-on reference asks Deskbar to instantiate the add-on there.
That distinction changes the ownership model. The BView* passed to AddItem() is not a live view that Deskbar can keep drawing after the caller closes its window. Deskbar receives an archived representation and reconstructs a replicant in its own context. The view must therefore be archivable, versioned, self-contained, and prepared to live under a host window it does not control.
Verify the service before integrating
BDeskbar::IsRunning() reports whether Deskbar is available. A third-party application should not assume a panel is present or launch-critical. If Deskbar is not running, keep the feature optional, provide the same essential state in the main application, and let the user retry later. Do not repeatedly call a synchronous API in a timer hoping the service will reappear.
The API also exposes panel geometry, location, expansion, auto-hide, always-on-top, and auto-raise settings. These are global desktop preferences, not application-local state. A utility should not change them as a side effect of adding an item. If an explicit Deskbar settings UI uses those methods, read the current value, explain the global effect, check status, and avoid overwriting a choice another process changed concurrently.
AddItem() and GetItemInfo() use status returns that distinguish a successful message exchange from a failed request. Preserve the exact status. Deskbar can exit between IsRunning() and the next call, so the check is not a lease. Treat every operation as fallible and make repeated user attempts safe.
Add an archivable view or an add-on
The view overload calls Archive() on the provided view, creates a request containing the archive, and sends it to Deskbar. This means the added item is a reconstructed copy, not the same C++ object. The archived view must implement the normal archiving contract, keep only durable configuration, and avoid serialized pointers, thread IDs, open file descriptors, or references to the original window.
The add-on overload takes an entry_ref; Deskbar loads the referenced add-on in its own environment. Use it when the feature is designed as a separately loadable replicant add-on. Keep the add-on installed in a stable location and report an unavailable path clearly. The interface does not let an app safely assume the add-on will remain present forever.
Choose one integration mechanism. Do not both add an archived view and load the add-on unless the design explicitly supports duplicate instances. Give the item a stable name and BArchivable identity, and make the archive constructor tolerate old, missing, extra, or invalid fields. Instantiate() should validate the archive type before constructing the view.
The returned item ID is useful for a later removal request during the current installation. Treat it as a Deskbar-managed identifier, not a permanent application database key. If the ID is not persisted or becomes stale, HasItem() and GetItemInfo() can help reconcile by name, but names may not be unique across multiple app versions or instances. Define a stable namespace for your item names and check for an existing instance before adding another.
Understand item query ownership
GetItemInfo(id, const char** name) returns a duplicated C string through the output pointer in the current implementation. The caller must free that allocation with the matching C allocator after copying or using it. A leak can accumulate if a settings panel refreshes the item list repeatedly. Check the status before dereferencing the pointer.
GetItemInfo(name, &id), HasItem(), CountItems(), MaxItemWidth(), and MaxItemHeight() support reconciliation and layout decisions. A successful HasItem() is a snapshot; the item can be removed moments later. Do not keep a UI row permanently enabled based on one query. Refresh after a failed remove or after a relevant Deskbar lifecycle event if the application monitors it.
Removal is not confirmed deletion
RemoveItem(id) and RemoveItem(name) send a removal message to Deskbar. The current Deskbar.cpp implementation contains a TODO noting Deskbar does not reply to the removal message, so the returned status confirms message delivery, not that the item was actually removed. Do not report “deleted” based only on B_OK. Re-query using HasItem() or GetItemInfo() after a short, bounded reconciliation step if the UX needs confirmation.
Make removal recoverable. A user should understand whether the action removes only the Deskbar item or uninstalls the add-on; these are separate operations. Keep settings and main application data intact. If the app is uninstalled while a replicant remains, make the stale replicant display a clean unavailable state and provide a removal action.
Deskbar is the host, not the app
When hosted by Deskbar, a replicant may run in a different process and window than the original application. Its be_app, looper, and window belong to that host. Persist only the state required to reconstruct a useful view. Use a BMessenger to talk to an optional application service, validate delivery failures, and make the item useful when the service is not running.
Do not call BDeskbar synchronous APIs from code executing inside a Deskbar add-on. The current upstream implementation warns that methods that need a reply can deadlock when called from Deskbar’s own add-on context. If a replicant needs to request its own removal or query Deskbar, post a request to another process or use a safe deferred mechanism instead of making a reentrant synchronous call.
Update the view from model state and avoid blocking draws on IPC, disk, or network. Handle AttachedToWindow() and DetachedFromWindow() to start and stop timers or workers. Repeated archive, instantiate, detach, and reattach cycles must not leak watchers or retain stale messenger targets.
Example: add once and retain the returned ID
An application can guard against duplicate instances and remember the returned identifier for its settings session:
BDeskbar deskbar;
if (!deskbar.IsRunning())
return B_NO_INIT;
if (deskbar.HasItem("application/x-vnd.example-status"))
return B_OK;
int32 itemID = -1;
status_t status = deskbar.AddItem(archivableView, &itemID);
if (status != B_OK)
return status;
SaveDeskbarItemID(itemID);
return B_OK;
The sample assumes the view has the correct archive implementation and a stable item name recognized by the hosting system. The name-based query is only an example of reconciliation; verify how the replicant name is exposed by your exact archive/add-on. Keep the returned ID scoped to the Deskbar item lifecycle and handle a stale ID during removal.
Acceptance tests
Test Deskbar stopped, start after the check, add an invalid archive, add a valid view, add a missing add-on, duplicate names, item removed externally, Deskbar restart, app restart, and user removal of the original application. Confirm the item reconstructs without the original app, reports unavailable services clearly, and has no surviving worker after detach.
Measure add/remove request duration and keep these operations off the window’s hot input path if a service timeout would freeze the UI. Confirm GetItemInfo() output ownership is released, and do not equate a successful RemoveItem() send with confirmed removal. Check that the app leaves global Deskbar settings unchanged unless the user explicitly requested a setting change.
BDeskbar makes desktop integration convenient, but the boundary is an archived object and message exchange, not shared object ownership. Designing the item as an independent replicant and validating every service result keeps the application reliable across restarts and host lifetimes.
Related:
- How to Build a Haiku Replicant That Can Live on the Desktop or Deskbar
- How to Use the Deskbar and Workspaces Effectively on Haiku
Sources: