Messages & Events

Sending Messages

// Synchronous: calls win->proc immediately; returns proc's return value
int  send_message(window_t *win, uint32_t msg, uint32_t wparam, void *lparam);

// Asynchronous: queued, delivered on the next repost_messages(-1) call
void post_message(window_t *win, uint32_t msg, uint32_t wparam, void *lparam);

Standard Window Messages

Constant When sent wparam / lparam
evCreate Window just created lparam = value from create_window
evDestroy Window about to be freed
evPaint Repaint requested
evNCPaint Non-client area repaint
evSetFocus Window gains focus
evKillFocus Window loses focus
evLeftButtonDown LMB pressed LOWORD=x, HIWORD=y (window-local)
evLeftButtonUp LMB released same
evRightButtonDown RMB pressed same
evMouseMove Mouse moved same
evMouseLeave Mouse left window
evKeyDown Key pressed SDL scancode
evKeyUp Key released SDL scancode
evTextInput Text character input lparam = const char * UTF-8
evWheel Mouse wheel wparam = MAKEDWORD(x,y) mouse pos; lparam = MAKEDWORD(dx,dy) scroll deltas
evCommand Control notification LOWORD=id, HIWORD=notification code
evResize Window resized / moved
evStatusBar Update status bar text lparam = (void *)const char *
evHScroll Built-in H scrollbar moved wparam = new scroll position
evVScroll Built-in V scrollbar moved wparam = new scroll position
evHitTest Find child at point lparam = window_t **
evRefreshStencil Stencil buffer needs update
evUser (1000) First app-defined message

Control Notification Codes

Sent to the root window via evCommand:

Code Control Meaning
btnClicked Button / Checkbox Button was clicked
edUpdate Text edit Text content changed
cbSelectionChange Combobox Selected item changed
RVN_SELCHANGE ColumnView Single-click selection change
RVN_DBLCLK ColumnView Double-click on item
kMenuBarNotificationItemClick MenuBar Menu item selected

Decoding in the parent window procedure:

case evCommand: {
    uint16_t notif = HIWORD(wparam);  // notification code
    uint16_t id    = LOWORD(wparam);  // item ID
    window_t *ctrl = (window_t *)lparam;

    if (notif == btnClicked) {
        if (strcmp(ctrl->title, "OK") == 0) { /* … */ }
    }
    if (notif == kMenuBarNotificationItemClick) {
        switch (id) {
            case MY_MENU_OPEN:  open_file(); break;
            case MY_MENU_QUIT:  running = false; break;
        }
    }
    return true;
}

Toolbar Button Clicks

// Set up toolbar buttons once in evCreate
toolbar_button_t buttons[] = {
    { .icon = icon16_folder, .ident = ID_OPEN, .flags = 0 },
    { .icon = icon16_save,   .ident = ID_SAVE, .flags = 0 },
};
send_message(win, tbAddButtons,
             sizeof(buttons)/sizeof(buttons[0]), buttons);

// In window proc – receive toolbar click
case tbButtonClick:
    switch (wparam) {  // ident
        case ID_OPEN: open_file(); break;
        case ID_SAVE: save_file(); break;
    }
    return true;

Mouse Wheel Handling

The evWheel message is sent when the user scrolls the mouse wheel. The message uses a WinAPI-style convention:

  • wparam: Mouse position as MAKEDWORD(x, y) in window-local scaled coordinates
  • lparam: Scroll deltas as MAKEDWORD(dx, dy) (cast from void*)
    • dx: Horizontal scroll amount (positive = scroll right, negative = scroll left)
    • dy: Vertical scroll amount (positive = wheel up, negative = wheel down)
    • Values are already multiplied by SCROLL_SENSITIVITY (3)

Extracting scroll deltas from lparam:

case evWheel: {
    int dx = (int16_t)LOWORD((uintptr_t)lparam);
    int dy = (int16_t)HIWORD((uintptr_t)lparam);
    
    // Horizontal: positive dx scrolls right
    scroll_x += dx;
    // Vertical: positive dy = wheel up, so subtract for natural content movement
    scroll_y -= dy;
    
    // Clamp to valid range
    if (scroll_y < 0) scroll_y = 0;
    if (scroll_y > max_scroll) scroll_y = max_scroll;
    
    invalidate_window(win);
    return true;
}

Built-in scrollbar integration:

Windows with WINDOW_HSCROLL or WINDOW_VSCROLL flags automatically handle wheel scrolling via their built-in scrollbars. The framework extracts deltas from lparam and calls sb_try_scroll() to update the scrollbar position and fire evHScroll/evVScroll notifications.

If a window cannot handle wheel events (no scrollbars or not enabled), the event bubbles to the parent window, following WinAPI behavior.

Keyboard Input

case evKeyDown:
    switch (wparam) {
        case SDL_SCANCODE_ESCAPE: running = false; break;
        case SDL_SCANCODE_S:     save_file();     break;
    }
    return true;

case evTextInput:
    append_char(win, (const char *)lparam);
    return true;

Event Loop

extern bool running;

ui_event_t e;
while (running) {
    while (get_message(&e))   // blocks until event, then drains queue
        dispatch_message(&e);
    repost_messages();        // process posted (async) messages + repaint
}

get_message() sleeps with SDL_WaitEvent when the SDL queue is empty, so the process yields the CPU instead of spinning. Calls to post_message() (including invalidate_window()) push a lightweight wakeup event into the SDL queue so that the wait is interrupted and the internal message queue is processed promptly.

Message Hooks

Register a global hook to intercept any message before it reaches its target window:

void register_hook(uint32_t msg, winhook_func_t func, void *userdata);
void unregister_hook(uint32_t msg, winhook_func_t func);

Orion UI Framework – built on SDL2 and OpenGL 3.2+

This site uses Just the Docs, a documentation theme for Jekyll.