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
Key insight: Return 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:

Touch Support: Touch events automatically map to mouse events (touchstart→LBUTTONDOWN, touchmove→MOUSEMOVE, touchend/cancel→LBUTTONUP). Drag operations and drawing apps work with both mouse and touch. Single-touch only (first touch point tracked). Access native TouchEvent via event.originalEvent if needed.

Best Practices

  • Return false early: As soon as you handle an event, return false to 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 _dragging won'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.message for efficiency
  • Store timer IDs: Save timer IDs returned from setTimer() to identify and kill specific timers