Haiku BInputDevice: Enumerating and Controlling Input Hardware
Use Haiku BInputDevice to discover hardware, watch device-state changes, and call supported controls without confusing device management with events.
Haiku’s BInputDevice API lets an application enumerate input devices known to the Input Server, inspect their name and type, check whether one is running, and request start, stop, or a device-specific control. It is an administrative and discovery API. It is not the event stream that delivers mouse movement and keyboard presses to windows, and it is not the plug-in API for writing an Input Server device add-on.
Keeping those responsibilities separate avoids a common design mistake. A window should receive user input through the normal Interface Kit message path. An application that needs to show device presence or expose supported configuration can use BInputDevice and device-change notifications. It should not repeatedly start and stop system devices as a way to intercept their events.
Enumerate objects and own their lifetime
get_input_devices(BList*) asks the Input Server for devices it knows about and populates the caller’s list with heap-allocated BInputDevice objects in the current implementation. The caller is responsible for deleting those objects after use; BList::MakeEmpty() alone removes pointers but does not delete the pointed-to devices. find_input_device() similarly returns a newly allocated object or NULL, so retain and delete it deliberately.
Check the status_t from enumeration before presenting results. The list can be empty because no matching devices are available, but a failed request to the Input Server is a different condition. An application should show an explicit unavailable state instead of treating a server failure as proof that the machine has no keyboard or pointing device.
The device’s Name() is a runtime identifier used by the API, while Type() describes a broad class such as pointing device, keyboard, or undefined. Do not infer exact hardware capabilities from the type alone. A tablet, mouse, touchpad, or specialized pointing device can share a broad type while supporting different controls. Query or configure only device-specific control codes that are documented for that implementation.
Watching device topology changes
watch_input_devices(BMessenger target, bool start) registers or unregisters a target for B_INPUT_DEVICES_CHANGED messages. The API guide documents fields be:opcode, be:device_name, and be:device_type. Opcodes distinguish added, started, stopped, and removed devices. Validate each field and message type; a notification is a cue to refresh state, not a complete snapshot of all devices.
Use a reconciliation loop: receive the notification, enqueue a short refresh on the app’s model thread, call get_input_devices(), replace the displayed snapshot, and release all allocated device objects. This handles bursts and event reordering more safely than incrementally assuming every message arrives exactly once and in a perfectly stable order. Coalesce several pending notifications into one refresh.
Stop watching when the target is going away. A BMessenger makes delivery across loopers possible, but it does not keep the receiving window alive forever. Handle a failed stop request, and treat late messages as ordinary stale messages. Register and unregister with a clear lifecycle, such as application startup and shutdown or a model service’s creation and destruction.
The Input Server watcher reports changes in devices it is aware of. It does not necessarily describe a physical plug event before the server has processed it, nor does it guarantee that a device will remain present until the next control call. After a notification, re-enumerate and check each operation’s status.
Start, stop, and control have system impact
BInputDevice::Start() and Stop() request state changes for a named device. Static overloads can act on a device type. These operations affect input availability and should be exposed only when there is a clear user need. A background tool should never stop a keyboard or pointing device merely to probe behavior. Confirm current state, request the narrowest operation, and provide an immediate recovery path.
Control(code, message) is device-specific. The public header does not define a universal set of control codes or message fields. The control code and message must match the supported hardware or add-on. Preserve the status result and validate returned fields before displaying or applying them. Do not pass arbitrary UI messages as device controls or assume a successful control call has a particular effect on all devices.
The current implementation sends synchronous requests to the Input Server with finite send and reply timeouts. That means a call can still take time and can fail when the service is unavailable. Do not call long-running controls from the window message loop. Dispatch them through a bounded worker, then send results back through a messenger. Prevent two concurrent control requests from racing over the same hardware state.
Control() mutates the supplied message
The implementation places the supplied message in a request, empties it, sends the request, then copies a returned message field back on success. Callers should not expect their input message to remain unchanged. If the request fields are needed after the call, keep a separate copy. Check for a returned message and each expected field rather than reading uninitialized outputs.
Treat the returned control data as device-provided input. Bound strings and arrays, check value ranges, and do not assume the device uses the same firmware or capability set as a development machine. If a control reports capabilities, use them to populate the UI rather than enabling options that the device may reject.
Device changes are not raw input events
Input Server device notifications are about device state. Keyboard and pointer actions arrive through application/window event messages and the system’s event dispatch path. BInputDevice does not provide a callback that streams every key or pointer event. For an application preference panel, show device health or configuration; for a game, handle events through the appropriate window or Game Kit APIs; for a new device implementation, use the Input Server add-on contract.
This separation also matters for privacy and accessibility. An app should not poll hardware or request global device control to infer what a user is typing. Use only documented, user-visible controls required by the feature. System-wide input interception belongs to specialized OS facilities and requires a broader trust and UX review.
Example: safe refresh after a notification
The model-side refresh can be kept separate from event processing:
BList devices;
status_t status = get_input_devices(&devices);
if (status != B_OK) {
ShowInputServiceUnavailable(status);
return;
}
for (int32 i = 0; i < devices.CountItems(); ++i) {
BInputDevice* device = static_cast<BInputDevice*>(devices.ItemAt(i));
if (device != NULL)
UpdateDeviceRow(device->Name(), device->Type(), device->IsRunning());
}
for (int32 i = 0; i < devices.CountItems(); ++i)
delete static_cast<BInputDevice*>(devices.ItemAt(i));
devices.MakeEmpty();
The example assumes the refresh and UI update run on a thread safe for the application’s model. It deletes each object returned by the current implementation. If using a wrapper or a different Haiku revision, confirm ownership behavior against its API/source before copying the cleanup pattern.
Failure modes and recovery
Test Input Server stopped or unavailable, no devices, device removal between enumeration and IsRunning(), device rename, control unsupported, start/stop rejection, and target destruction while watching. Check status codes and preserve the previous good UI state if refresh fails. Do not clear the list and then show “no devices” when the refresh actually failed.
If an application starts or stops a device, make that state visible and reversible. Confirm the intended device name and type, avoid acting on a stale pointer after refresh, and re-enumerate after a successful request. If the operation can leave the user without primary input, require explicit confirmation and do not run it at startup.
Use a fake or test input device where possible. Verify B_INPUT_DEVICES_CHANGED field parsing, burst coalescing, list cleanup, and thread marshalling. Record the Haiku revision and device/add-on name in reports, but avoid storing sensitive user activity. A clean diagnostic states whether enumeration, notification, or control failed rather than saying only “input is broken.”
BInputDevice is most useful for narrow device management and settings UI. Its value comes from a snapshot-and-reconcile pattern, explicit object ownership, and device-specific controls. Keeping it separate from raw event handling prevents applications from confusing hardware management with ordinary input delivery.
Related:
- Haiku’s Input Server: Device Add-ons, Event Filters, and Input Methods
- How to Configure and Test a Non-US Keymap on Haiku
Sources: