Skip to content
Haiku OSDeep Dive Published Updated 7 min readViews unavailable

Haiku BView Mouse Tracking: Event Masks, Focus, and Motion History

Capture a Haiku BView drag across view boundaries with SetMouseEventMask, understand motion history and focus options, and test the limits.

A pointer-down handler sees where a press began. That is not enough for a canvas, range selector, or drag handle that must keep updating after the pointer leaves its view. In Haiku, the intended tool for that short interaction is BView::SetMouseEventMask(): it expands one view’s pointer-event scope while a mouse button is held, then restores the ordinary event mask after the following mouse-up.

This is temporary event routing, not a global pointer lock and not a polling loop. Knowing exactly when the mask starts and ends makes drag code simpler and prevents a common mistake: using GetMouse() as a substitute for receiving MouseMoved() messages.

A mouse mask belongs to one press

Call SetMouseEventMask() from the view’s MouseDown() implementation, while the view is attached to a window. The requested event bits are added to the view’s current mask until and including the next MouseUp(). The earlier persistent mask configured with SetEventMask() is then restored. Haiku’s implementation returns an error when the view is unattached or the call is made outside a B_MOUSE_DOWN message, so check the status rather than silently assuming tracking began.

A compact interaction can keep a model point updated while the button is down:

void DragView::MouseDown(BPoint where)
{
    if (SetMouseEventMask(B_POINTER_EVENTS,
            B_NO_POINTER_HISTORY) != B_OK) {
        return;
    }

    fTracking = true;
    fLastPoint = where;
}

void DragView::MouseMoved(BPoint where, uint32 transit,
    const BMessage* dragMessage)
{
    if (!fTracking || dragMessage != NULL)
        return;

    fLastPoint = where;
    Invalidate();
}

void DragView::MouseUp(BPoint where)
{
    if (!fTracking)
        return;

    fLastPoint = where;
    fTracking = false;
    Invalidate();
}

This is a method excerpt, not a complete class: it assumes fTracking and fLastPoint are members and that Draw() renders the model. A real drawing tool should append each accepted point to its document model before invalidating; a selector can instead update a preview and commit it in MouseUp(). The important contract is that MouseMoved() continues to receive pointer events for the duration of the press, including movement outside the view’s bounds.

If SetMouseEventMask() fails, this example leaves the interaction unarmed. That is preferable to leaving fTracking true while the view receives no later motion or release event. Also do not call the method from a timer, a worker thread, or MouseMoved(); the API’s scope is specifically the MouseDown() dispatch.

SetEventMask() is the persistent alternative

SetEventMask() has a broader lifetime. It can extend the view’s mouse-event scope beyond the view or request keyboard events when the view is not the focus view. Use it when that behavior is a lasting part of the view’s design, not as a one-shot drag capture. A persistent pointer mask can leave an invisible or background view interested in events long after the user expects the interaction to end.

The two methods also have different option contracts. The Haiku Book says SetEventMask() accepts the no-pointer-history option; SetMouseEventMask() also accepts the focus-related options. Passing an event mask of zero lets a caller change options without adding event types. EventMask() reports the persistent mask, not the temporary mouse mask, so it is not a reliable way to inspect the active drag scope.

Decide what motion history the interaction needs

With B_NO_POINTER_HISTORY, the Application Server can discard older queued pointer-moved events and deliver only the most recent position. That is often right for a scrubber, hover preview, or drag handle: if rendering cannot keep up, showing the latest position is more useful than replaying stale intermediate positions.

It is the wrong choice when every sampled point matters, such as a freehand stroke or a gesture recognizer that derives velocity from path samples. Current Haiku headers also define B_FULL_POINTER_HISTORY as a Haiku-specific event option; when the target SDK exposes it, pass it as the options argument to SetMouseEventMask() from MouseDown() to request full queued pointer history for that tracking interval. Current View.cpp uses the combined persistent and temporary options when handling moved messages and GetMouse() queue filtering. This option is absent from older BeOS-era documentation, so verify it against the SDK you compile with. Even then, it does not promise an exact number of callbacks or preserve every physical sensor sample.

The transit argument in MouseMoved() is not another event-mask bit. It reports B_ENTERED_VIEW, B_INSIDE_VIEW, B_EXITED_VIEW, or B_OUTSIDE_VIEW, allowing hover state to be cleared when the pointer exits. A view receiving a normal in-bounds hover stream and a view temporarily receiving events outside its bounds should handle those transitions deliberately. For example, clear a hover highlight on exit, but do not cancel an active drag merely because the pointer is outside.

Focus options solve different problems

B_SUSPEND_VIEW_FOCUS suppresses events normally sent to the focus view during the held-button interaction; in practical terms, keyboard input to that focus view is suspended while the mouse remains down. This is a strong choice for modal pointer gestures, not a default to add to every drag.

B_LOCK_WINDOW_FOCUS prevents the view’s window from losing focused status while the button is down, even if the pointer leaves the window. It does not mean that the view permanently owns keyboard focus. Keep the distinction clear: one option suppresses the focus view’s messages; the other holds the window’s active/focused status during the press.

Before enabling either option, test what happens when a user presses a key during the drag, moves into another window, releases outside the original view, or switches workspaces. Focus changes can affect unrelated controls and accessibility workflows. Use the least restrictive mask that satisfies the interaction.

Prefer event delivery over cursor polling

GetMouse() reports pointer position and button state, but Haiku’s API reference explicitly says not to use it to track the mouse; implement MouseMoved() instead. Its optional queue-checking behavior can consume pending mouse-moved or mouse-up messages, which makes it especially unsuitable as a second event loop hidden inside drawing or input code. Likewise, MouseUp() reports the release point, not a guarantee that every intermediate point was delivered.

Keep model updates in the view’s message path. If another thread needs to observe drag state, publish a synchronized snapshot or message it; do not assume that calling a view method from a worker thread is safe. If redrawing on every event is expensive, coalesce work in the model or use the latest-position policy rather than blocking the window looper.

Verify the boundary conditions

Instrument a small test view to log the MouseDown, MouseMoved, and MouseUp coordinates, transit code, and whether a drag is active. Try these cases:

  • Press and release inside the view without moving; the click should not leave a drag armed.
  • Press inside, move outside the view, and release; confirm the same view sees the release and clears its state.
  • Move outside the window; compare behavior with and without B_LOCK_WINDOW_FOCUS.
  • Press a key during the drag; compare with and without B_SUSPEND_VIEW_FOCUS.
  • On SDKs that define B_FULL_POINTER_HISTORY, compare GetMouse() queue behavior and actual MouseMoved() callbacks with and without that option passed to SetMouseEventMask(); do not assume the flag exists on older BeOS-compatible SDKs.
  • Call the mask from an unattached view or from the wrong hook in a debug build; check that failure is handled.

If hover works but drag motion stops at the view edge, the scope was probably never expanded from MouseDown(). If the release is missed, check the return status, attachment, and interaction state before investigating drawing. If a hand-drawn line has gaps, inspect event-history filtering and looper load before changing coordinate math.

These contracts describe Haiku’s Interface Kit, not a portable C++ mouse-capture API. In particular, the B_FULL_POINTER_HISTORY flag is a Haiku addition to the BeOS-era interface. Check the target Haiku headers and documentation when building for a particular release, and avoid mixing old BeBook assumptions with current Haiku behavior.

Related:

Sources:

Comments