How to Build a Haiku Replicant That Can Live on the Desktop or Deskbar
A safe Haiku Replicant design covering archiving, instantiation, BDragger integration, messages, resource ownership, and testing outside its host app.
A Haiku Replicant is an archivable view that can leave its original application and be instantiated inside another host, such as the Desktop. That portability changes normal GUI assumptions. The view cannot depend on the original window, application globals, borrowed resources, or an undocumented construction path. Its archive must contain enough versioned state to reconstruct a safe, independent object.
Design the portable state first
Separate model state from process-specific state. A small clock Replicant might persist its display mode and color, but not a pointer to a timer, a cached BWindow, or an open file descriptor. Store only bounded values with stable meanings. Sensitive tokens and credentials should not be embedded in the archive merely because BMessage can hold them.
Derive the view from BView, provide the normal constructor, and add an archive constructor that accepts BMessage*. Implement Archive() and the static Instantiate() factory expected by Haiku’s archiving mechanism:
status_t ClockView::Archive(BMessage* archive, bool deep) const
{
status_t status = BView::Archive(archive, deep);
if (status != B_OK)
return status;
archive->AddInt32("clock:version", 1);
archive->AddBool("clock:seconds", fShowSeconds);
return B_OK;
}
BArchivable* ClockView::Instantiate(BMessage* archive)
{
if (!validate_instantiation(archive, "ClockView"))
return nullptr;
return new(std::nothrow) ClockView(archive);
}
The archive constructor should begin with the base archive constructor, read optional fields with defaults, reject unsupported versions when their meaning is unsafe, and clamp values such as update intervals. Treat the archive as input, not as trusted memory.
Add a drag handle deliberately
BDragger provides the interaction used to drag or manage a Replicant. It can be a child of the replicable view or arranged with it according to the API’s supported relationship. Give the handle a usable target area and ensure it does not overlap essential controls. Users can show or hide Replicant handles, so the embedded content should remain understandable without relying on the handle as a permanent label.
The archived graph must include the required children when deep archiving is requested. Avoid archiving the same child twice or retaining parent-owned pointers after removal. Standard ownership rules continue to apply when the host deletes the view.
Remove assumptions about the host
Once embedded, be_app and the containing BWindow belong to the host process, not the original application. Send messages through explicitly stored and validated messengers only when the target is truly optional. A Replicant should still draw and expose a useful context menu if its companion service is absent.
Start timers or background work in AttachedToWindow() and stop it in DetachedFromWindow() or destruction. Do not keep a worker posting to a handler after detachment. Network operations need cancellation and timeouts; drawing must never block on them. Use LockLooper() only when required and release it promptly.
Test serialization as a compatibility boundary
Archive the view, flatten the archive, unflatten it into a fresh message, and instantiate it in a small test host. Then test dragging it into a supported host, restarting that host, removing it, changing system fonts, hiding handles, and running without the original app. Feed missing, extra, old-version, and oversized fields into the archive constructor.
A Replicant succeeds when its visible behavior and cleanup survive process relocation. The draggable effect is only the surface; the engineering work is a small, durable serialized protocol plus a view whose lifecycle is entirely owned by whichever host adopts it.
Related:
- How to Create BFS Attributes and Indexes for Fast Haiku Queries
- Haiku Node Monitoring: Receiving Live File-System Changes Through the Storage Kit
Sources: