amiga-bootcamp/09_intuition/frameworks/mui/02-architecture.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

18 KiB
Raw Permalink Blame History

← Home · Intuition · Frameworks · MUI

Architecture

This document describes MUI's internal architecture derived from the official MUI 3.8 SDK (MUIdev.guide, autodocs, and libraries/mui.h).


BOOPSI Foundation

MUI is built on BOOPSI, AmigaOS's Basic Object-Oriented Programming System for Intuition. BOOPSI provides:

  • Classes — type definitions containing a dispatcher function and default attributes
  • Objects — instances of classes created with NewObject()
  • Methods — operations invoked on objects via DoMethod()
  • Attributes — properties set with SetAttrs() and read with GetAttr()

MUI extends BOOPSI with its own class hierarchy, layout engine, and notification system while remaining fully compatible with the underlying mechanism. Every MUI object is a valid BOOPSI object; every MUI class is a BOOPSI IClass.

How MUI Extends BOOPSI

BOOPSI Primitive MUI Extension
NewObject(class, ...) MUI_NewObject(MUIC_name, ...) — auto-loads disk-resident classes
DoMethod(obj, methodID, ...) Same — MUI adds 60+ new method IDs (MUIM_*)
SetAttrs(obj, tag, val, ...) set(obj, attr, val) macro — same mechanism, convenience wrapper
GetAttr(attr, obj, &store) get(obj, attr, &store) macro
DisposeObject(obj) MUI_DisposeObject(obj) — cascading disposal of entire object tree

Class Hierarchy

The canonical class tree from libraries/mui.h:

rootclass (BOOPSI's base class)
|
+-- Notify                   (implements notification mechanism)
|   |
|   +-- Family               (handles multiple children)
|   |   |
|   |   +-- Menustrip        (describes a complete menu strip)
|   |   +-- Menu             (describes a single menu)
|   |   \-- Menuitem         (describes a single menu item)
|   |
|   +-- Application          (main class for all applications)
|   |
|   +-- Window               (main class for all windows)
|   |   \-- Aboutmui         (About window of MUI preferences)
|   |
|   +-- Area                 (base class for all GUI elements)
|       |
|       +-- Rectangle        (spacing object)
|       +-- Balance          (balancing separator bar)
|       +-- Image            (image display)
|       +-- Bitmap           (draws bitmaps)
|       |   \-- Bodychunk    (makes bitmap from ILBM body chunk)
|       +-- Text             (text display)
|       +-- Gadget           (base class for intuition gadgets)
|       |   |
|       |   +-- String       (string gadget)
|       |   +-- Boopsi       (interface to BOOPSI gadgets)
|       |   \-- Prop         (proportional gadget)
|       |
|       +-- Gauge            (fuel gauge)
|       +-- Scale            (percentage scale)
|       +-- Colorfield       (field with changeable color)
|       |
|       +-- List             (line-oriented list)
|       |   |
|       |   +-- Floattext    (special list with floating text)
|       |   +-- Volumelist   (special list with volumes)
|       |   +-- Scrmodelist  (special list with screen modes)
|       |   \-- Dirlist      (special list with files)
|       |
|       +-- Numeric          (base class for slider gadgets)
|       |   |
|       |   +-- Knob         (turning knob)
|       |   +-- Levelmeter   (level display)
|       |   +-- Numericbutton (space saving popup slider)
|       |   \-- Slider       (traditional slider)
|       |
|       +-- Group            (groups other GUI elements)
|           |
|           +-- Register     (handles page groups with titles)
|           +-- Virtgroup    (handles virtual groups)
|           +-- Scrollgroup  (virtual groups with scrollbars)
|           +-- Scrollbar    (traditional scrollbar)
|           +-- Listview     (listview)
|           +-- Radio        (radio button)
|           +-- Cycle        (cycle gadget)
|           +-- Coloradjust  (several gadgets to adjust a color)
|           +-- Palette      (complete palette gadget)
|           |
|           +-- Popstring    (base class for popup objects)
|               |
|               +-- Popobject (popup anything in a separate window)
|               |   |
|               |   +-- Poplist   (popup a simple listview)
|               |   \-- Popscreen (popup a list of public screens)
|               |
|               \-- Popasl    (popup an asl requester)
|
+-- Semaphore                (semaphore equipped objects)
    |
    +-- Applist              (private)
    +-- Dataspace            (handles general purpose data spaces)
        \-- Configdata       (private)

Class Role Summary

Branch Root Responsibility
Notify rootclass → Notify Attribute observation, inter-object messaging
Application Notify → Application Program root, event loop, ARexx, Commodities
Window Notify → Window Intuition window lifecycle, IDCMP multiplexing
Area Notify → Area Visual base — sizing, rendering, input, frames
Group Area → Group Layout container — child arrangement
Semaphore Notify → Semaphore Thread-safe data containers

Application Object Tree

A MUI application forms a tree at runtime. Only three class types may have children:

graph TB
    APP["Application<br/><i>zero or more Window children</i>"]
    W1["Window 1"]
    W2["Window 2"]
    ROOT1["Group<br/><i>root object</i>"]
    ROOT2["Group<br/><i>root object</i>"]
    C1["String"]
    C2["Group"]
    C3["Text"]
    C4["List"]
    C5["Cycle"]

    APP --> W1 & W2
    W1 --> ROOT1
    W2 --> ROOT2
    ROOT1 --> C1 & C2 & C3
    C2 --> C4 & C5

    style APP fill:#fff3e0,stroke:#ff9800,color:#333
    style W1 fill:#e8f4fd,stroke:#2196f3,color:#333
    style W2 fill:#e8f4fd,stroke:#2196f3,color:#333
    style ROOT1 fill:#e8f5e9,stroke:#4caf50,color:#333
    style ROOT2 fill:#e8f5e9,stroke:#4caf50,color:#333
Parent Allowed Children Count
Application Window objects only 0..N
Window Any Area subclass (usually Group) Exactly 1
Group Any Area subclass (including nested Groups) 1..N

Error handling is cascading: if any child creation fails (returns NULL), the parent automatically disposes all previously created siblings and returns NULL itself. A successful ApplicationObject guarantees the entire tree was created. A single MUI_DisposeObject(app) destroys everything.


Object Lifecycle

Every MUI object passes through a strict sequence of constructor/destructor pairs. The SDK documents this as the fundamental contract:

OM_NEW          — parse initial taglist, allocate instance data
{
   MUIM_Setup      — display environment known (screen, fonts, DrawInfo)
   MUIM_AskMinMax  — report min/max/default dimensions
   [ window opens ]
   {
      MUIM_Show       — object added to window (AddGadget for Intuition types)
      {
         MUIM_Draw      — render content (may be called multiple times)
      }
      MUIM_Hide       — object removed from window (RemGadget)
   }
   [ window closes ]
   MUIM_Cleanup    — free display-dependent resources
}
OM_DISPOSE      — free all resources, destroy object

Lifecycle as State Machine

stateDiagram-v2
    [*] --> Created: OM_NEW
    Created --> SetUp: MUIM_Setup
    SetUp --> Sized: MUIM_AskMinMax
    Sized --> Visible: MUIM_Show
    Visible --> Visible: MUIM_Draw (repeated)
    Visible --> Sized: MUIM_Hide (resize)
    Sized --> Visible: MUIM_Show (re-layout)
    Sized --> Created: MUIM_Cleanup
    Created --> [*]: OM_DISPOSE

Three Levels of Resource Binding

Level Constructor Destructor Available Information
Object OM_NEW OM_DISPOSE Nothing — only taglist parsing
Display MUIM_Setup MUIM_Cleanup Screen, fonts, DrawInfo, pens
Window MUIM_Show MUIM_Hide Intuition Window, RastPort, position/size

Each level may repeat independently. You may receive multiple Setup/Cleanup cycles (e.g., screen mode change) and multiple Show/Hide cycles within a single Setup (e.g., window resize).


Method Dispatch Model

Every MUI class has a dispatcher function — a single entry point that receives all method calls. This is the standard BOOPSI dispatch pattern:

ULONG MyDispatcher(struct IClass *cl, Object *obj, Msg msg)
{
    switch (msg->MethodID)
    {
        case OM_NEW        : return mNew(cl, obj, (APTR)msg);
        case OM_DISPOSE    : return mDispose(cl, obj, (APTR)msg);
        case OM_SET        : return mSet(cl, obj, (APTR)msg);
        case OM_GET        : return mGet(cl, obj, (APTR)msg);
        case MUIM_Setup    : return mSetup(cl, obj, (APTR)msg);
        case MUIM_Cleanup  : return mCleanup(cl, obj, (APTR)msg);
        case MUIM_AskMinMax: return mAskMinMax(cl, obj, (APTR)msg);
        case MUIM_Draw     : return mDraw(cl, obj, (APTR)msg);
        case MUIM_HandleInput: return mHandleInput(cl, obj, (APTR)msg);
    }

    /* Unknown methods → pass to superclass */
    return DoSuperMethodA(cl, obj, msg);
}

Dispatch Chain

graph LR
    CALLER["DoMethod(obj, MUIM_Draw)"] --> DISP_MY["MyClass<br/>Dispatcher"]
    DISP_MY -->|"handled"| RESULT["Return value"]
    DISP_MY -->|"DoSuperMethodA()"| DISP_AREA["Area<br/>Dispatcher"]
    DISP_AREA -->|"DoSuperMethodA()"| DISP_NOTIFY["Notify<br/>Dispatcher"]
    DISP_NOTIFY -->|"DoSuperMethodA()"| DISP_ROOT["rootclass<br/>Dispatcher"]

    style DISP_MY fill:#e8f5e9,stroke:#4caf50,color:#333
    style DISP_AREA fill:#e8f4fd,stroke:#2196f3,color:#333
    style DISP_NOTIFY fill:#e8f4fd,stroke:#2196f3,color:#333

Key rules:

  • Always call DoSuperMethodA() for unrecognized methods — MUI sends undocumented internal methods
  • Never render in OM_SET — call MUI_Redraw(obj, flag) instead; MUI will invoke MUIM_Draw
  • The dispatcher receives struct IClass *cl (the class pointer), Object *obj, and Msg msg in registers a0, a2, a1 respectively

Notification System

The central mechanism for controlling a MUI application. Notification creates declarative reactive bindings between objects.

How It Works

Every MUI object has attributes that reflect its current state. Attributes change either programmatically (SetAttrs()) or through user interaction. Notification watches for these changes and automatically invokes methods on target objects.

sequenceDiagram
    participant User
    participant Scrollbar as Scrollbar Object
    participant MUI as MUI Notify Engine
    participant List as List Object

    User->>Scrollbar: Drags knob
    Scrollbar->>Scrollbar: MUIA_Prop_First changes
    Scrollbar->>MUI: Attribute change detected
    MUI->>MUI: Match against registered notifications
    MUI->>List: MUIM_Set(MUIA_List_TopPixel, new_value)
    List->>List: Scrolls to new position

Registration

DoMethod(source, MUIM_Notify,
    watched_attribute,    /* which attribute to observe */
    trigger_value,        /* MUIV_EveryTime or specific value */
    target_object,        /* destination object */
    param_count,          /* number of following parameters */
    target_method, ...);  /* method + arguments to invoke */

Notification Suppression

When setting attributes programmatically, you may want to suppress notifications to avoid loops:

/* Normal set — triggers any registered notifications */
set(slider, MUIA_Slider_Level, 50);

/* Suppressed set — no notifications fire */
SetAttrs(slider, MUIA_NoNotify, TRUE,
    MUIA_Slider_Level, 50, TAG_DONE);

MUIA_NoNotify is a one-shot attribute — it only applies to the current SetAttrs() call.

Deregistration

/* Remove all notifications on a specific attribute */
DoMethod(source, MUIM_KillNotify, MUIA_Slider_Level);

/* Remove notification targeting a specific object */
DoMethod(source, MUIM_KillNotifyObj, MUIA_Slider_Level, target);

Layout Engine Internals

MUI's layout engine uses a three-pass constraint system that runs before window open and on every resize.

Pass 1: AskMinMax (Bottom-Up)

Every object reports its minimum, maximum, and default dimensions. Leaf objects (Text, String, etc.) report fixed values based on content and font. Group objects aggregate their children:

Horizontal Group:

  • Min width = sum of all children's min widths
  • Max width = sum of all children's max widths
  • Min height = largest child min height
  • Max height = smallest child max height

Vertical Group:

  • Min height = sum of all children's min heights
  • Max height = sum of all children's max heights
  • Min width = largest child min width
  • Max width = smallest child max width

Pass 2: Size Distribution (Top-Down)

Starting from the window's current size, the root group distributes available space among children. Extra space (beyond minimum) is distributed proportionally according to weight attributes (MUIA_Weight, default 100).

Pass 3: Placement

Each object receives its final rectangle (left, top, width, height) and records it in instance data accessible via macros:

Macro Returns
_mleft(obj) Left edge of content area (inside frame)
_mtop(obj) Top edge of content area
_mright(obj) Right edge of content area
_mbottom(obj) Bottom edge of content area
_mwidth(obj) Content area width
_mheight(obj) Content area height
_rp(obj) RastPort pointer for rendering

Layout Flow

graph TB
    subgraph "Pass 1: AskMinMax (bottom-up)"
        LEAF1["Text: min=40, max=200"]
        LEAF2["Button: min=60, max=120"]
        GROUP1["HGroup: min=100, max=320"]
    end

    subgraph "Pass 2: Distribute (top-down)"
        WINDOW["Window size: 250px wide"]
        CALC["HGroup: 250px available<br/>Text weight=100, Button weight=100<br/>→ Text=125px, Button=125px"]
    end

    subgraph "Pass 3: Place"
        PLACE1["Text: left=5, width=125"]
        PLACE2["Button: left=130, width=125"]
    end

    LEAF1 & LEAF2 --> GROUP1
    GROUP1 --> WINDOW --> CALC
    CALC --> PLACE1 & PLACE2

Input Handling

MUI multiplexes Intuition IDCMP messages through the Application object's event loop.

Event Flow

graph TB
    IDCMP["Intuition IDCMP Port<br/>(mouse, keyboard)"]
    APP["MUIM_Application_NewInput"]
    DISPATCH["MUI dispatches to<br/>active/relevant objects"]
    HANDLE["MUIM_HandleInput<br/>(on each object)"]
    NOTIFY["Notifications fire<br/>(attribute changes)"]
    RETURN["Return ID to app loop"]

    IDCMP --> APP --> DISPATCH --> HANDLE --> NOTIFY --> RETURN

    style APP fill:#fff3e0,stroke:#ff9800,color:#333
    style HANDLE fill:#e8f5e9,stroke:#4caf50,color:#333

IDCMP Subscription

Custom classes request specific IDCMP events dynamically:

/* During MUIM_HandleInput — request mouse move tracking */
MUI_RequestIDCMP(obj, IDCMP_MOUSEMOVE);

/* Stop tracking when done */
MUI_RejectIDCMP(obj, IDCMP_MOUSEMOVE);

MUI translates raw IDCMP events into higher-level MUIKEY_* constants for keyboard navigation:

MUIKEY Constant Meaning
MUIKEY_PRESS Confirm / activate
MUIKEY_TOGGLE Toggle selection
MUIKEY_UP/DOWN/LEFT/RIGHT Directional navigation
MUIKEY_PAGEUP/PAGEDOWN Page scrolling
MUIKEY_TOP/BOTTOM Jump to extremes
MUIKEY_WORDLEFT/WORDRIGHT Word-level navigation

Dynamic Object Linking

MUI supports late binding — adding and removing children after the initial tree is created.

/* Add a window to a running application */
DoMethod(app, OM_ADDMEMBER, new_window);
set(new_window, MUIA_Window_Open, TRUE);

/* Remove and dispose when done */
set(new_window, MUIA_Window_Open, FALSE);
DoMethod(app, OM_REMMEMBER, new_window);
MUI_DisposeObject(new_window);  /* manual disposal required */

For groups, the window must be closed before modifying children:

set(window, MUIA_Window_Open, FALSE);
DoMethod(group, MUIM_Group_InitChange);    /* begin modification */
DoMethod(group, OM_ADDMEMBER, new_child);
DoMethod(group, MUIM_Group_ExitChange);    /* end — triggers re-layout */
set(window, MUIA_Window_Open, TRUE);

Rendering Model

MUI uses retained-mode rendering — the framework maintains the object tree and handles all damage repair.

Draw Flags

Flag Meaning
MADF_DRAWOBJECT Complete redraw of the object
MADF_DRAWUPDATE Incremental update (optimization)

Custom classes call MUI_Redraw(obj, flag) to request a redraw. MUI calls MUIM_Draw with appropriate flags. Inside MUIM_Draw, the class can check which type of redraw is needed:

case MUIM_Draw:
{
    struct MUIP_Draw *msg = (struct MUIP_Draw *)msg;
    DoSuperMethodA(cl, obj, msg);  /* let Area draw background + frame */

    if (msg->flags & MADF_DRAWOBJECT)
    {
        /* Full redraw — render everything */
    }
    else if (msg->flags & MADF_DRAWUPDATE)
    {
        /* Partial update — only changed elements */
    }
    return 0;
}

Rendering Rules

  1. Never draw outside MUIM_Draw — not in OM_SET, not in MUIM_HandleInput
  2. Always call DoSuperMethodA() first — Area class draws background and frame
  3. Only draw within _mleft() / _mtop() / _mright() / _mbottom() — the content rectangle inside the frame
  4. Use _rp(obj) to get the RastPort — never cache it across Show/Hide cycles

Tag ID Namespace

MUI uses the AmigaOS TagItem system for all attributes and methods. The namespace is partitioned:

Range Owner
0x80420000 0x8042FFFF MUI built-in classes (reserved)
TAG_USER | (serial << 16) | offset Registered third-party classes

Each registered MUI developer received a unique serial number. All their class attributes and methods are prefixed with TAG_USER | (serial << 16) to avoid tag collisions across the MCC ecosystem.


Key Architectural Files

File Purpose
libraries/mui.h Main header — class names, method IDs, attribute tags, macros, class tree
clib/muimaster_protos.h C prototypes for muimaster.library functions
proto/muimaster.h Compiler-specific prototype wrappers
pragma/muimaster_lib.h SAS/C library call pragmas
muimaster.library Runtime — object creation, layout, event handling, preferences
MUI_<Class>.doc Per-class autodoc reference (66 files)
MUIdev.guide Official developer guide — architecture, custom classes, style guide

Previous: Introduction Next: Getting Started