msgProc: Mouse Capture
Mouse capture routes all mouse events to your container regardless of cursor position. Essential for drag operations - without it, mousemove events stop firing the moment the cursor leaves your container boundary, breaking drag-and-drop, resizing, sliders, and drawing tools.
The Problem Without Capture
Normal event routing breaks these common interactions:
- Drag operations: Element stops following cursor when dragged outside container
- Resizing: Resize handles lose tracking when cursor moves fast
- Sliders: Slider thumb stops responding mid-drag
- Drawing tools: Lines end abruptly when cursor exits canvas
- Selection boxes: Selection stops growing when cursor leaves bounds
The Solution
Capture ensures mouse events route to your container regardless of cursor position. Once capture is active, your container receives all mouse events until capture is released. This works exactly like Win32's SetCapture/ReleaseCapture API.
How Capture Works
When capture is active:
- All mouse events (move, down, up, clicks, wheel) route to the captured container
- Hit-testing is bypassed - cursor position doesn't matter
- Click routing is preserved - clicks go to the container that received the mousedown
- Other events are unaffected - keyboard and focus events route normally
- MSG_MOUSEENTER/LEAVE are suppressed - the captured container logically retains the pointer
Event Routing During Capture
Normal (no capture):
User moves mouse over Container A → Container A receives MSG_MOUSEMOVE
User moves mouse over Container B → Container B receives MSG_MOUSEMOVE
With capture on Container A:
User moves mouse over Container A → Container A receives MSG_MOUSEMOVE
User moves mouse over Container B → Container A receives MSG_MOUSEMOVE
User moves mouse anywhere → Container A receives MSG_MOUSEMOVE
Capture API
setCapture(pacId)
wakaPAC.setCapture(this.pacId);
Captures the mouse for the specified container. All subsequent mouse events route to this container until capture is released. Only one container can have capture at a time - calling setCapture() again transfers capture to the new container.
| Parameter | Type | Description |
|---|---|---|
pacId |
string | The container ID to receive captured events |
releaseCapture()
wakaPAC.releaseCapture();
Releases mouse capture. Events return to normal routing (based on cursor position). Safe to call even if no capture is active.
hasCapture()
if (wakaPAC.hasCapture()) {
// Capture is active
}
Checks if mouse capture is currently active.
| Returns | Type | Description |
|---|---|---|
| result | boolean | True if any container has capture, false otherwise |
Drag-and-Drop Example
msgProc(event) {
if (event.message === wakaPAC.MSG_LBUTTONDOWN) {
const pos = wakaPAC.MAKEPOINTS(event.lParam);
wakaPAC.setCapture(this.pacId);
this._dragging = true;
this._dragStartX = pos.x;
this._dragStartY = pos.y;
this._startX = this.x;
this._startY = this.y;
return false;
}
if (event.message === wakaPAC.MSG_MOUSEMOVE && this._dragging) {
const pos = wakaPAC.MAKEPOINTS(event.lParam);
const deltaX = pos.x - this._dragStartX;
const deltaY = pos.y - this._dragStartY;
this.x = this._startX + deltaX;
this.y = this._startY + deltaY;
return false;
}
if (event.message === wakaPAC.MSG_LBUTTONUP && this._dragging) {
wakaPAC.releaseCapture();
this._dragging = false;
return false;
}
}
Capture Changed (MSG_CAPTURECHANGED)
MSG_CAPTURECHANGED fires when your container loses capture (either because you called releaseCapture() or another container stole capture). Use this for cleanup when capture is lost unexpectedly. This event fires after capture has already been released.
Message Parameters
| Parameter | Type | Description |
|---|---|---|
wParam |
number | Reserved (always 0) |
lParam |
number | Reserved (always 0) |
setCapture() or releaseCapture() inside the handler - just clean up state. Put your normal completion logic in MSG_LBUTTONUP.
Example: Handling Unexpected Capture Loss
msgProc(event) {
if (event.message === wakaPAC.MSG_LBUTTONDOWN) {
wakaPAC.setCapture(this.pacId);
this._dragging = true;
return false;
}
if (event.message === wakaPAC.MSG_LBUTTONUP) {
wakaPAC.releaseCapture();
return false;
}
// Cleanup if capture is lost unexpectedly
if (event.message === wakaPAC.MSG_CAPTURECHANGED) {
this._dragging = false;
this.isDragging = false;
this.resetDragState();
return false;
}
}
Auto-Release Behavior
Capture automatically releases if the captured container is removed from the DOM. This prevents crashes, but you should still manage capture lifecycle explicitly.
Auto-release occurs when:
- Container element is removed from DOM
- Container component is destroyed
- Another container calls
setCapture()(capture transfers)
You must manually release on:
- Mouseup (end of drag operation)
- Escape key (user cancellation)
- Error conditions (validation fails, drag aborts)
- Mode or state changes within your component
Best Practices
- Track state privately: Use
_dragging,_resizing,_slidingwith underscore prefix (won't trigger reactivity) - Store start positions: Save both mouse position and object position at capture start for accurate deltas
- Handle edge cases: Use MSG_CAPTURECHANGED for cleanup if capture is lost unexpectedly
- Consider Escape key: Let users cancel drag operations with Escape
- Always release on mouseup: Don't forget to call
releaseCapture()when the operation completes - Check capture state: Use
wakaPAC.hasCapture()if you need to verify capture is active
SetCapture() and ReleaseCapture() functions, providing a familiar API for developers with desktop programming experience.