IDCMP Event Handling — Section 3.7

This article synthesises Section 3.7 ("Checking the IDCMP") of Amiga C for Advanced Programmers by Bleek, Jennrich, Schulz (Abacus / Data Becker, ~1991). It covers the Intuition event loop pattern: configuring which events a window receives (the IDCMPFlags field of NewWindow), reading messages from the window's UserPort, and dispatching based on the event class.

For the modern comprehensive treatment, see intuition/intuition-windows-screens-events.md and library-reference/intuition-library.md. The book covers the pre-OS-2.0 model; OS 2.0+ uses the same patterns but with WindowUserData(), GT_GetIMsg(), and IExec->Wait().

Editorial note: page numbers in this article refer to the PDF page index in the raw OCR source.


What the IDCMP is (page 273)

The Intuition Direct-Communication Message Port (IDCMP) is the asynchronous event channel between Intuition and your application. When the user moves the mouse, clicks a gadget, selects a menu, or anything else happens in your window, Intuition posts a message on the window's UserPort. Your event loop pulls messages off, dispatches based on the message class, and replies to free the slot.

"The IDCMP is not constantly on standby, ready for the programmer's call. Intuition must know that a possibility exists for data reception."

The book breaks down IDCMP flags into six conceptual groups (page 274), which map to the event classes in <intuition/intuition.h>:

Group Common flags
Window lifecycle IDCMP_REFRESHWINDOW, IDCMP_NEWSIZE, IDCMP_CLOSEWINDOW, IDCMP_ACTIVEWINDOW, IDCMP_INACTIVEWINDOW
Mouse IDCMP_MOUSEBUTTONS, IDCMP_MOUSEMOVE, IDCMP_DELTAMOVE
Keyboard IDCMP_RAWKEY, IDCMP_VANILLAKEY
Gadgets IDCMP_GADGETDOWN, IDCMP_GADGETUP
Menus IDCMP_MENUPICK, IDCMP_MENUVERIFY
Requesters IDCMP_REQSET, IDCMP_REQCLEAR, IDCMP_REQVERIFY

Each flag is a single bit in the IDCMPFlags field of NewWindow. You set the bits you care about; Intuition only sends you those events.


The IntuiMessage structure (page 280)

Each event message has the format:

struct IntuiMessage {
    struct Message ExecMessage;        /* standard exec message header */
    ULONG Class;                       /* IDCMP_* */
    UWORD Code;                        /* event-specific */
    UWORD Qualifier;                  /* IEQUALIFIER_* */
    APTR  IAddress;                    /* gadget, menu item, etc. */
    WORD  MouseX, MouseY;             /* relative to window origin */
    ULONG Seconds, Micros;            /* timestamp (V36+) */
    struct Window *IDCMPWindow;
    struct IntuiMessage *SpecialLink;  /* V39+ chaining */
};

Most applications only need: - Class — what happened - Code — additional info (e.g. for IDCMP_RAWKEY, this is the raw key code) - Qualifier — shift / alt / ctrl state - IAddress — gadget pointer or menu pointer - MouseX, MouseY — pointer position


The event loop (page 281)

The book's standard pattern:

BOOL done = FALSE;

while (!done) {
    /* Block until at least one event is available */
    WaitPort(win->UserPort);

    /* Drain all pending events */
    while ((msg = (struct IntuiMessage *)GetMsg(win->UserPort))) {
        switch (msg->Class) {
            case IDCMP_CLOSEWINDOW:
                done = TRUE;
                break;

            case IDCMP_REFRESHWINDOW:
                BeginRefresh(win);
                /* redraw your content */
                EndRefresh(win, TRUE);
                break;

            case IDCMP_MOUSEBUTTONS:
                if (msg->Code == SELECTDOWN) {
                    /* left mouse press at (msg->MouseX, msg->MouseY) */
                }
                break;

            case IDCMP_RAWKEY:
                /* see [intuition-keymap](../intuition/intuition-windows-screens-events.md) */
                break;

            case IDCMP_GADGETUP:
                /* msg->IAddress is the Gadget */
                break;

            case IDCMP_MENUPICK:
                /* msg->Code is the menu number (encode as FullMenuStrip) */
                break;

            default:
                break;
        }

        /* Return the message to Intuition */
        ReplyMsg((struct Message *)msg);
    }
}

This is the canonical pattern. Variations:

  • Multi-source wait: if you have multiple windows / ports, use Wait() with win->UserPort->mp_SigBit instead of WaitPort().
  • Modern (OS 4 / MorphOS): use IExec->Wait() instead of Wait().

Reading the message: class-by-class (page 281+)

The book's section 3.7 walks through the major message classes.

IDCMP_CLOSEWINDOW and IDCMP_NEWSIZE

Simplest messages. Class tells you the type; Code is unused.

case IDCMP_CLOSEWINDOW:
    done = TRUE;
    break;
case IDCMP_NEWSIZE:
    /* window was resized; may need to re-layout content */
    RefreshWindowFrame(win);
    break;

IDCMP_MOUSEBUTTONS

Code holds the mouse code: SELECTDOWN, SELECTUP, MIDDLEDOWN, MIDDLEUP, MENUDOWN, MENUUP. The book uses the IECODE_* constants:

case IDCMP_MOUSEBUTTONS:
    if (msg->Code == SELECTDOWN) {
        /* left button pressed at msg->MouseX, msg->MouseY */
    }

IDCMP_RAWKEY

Code is the raw key code; Qualifier has shift / alt / ctrl state.

case IDCMP_RAWKEY:
    if (msg->Code == 0x44) {  /* left arrow */
        /* move cursor left */
    }
    break;

The book's section 3.10 covers raw-key → ASCII conversion tables (pre-OS-2.0 era; OS 2.0+ has Intuition → Keymap).

IDCMP_GADGETUP

IAddress is the struct Gadget * that triggered. Code is unused for classic gadgets:

case IDCMP_GADGETUP:
    {
        struct Gadget *g = (struct Gadget *)msg->IAddress;
        /* use g->GadgetID to identify */
    }
    break;

IDCMP_MENUPICK

Code is the menu number. Use MenuNumber() to decode:

case IDCMP_MENUPICK:
    {
        UWORD code = msg->Code;
        while (code != MENUNULL) {
            struct MenuItem *item = ItemAddress(menu_strip, code);
            /* process item */
            code = item->NextSelect;
        }
    }
    break;

ReplyMsg: freeing the slot (page 290)

Every message retrieved with GetMsg() MUST be replied via ReplyMsg(). This signals Intuition that the message structure can be reused.

The book is explicit: if you forget ReplyMsg(), you'll eventually exhaust Intuition's message pool (typically 32 messages) and the system will lock up.

Modern OS 2.0+ provides GT_GetIMsg() and GT_ReplyIMsg() for gadget messages specifically — these functions do ReplyMsg() automatically when appropriate.


Multi-window applications

The book notes that more complex applications have multiple windows with their own ports. The pattern becomes:

/* Wait for any of N ports */
ULONG sigmask = 0;
for (i = 0; i < nports; i++) sigmask |= (1UL << ports[i]->mp_SigBit);
ULONG signals = Wait(sigmask);

/* Service whichever ports have signals */
for (i = 0; i < nports; i++) {
    if (signals & (1UL << ports[i]->mp_SigBit)) {
        /* process this port */
    }
}

The modern replacement is IExec->Wait() (V40+) which is functionally identical but uses an exec.library Cleanup macro.


What's NOT in section 3.7

  • IECLASS_xxx constants (V39+ class-based events). The book uses the legacy Class field directly; OS 2.0+ accepts both.
  • IIntuition->GetAttr() / DoGadgetMethod() (V39+) — the BOOPSI way to query gadgets programmatically.
  • Async / interrupt-driven event handling — events are always handled on the main task.
  • SetGadgetAttrs() from an interrupt — covered in BOOPSI autodocs, not here.

Modern migration

If writing new code today:

Use this Instead of this Why
IExec->Wait() Wait() Forward-compatible
GT_GetIMsg() for gadgets GetMsg() + manual class check Type-safe gadget helpers
IECLASS_* constants IDCMP_* legacy flags V39+ class system
IIntuition->IDoMethod() Direct gadget-pointer poke Method dispatch

But the fundamentals (port → get → dispatch → reply) carry forward unchanged.


Sources