amiga-bootcamp/09_intuition/frameworks/mui/10-events-and-notifications.md
Ilia Sharin 94a3ad1614 doc: MUI framework documentation — whitepaper overview, SDK-derived architecture, layout mockups
- README.md: comprehensive whitepaper-style overview with historical context,
  paradigm shift analysis, BOOPSI comparison, version history (Stefan Stuntz
  as sole author 1.0-3.8), licensing model breakdown, parallel evolution
  timeline (NeXTSTEP/Qt/MUI convergence), community-sourced developer values
- 02-architecture.md: complete rewrite from MUI 3.8 SDK sources — object
  lifecycle state machine, three-level resource binding, method dispatch chain,
  notification system with sequence diagrams, layout engine internals
  (3-pass constraint system), input handling, dynamic object linking,
  rendering model, tag ID namespace
- 05-layout-system.md: Mermaid visual mockups for VGroup, HGroup, nested
  groups, column grids, scrollgroups, file requester real-world example,
  layout algorithm and resize sequence diagrams
- frameworks/README.md: framework index with comparison table
- All content in American English
2026-04-23 16:46:58 -04:00

7 KiB

← Home · Intuition · Frameworks

Events and Notifications

The MUI Event Model

MUI replaces traditional callback registration with a declarative notification system. Instead of writing:

/* Traditional GUI toolkit */
button_set_callback(myButton, on_click, myHandler);

You write:

/* MUI */
DoMethod(myButton, MUIM_Notify, MUIA_Pressed, FALSE,
    targetObj, 3, MUIM_CallHook, &myHook, MUIV_TriggerValue);

This establishes a relationship: when the source attribute changes to a specific value, invoke a method on the target object.

MUIM_Notify

The notification method signature:

DoMethod(sourceObj, MUIM_Notify,
    sourceAttribute, triggerValue,
    targetObj, numArgs, targetMethod, ...);
Parameter Description
sourceObj Object to monitor
sourceAttribute Attribute to watch (e.g., MUIA_Pressed)
triggerValue Value that triggers the notification
targetObj Object to invoke the method on
numArgs Number of arguments passed to the target method
targetMethod Method to invoke (e.g., MUIM_Application_ReturnID)
... Additional arguments

Common Trigger Values

Value Meaning
MUIV_EveryTime Trigger on any change, not just a specific value
TRUE / FALSE Trigger only when boolean attribute becomes true/false
Specific value Trigger only when attribute equals this exact value

Simple Example: Close Window

DoMethod(window, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
    app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

When the window's close request flag becomes TRUE (user clicks the close gadget), the Application receives a ReturnID of Quit.

Button Press

DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
    app, 2, MUIM_Application_ReturnID, ID_SAVE);

MUIA_Pressed becomes FALSE when the user releases the mouse button over the gadget. This is the standard way to detect button clicks.

Slider Value Change

DoMethod(slider, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
    textObj, 3, MUIM_Set, MUIA_Text_Contents, MUIV_TriggerValue);

Every time the slider value changes, update a text object. MUIV_TriggerValue passes the new slider value as the argument.

Input Loops

MUI provides two methods for running the application's main loop.

Modern Loop: MUIM_Application_NewInput

This is the preferred method. It is faster because it integrates signal waiting.

ULONG sigs = 0;

while (DoMethod(app, MUIM_Application_NewInput, &sigs)
       != MUIV_Application_ReturnID_Quit)
{
    if (sigs)
    {
        sigs = Wait(sigs | SIGBREAKF_CTRL_C);
        if (sigs & SIGBREAKF_CTRL_C)
            break;
    }
}

How it works:

  1. DoMethod(app, MUIM_Application_NewInput, &sigs) processes pending input
  2. It returns a ReturnID if one was triggered, or 0
  3. It fills sigs with the signal mask the application should wait on
  4. Your code calls Wait() with those signals
  5. The loop repeats

This is significantly more efficient than the legacy loop because MUI tells you exactly which signals to wait for.

Legacy Loop: MUIM_Application_Input

Older examples use this method. It is simpler but less efficient.

ULONG signals;
BOOL running = TRUE;

while (running)
{
    switch (DoMethod(app, MUIM_Application_Input, &signals))
    {
        case MUIV_Application_ReturnID_Quit:
            running = FALSE;
            break;

        case ID_ABOUT:
            /* handle about */
            break;

        case ID_SAVE:
            /* handle save */
            break;
    }

    if (running && signals)
        Wait(signals);
}

The difference: MUIM_Application_Input handles waiting internally but returns a generic signal mask. MUIM_Application_NewInput gives you finer control.

Return IDs

Return IDs are how notifications communicate with the input loop. MUI defines a standard quit ID:

Constant Value Meaning
MUIV_Application_ReturnID_Quit -1 Application should terminate

You can define your own IDs:

#define ID_ABOUT  1
#define ID_SAVE   2
#define ID_OPEN   3

Return IDs are triggered by notifications:

DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
    app, 2, MUIM_Application_ReturnID, ID_SAVE);

Hooks

For complex actions that cannot be expressed as a simple method call, use hooks. A hook is a standard AmigaOS callback structure.

Hook Structure

struct Hook
{
    struct MinNode h_MinNode;
    ULONG          (*h_Entry)();  /* function pointer */
    ULONG          (*h_SubEntry)();
    APTR           h_Data;        /* user data */
};

Defining a Hook

SAVEDS ASM LONG MyHookFunc(REG(a2) APTR obj, REG(a1) APTR param)
{
    /* obj is the object that triggered the hook */
    /* param is the argument passed from the notification */
    return 0;
}

static struct Hook MyHook = {
    { NULL, NULL }, (VOID *)MyHookFunc, NULL, NULL
};

Invoking a Hook via Notification

DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE,
    app, 3, MUIM_CallHook, &MyHook, MUIV_TriggerValue);

Hook Register Conventions

The MUI examples use register arguments for hook functions:

Register Parameter
a0 struct Hook *
a2 Object * (source object)
a1 Msg / parameter

Different compilers handle register keywords differently, so the examples use conditional macros:

#ifdef _DCC
#define REG(x) __ ## x
#define ASM
#define SAVEDS __geta4
#else
#define REG(x) register __ ## x
#if defined __MAXON__ || defined __GNUC__
#define ASM
#define SAVEDS
#else
#define ASM    __asm
#define SAVEDS __saveds
#endif
#endif

Notification Chains

Notifications can be chained. One notification can trigger an attribute change that causes another notification:

/* When checkbox is checked, enable the button */
DoMethod(checkbox, MUIM_Notify, MUIA_Selected, TRUE,
    button, 3, MUIM_Set, MUIA_Disabled, FALSE);

/* When checkbox is unchecked, disable the button */
DoMethod(checkbox, MUIM_Notify, MUIA_Selected, FALSE,
    button, 3, MUIM_Set, MUIA_Disabled, TRUE);

Common Notification Patterns

Synchronize Two Widgets

/* Slider and numeric button show the same value */
DoMethod(slider, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
    numButton, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);

DoMethod(numButton, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
    slider, 3, MUIM_Set, MUIA_Numeric_Value, MUIV_TriggerValue);

Update Window Title

DoMethod(string, MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime,
    window, 3, MUIM_Set, MUIA_Window_Title, MUIV_TriggerValue);

Enable Widget Based on Selection

DoMethod(listview, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
    app, 2, MUIM_CallHook, &UpdateButtonHook);

Previous: Custom Classes Next: Advanced Patterns