msgProc: Overview
msgProc is WakaPAC's central event processing pipeline. It intercepts all events before standard bindings execute, giving you low-level control for keyboard shortcuts, drag operations, mouse capture, timers, and complex event coordination.
Why Use msgProc?
While data-pac-bind handles straightforward event-to-handler wiring, msgProc is WakaPAC's central event processing pipeline — the equivalent of a Win32 window procedure. It intercepts all events before standard bindings execute, making it the natural home for:
- Intercept events globally: Handle Ctrl+S anywhere in your component
- Prevent default behavior: Block form submission for validation
- Track complex interactions: Implement drag-and-drop with mouse coordinates
- Capture mouse input: Continue tracking drag operations when cursor leaves container
- Coordinate multiple events: Handle mousedown + mousemove + mouseup sequences
- Access low-level event data: Get exact mouse coordinates or modifier keys
- Recognize mouse gestures: Implement browser-style gestures (drag right = forward, drag left = back)
Think of msgProc as a global event filter - it sees every event first and can either handle it or pass it through to standard bindings.
How msgProc Works
Event Flow
1. DOM event occurs (click, keypress, etc.)
↓
2. msgProc runs (if defined)
├─ Returns false? → Event handled, stop here
└─ Returns true/undefined? → Continue processing
↓
3. Standard data-pac-bind handlers execute
false from msgProc to stop event processing. Return true (or nothing) to let standard bindings run.
When to Use msgProc vs Standard Bindings
Standard bindings are declarative: you attach a handler to a specific element and event. msgProc is procedural: a central handler that sees every event and can maintain state across them. The choice follows from that distinction:
| Use Standard Bindings When | Use msgProc When |
|---|---|
| The response is local — one element, one event, one action | The response is global — keyboard shortcuts, context menus, gestures |
| No coordination is needed between events | Multiple events form a sequence (mousedown → mousemove → mouseup) |
| You're reacting to an event | You need to intercept, validate, or suppress an event |
| The interaction is stateless | You need to track state across events (dragging, drawing, resizing) |
| Timing doesn't matter | You need recurring updates (clocks, polling, game loops) |
Event Detail Structure
Every msgProc event receives a CustomEvent with Win32-style message properties as top-level properties:
{
type: 'pac:event', // Always 'pac:event' for msgProc
pacId: 'xyz' // pacId of the container
message: 0x0100, // Message type constant
wParam: 0x0001, // Modifier keys and button states
lParam: 0x00640032, // Coordinates or other data
target: HTMLElement, // DOM element that triggered the event
originalEvent: Event, // Original browser event object
timestamp: 1640995200000, // When event occurred (milliseconds)
detail: {} // Event-specific additional data
};
Key Properties
| Property | Type | Description |
|---|---|---|
type |
string | Always 'pac:event' for msgProc events |
pacId |
string | The data-pac-id attribute value of the container the message is being dispatched to |
message |
number | Message type constant (e.g. MSG_KEYDOWN, MSG_MOUSEMOVE). See reference page for the full list |
wParam |
number | Context-specific data — modifier keys and button states for mouse events, virtual key code for keyboard events. See individual event pages for details |
lParam |
number | Context-specific data — packed mouse coordinates for mouse events, modifier key flags for keyboard events. See individual event pages for details |
target |
HTMLElement | The DOM element that triggered the event |
originalEvent |
Event | The original browser event object — use this when you need access to the full browser Event API |
timestamp |
number | When the event occurred, in milliseconds (same epoch as Date.now()) |
detail |
object | Event-specific additional data. Contents vary by message type (e.g., text for MSG_INPUT, entries for MSG_SUBMIT). See individual message type documentation for specific properties |
Basic Message Handling Pattern
Use a switch statement on event.message for clean, efficient event handling:
msgProc(event) {
switch(event.message) {
case wakaPAC.MSG_KEYDOWN:
if (event.wParam === wakaPAC.VK_S && (event.lParam & wakaPAC.KM_CONTROL)) {
this.save();
return false;
}
break;
case wakaPAC.MSG_LBUTTONDOWN:
const pos = wakaPAC.MAKEPOINTS(event.lParam);
this.startDrag(pos.x, pos.y);
return false;
case wakaPAC.MSG_TIMER:
if (event.wParam === this.clockTimer) {
this.updateClock();
}
break;
}
}
Configuration
Mouse Tracking Throttling
Mouse movement events are throttled to prevent performance issues. The default is 60 FPS (~16ms between events):
wakaPAC.mouseMoveThrottleFps = 120; // Higher precision
wakaPAC.mouseMoveThrottleFps = 30; // Battery saving
wakaPAC.mouseMoveThrottleFps = 0; // No throttling
wakaPAC('#app', { /* ... */ });
Touch Event Support
Touch events are automatically mapped to mouse events. Your existing msgProc code works identically for both mouse and touch input:
event.originalEvent if needed.
Best Practices
- Return false early: As soon as you handle an event, return
falseto stop processing - Keep it fast: msgProc runs for EVERY event - avoid expensive operations
- Prefer standard bindings: Only use msgProc when you need low-level control or interception
- Use underscore prefix: Private state like
_draggingwon't trigger DOM updates - Use VK constants: Access VK codes via
wakaPAC.VK_*for clarity and maintainability - Check event types first: Use switch statements on
event.messagefor efficiency - Store timer IDs: Save timer IDs returned from
setTimer()to identify and kill specific timers