amiga-bootcamp/08_graphics/views.md

26 KiB
Raw Permalink Blame History

← Home · Graphics

Views and ViewPorts — Display Construction

Overview

Drawing a frame on the Amiga isn't like drawing on a modern GPU where you fill a framebuffer and flip. The Amiga's custom chips demand a Copper list — a script of register writes timed to the video beam — before a single pixel appears. The OS provides a 3-stage pipeline that translates a high-level description of your screen into that low-level Copper program: MakeVPortMrgCopLoadView.

The pipeline is a compiler, in the literal sense. You give it a View (a chain of ViewPort descriptors, each pointing to a BitMap and a ColorMap), and it generates the Copper instructions that tell Agnus/Alice which bitplanes to fetch, when to switch palettes mid-screen, where to place sprites, and how to scroll. The result is a display that can mix resolutions, color depths, and scroll offsets on different horizontal bands — a feature no consumer OS would replicate for over a decade.

This system is what makes the Amiga's draggable screens possible. Each screen is a View, and Workbench can slide them up and down by changing scroll offsets in the Copper list — no framebuffer copy, no blit, just a register write.


The 3-Stage Pipeline

graph TD
    subgraph "Stage 1: MakeVPort"
        APP["Application"] -->|"sets View→ViewPort→RasInfo→BitMap,<br/>DWidth, DHeight, Modes, ColorMap"| MV["MakeVPort(view, vp)"]
        MV -->|"merges View + ViewPort settings,<br/>allocates intermediate Copper lists<br/>(DspIns, SprIns, ClrIns, UCopIns),<br/>builds the color table"| VPI["ViewPort → intermediate CopLists"]
    end

    subgraph "Stage 2: MrgCop"
        VPI --> MC["MrgCop(view)"]
        MC -->|"merges all ViewPort CopLists into<br/>one unified Copper program,<br/>handles ViewPort boundary<br/>transitions, inserts WAIT/MOVE for<br/>mid-screen mode changes"| COP["LOFCprList / SHFCprList<br/>(final Copper list)"]
    end

    subgraph "Stage 3: LoadView"
        COP --> LV["LoadView(view)"]
        LV -->|"installs view as GfxBase→ActiView,<br/>swaps Copper pointer at next VBlank,<br/>display goes live"| HW["Custom Chips<br/>(Agnus/Alice + Denise/Lisa)"]
    end

    style MV fill:#fff9c4,stroke:#f9a825
    style MC fill:#fff9c4,stroke:#f9a825
    style LV fill:#c8e6c9,stroke:#2e7d32
    style HW fill:#e8f4fd,stroke:#2196f3

Stage 1: MakeVPop(View, ViewPort) — Compile the ViewPort

MakeVPort is the per-ViewPort compiler. For each ViewPort in the View's chain, it:

  1. Validates mode combinations — HAM + Dual Playfield? Rejected. SuperHires on OCS? Silently downgraded.
  2. Allocates Copper instruction buffersDspIns (display setup: bitplane pointers, modulo, BPLCON registers), SprIns (sprite DMA control), ClrIns (palette loads), UCopIns (user Copper list, if provided).
  3. Builds the color table — maps ColorMap entries to hardware COLOR registers. For each color register touched by this ViewPort, generates CMOVE instructions that load it at the top of the ViewPort's vertical region.
  4. Computes display window — from DWidth, DHeight, DxOffset, DyOffset, and mode flags, calculates DIWSTRT/DIWSTOP/DDFSTRT/DDFSTOP values.

Note

MakeVPort does not generate the final Copper list. It produces intermediate CopList structures — one per ViewPort. These are raw sequences of CMOVE/CWAIT macros that know about their own ViewPort's vertical range but not about other ViewPorts. MrgCop handles the merge.

Stage 2: MrgCop(View) — Merge into One Copper Program

MrgCop is the linker. It takes all the intermediate CopLists from every ViewPort and stitches them into a single Copper program — the LOFCprList (long-frame) and, for interlaced displays, SHFCprList (short-frame). This is where the system decides:

  • ViewPort ordering — ViewPorts with lower DyOffset appear higher on screen. The Copper list inserts WAIT instructions at each ViewPort boundary.
  • Mid-screen mode changes — if ViewPort 0 is lores 5-bitplanes and ViewPort 1 is hires 4-bitplanes, MrgCop generates the BPLCON0/BPLCON1 switch instructions at the transition line.
  • Color register conflicts — if two ViewPorts use the same hardware color register, MrgCop inserts reload instructions at the boundary so each ViewPort sees its own palette.
  • Copper list overflow protection — if the combined Copper list exceeds the hardware's ~2.5 KB instruction limit (roughly 312 instructions), MrgCop uses a "loop-back" technique: the short-frame list renders half the ViewPorts, the long-frame list renders the other half, alternating per field. This is why interlaced screens with many ViewPorts flicker.

The output is stored in view->LOFCprList and view->SHFCprList as raw struct cprlist nodes — exactly the format the Copper hardware expects.

Stage 3: LoadView(View) — Activate at VBlank

LoadView is deceptively simple. It:

  1. Waits for the vertical blank — the Copper reloads its program pointer at the start of each frame
  2. Swaps Copper pointers — atomically (from the Copper's perspective) switches the hardware's COP1LC register to point at the new LOFCprList
  3. Sets GfxBase->ActiView — marks this View as the active display
  4. Returns immediately — the new Copper list takes effect on the next scanline

This is why LoadView must always be followed by WaitTOF() (or two, for interlaced displays). Without the wait, your code may try to free the old View's resources before the Copper has stopped reading them.

Warning

Calling LoadView mid-frame (without waiting for VBlank) will tear — the Copper switches lists partway through a frame, producing a split image with the top half from the old View and the bottom half from the new one. LoadView itself does not wait; it's your responsibility to synchronize.


Key Structures

/* graphics/view.h — NDK39 */
struct View {
    struct ViewPort *ViewPort;  /* first viewport in chain */
    struct cprlist  *LOFCprList; /* long-frame copper list */
    struct cprlist  *SHFCprList; /* short-frame copper list */
    WORD            DyOffset;   /* vertical scroll offset */
    WORD            DxOffset;   /* horizontal scroll offset */
    UWORD           Modes;      /* display mode flags */
};

struct ViewPort {
    struct ViewPort *Next;       /* next viewport in chain */
    struct ColorMap *ColorMap;   /* palette for this viewport */
    struct CopList  *DspIns;     /* display copper instructions */
    struct CopList  *SprIns;     /* sprite copper instructions */
    struct CopList  *ClrIns;     /* color copper instructions */
    struct CopList  *UCopIns;    /* user copper instructions */
    WORD            DWidth;      /* display width */
    WORD            DHeight;     /* display height */
    WORD            DxOffset;
    WORD            DyOffset;
    UWORD           Modes;       /* HIRES, LACE, HAM, EHB, etc. */
    UBYTE           SpritePriorities;
    UBYTE           ExtendedModes;
    struct RasInfo  *RasInfo;    /* linked bitmap info */
};

struct RasInfo {
    struct RasInfo *Next;        /* for dual-playfield */
    struct BitMap  *BitMap;      /* the actual bitmap */
    WORD           RxOffset;     /* horizontal scroll within bitmap */
    WORD           RyOffset;     /* vertical scroll within bitmap */
};

Display Mode Flags

/* graphics/view.h */
#define HIRES       0x8000   /* 640 pixel mode */
#define LACE        0x0004   /* interlaced */
#define HAM         0x0800   /* Hold-And-Modify (4096 colors) */
#define EXTRA_HALFBRITE 0x0080 /* Extra Half-Brite (64 colors) */
#define DUALPF      0x0400   /* dual playfield */
#define PFBA        0x0040   /* playfield B has priority */
#define SUPERHIRES  0x0020   /* 1280 pixel mode (ECS+) */
#define VP_HIDE     0x2000   /* viewport is hidden */

Building a Display

struct View view;
struct ViewPort vp;
struct RasInfo ri;
struct BitMap *bm = AllocBitMap(320, 256, 5, BMF_DISPLAYABLE|BMF_CLEAR, NULL);

InitView(&view);
InitVPort(&vp);
view.ViewPort = &vp;
vp.RasInfo = &ri;
ri.BitMap = bm;
vp.DWidth = 320;
vp.DHeight = 256;
vp.Modes = 0;   /* lores */

/* Build color map: */
vp.ColorMap = GetColorMap(32);

/* Compile to copper: */
MakeVPort(&view, &vp);
MrgCop(&view);

/* Activate: */
LoadView(&view);
WaitTOF();

/* Cleanup: */
LoadView(GfxBase->ActiView);  /* restore system view */
FreeVPortCopLists(&vp);
FreeCprList(view.LOFCprList);
FreeColorMap(vp.ColorMap);
FreeBitMap(bm);

ViewPort Chaining — Split Screens

The ViewPort->Next pointer chains multiple ViewPorts vertically, producing the Amiga's signature split-screen effect. Each ViewPort occupies a horizontal band with its own resolution, color depth, palette, and scroll offset:

┌────────────────────────────────┐  ← Top of display
│  ViewPort 0: 320×100, 32-color │  DyOffset = 0
│  5 bitplanes, palette 0        │
├────────────────────────────────┤
│  ViewPort 1: 640×100, 4-color  │  DyOffset = 100
│  2 bitplanes, palette 1        │
├────────────────────────────────┤
│  ViewPort 2: 320×56, HAM6      │  DyOffset = 200
│  6 bitplanes, palette 2        │
└────────────────────────────────┘  ← Bottom of display (256 lines)
/* Three-ViewPort split screen: */
struct View view;
struct ViewPort vp[3];
struct RasInfo ri[3];
struct BitMap *bm[3];

InitView(&view);
view.ViewPort = &vp[0];          /* chain start */

for (int i = 0; i < 3; i++) {
    InitVPort(&vp[i]);
    vp[i].RasInfo = &ri[i];
    ri[i].BitMap = bm[i];
    vp[i].DWidth  = 320;
    vp[i].DHeight = (i == 2) ? 56 : 100;
    vp[i].DyOffset = i * 100;    /* 0, 100, 200 */
    vp[i].ColorMap = GetColorMap(32);
    vp[i].Next = (i < 2) ? &vp[i+1] : NULL;

    MakeVPort(&view, &vp[i]);
}
MrgCop(&view);                    /* merge all three into one Copper list */
LoadView(&view);
WaitTOF();

How the Copper handles it. MrgCop inserts WAIT instructions at each ViewPort boundary (lines 100 and 200). When the beam reaches line 100, the Copper hits a WAIT for ViewPort 1, which triggers a MOVE sequence that reloads BPLCON0 (new bitplane count), BPL1PTH/BPL1PTL (new bitplane pointers), and COLOR00COLOR31 (new palette). The display changes resolution and color depth mid-frame with no CPU involvement.

Constraints:

  • Maximum ~16 ViewPorts practical (Copper list size limit)
  • Each ViewPort increase consumes ~2040 Copper instructions for mode/palette setup
  • OCS: maximum 6 bitplanes total across all ViewPorts (sum of bitplane counts). ECS/AGA: up to 6 or 8 respectively per ViewPort
  • Dual playfield ViewPorts cannot be chained — only one dual-playfield ViewPort per View

ColorMap & LoadRGB4

The ColorMap connects logical color indices (031 for OCS/ECS, 0255 for AGA) to hardware color registers and their 12-bit or 24-bit RGB values:

struct ColorMap *cm = GetColorMap(32);            /* request 32 color registers */
vp.ColorMap = cm;

/* Load a palette — each entry is (R,G,B) in 4-bit (015) per channel: */
UWORD palette[32];
palette[0] = 0x000;    /* black */
palette[1] = 0xF00;    /* red (R=15, G=0, B=0) */
palette[2] = 0x0F0;    /* green */
palette[3] = 0x00F;    /* blue */
palette[4] = 0xFFF;    /* white */
/* ... etc ... */

LoadRGB4(&vp, palette, 32);   /* load 32 colors into color table */

/* After LoadRGB4, you must rebuild: */
MakeVPort(&view, &vp);
MrgCop(&view);
/* LoadView not needed if view already active — Copper reloads palette
   at the top of the ViewPort automatically */

Color register sharing. On OCS/ECS, there are only 32 hardware color registers (COLOR00COLOR31). If three ViewPorts each request 32 colors, MrgCop must arbitrate. Two ViewPorts can share registers if their palettes are identical at those indices; otherwise, MrgCop reloads the conflicting registers at the ViewPort boundary. On AGA, with 256 color registers (COLOR00COLOR255), register conflicts are rare and the Copper reload overhead diminishes.

Note

LoadRGB4 only updates the color table in RAM. The actual Copper register loads happen when the beam reaches the ViewPort's vertical region — MrgCop generated those instructions at compile time. This means palette changes mid-frame (via Copper-driven color cycling) are possible by modifying the Copper list directly, bypassing LoadRGB4.


Named Antipatterns

1. "The Ghost Screen" — Freeing While Displayed

Broken:

LoadView(&myView);
WaitTOF();
/* ... user clicks "close" ... */
FreeVPortCopLists(&vp);
FreeCprList(myView.LOFCprList);
FreeColorMap(vp.ColorMap);
FreeBitMap(bm);
/* The Copper is STILL reading LOFCprList! Next frame = crash or garbage. */

Why it fails. LoadView told the Copper to start using myView.LOFCprList. Freeing those Copper lists while the Copper is actively executing them produces random custom chip register writes — colors flicker, bitplane pointers go wild, and the system can hang.

Fixed:

LoadView(GfxBase->ActiView);  /* restore system View FIRST */
WaitTOF();                     /* wait for Copper to switch */
WaitTOF();                     /* double-wait for interlace safety */
/* NOW safe to free: */
FreeVPortCopLists(&vp);
FreeCprList(myView.LOFCprList);

2. "The Half-Screen Haunt" — Missing WaitTOF After LoadView

Broken:

LoadView(&myView);
/* immediately start drawing into the bitmap */
SetRast(&rp, 0);
RectFill(&rp, 0, 0, 319, 255);   /* drawing while Copper may be in mid-frame */

Why it fails. LoadView returns immediately — the Copper is still executing the previous frame's list partway through the current frame. If you start drawing into the bitmap before the VBlank, your rendering may appear on the old frame, the new frame, or half of each (tearing). The Copper doesn't synchronize with LoadView's return.

Fixed:

LoadView(&myView);
WaitTOF();          /* wait until the new Copper list begins executing */
/* NOW draw into the bitmap — will appear on the next frame */

3. "The Color Clash" — ViewPorts Fighting Over Palette Registers

Broken:

/* ViewPort 0 uses COLOR00COLOR15 at lines 0100 */
/* ViewPort 1 uses COLOR00COLOR15 at lines 100200 */
/* Both call LoadRGB4 with DIFFERENT palettes for COLOR00 */

Why it fails. MrgCop detects the conflict and inserts reload instructions at line 100 — which works if both ViewPorts go through MrgCop together. But if ViewPort 1 calls LoadRGB4 after MrgCop, the color table is updated in RAM but the Copper list still has the old values. ViewPort 0's COLOR00 bleeds into ViewPort 1's region.

Fixed:

/* Load palettes, then rebuild the entire pipeline: */
LoadRGB4(&vp[0], palette0, 16);
LoadRGB4(&vp[1], palette1, 16);
MakeVPort(&view, &vp[0]);
MakeVPort(&view, &vp[1]);
MrgCop(&view);
/* Palette changes are now baked into a fresh Copper list */

Pitfalls

1. Scroll Offsets Are Cumulative

View.DxOffset/View.DyOffset and ViewPort.DxOffset/ViewPort.DyOffset are added together to produce the final scroll value. If you set both to non-zero, the screen scrolls by the sum — which is usually unintentional.

view.DyOffset = 10;      /* "scroll whole display down 10" */
vp.DyOffset  = 20;       /* "scroll this ViewPort down 20" */
/* Result: this ViewPort scrolls by 30. Probably not what you meant. */

2. FreeVPortCopLists vs FreeCprList

FreeVPortCopLists(&vp) frees the intermediate CopLists inside a ViewPort (DspIns, SprIns, ClrIns, UCopIns). FreeCprList(view.LOFCprList) frees the final merged Copper program. You must call both — in the correct order — and you must call FreeVPortCopLists for every ViewPort in the chain:

/* Correct cleanup order: */
for (struct ViewPort *v = view.ViewPort; v; v = v->Next)
    FreeVPortCopLists(v);
FreeCprList(view.LOFCprList);
if (view.SHFCprList) FreeCprList(view.SHFCprList);

3. BMF_DISPLAYABLE Is Required but Not Checked

AllocBitMap() with BMF_DISPLAYABLE ensures the bitmap lives in Chip RAM and is aligned for DMA. The ViewPort system does not validate this — if your bitmap is in Fast RAM, MakeVPort succeeds silently, and the display shows random Chip RAM contents (or nothing).

4. Interleaved Bitmaps Need Correct Modulo

If you allocated the bitmap yourself (not via AllocBitMap()), you must set BitMap->BytesPerRow correctly. For a 320-pixel wide, 5-bitplane screen, that's 320/8 = 40 bytes per row per bitplane. The modulo register (BPL1MOD) is computed from this — incorrect values produce diagonal scrolling or garbage.


When to Use Views vs Direct Copper

graph TD
    A[Need to display graphics?] --> B{Multiple screens<br/>or Workbench integration?}
    B -->|Yes| C{Need screen dragging,<br/>depth arrangement?}
    B -->|No| D{Need mid-screen<br/>mode/palette changes?}
    C -->|Yes| E[Views + Intuition Screens]
    C -->|No| F[Views with single ViewPort]
    D -->|Yes| F
    D -->|No| G[Direct Copper + Direct Bitmap]
    E --> H["Pros: full OS integration,<br/>draggable, system-friendly"]
    F --> I["Pros: OS handles Copper,<br/>mode validation, cleanup"]
    G --> J["Pros: zero OS overhead,<br/>full Copper control,<br/>cycle-exact timing"]
Criterion Views + ViewPort Direct Copper
OS integration Full — works with Intuition screens, Workbench None — you own the hardware
Copper list management Automatic via MakeVPort/MrgCop Manual — you write every WAIT/MOVE
Mode validation HAM+DualPF rejected, OCS limits enforced None — you can program illegal modes (may crash)
Palette management ColorMap + LoadRGB4 handles conflicts Manual — you track COLOR register usage
Split screens Yes — ViewPort chaining Yes — direct WAIT + register rewrites
CPU overhead Moderate — pipeline rebuild on change Zero — static Copper list
Recommended for Applications, utilities, multi-screen setups Games, demos, full-screen effects

Best Practices

  1. Always double-WaitTOF after LoadView — once to switch Copper lists, once for interlace safety
  2. Restore GfxBase->ActiView before freeing your View — never free a View the Copper is still using
  3. Call FreeVPortCopLists for every ViewPort, then FreeCprList on the merged lists — in that order
  4. Use AllocBitMap() with BMF_DISPLAYABLE — don't hand-allocate bitplane memory for Views
  5. Call MakeVPort and MrgCop after ANY ViewPort property change — DWidth, DHeight, DyOffset, ColorMap, or mode flags
  6. Re-MrgCop after adding/removing ViewPorts from the chain — the Copper merge is not incremental
  7. For Intuition screens, use screen->ViewPort — don't create a parallel View hierarchy; let Intuition manage it

Historical Context

The 1985 Landscape

Platform Screen Setup Resolution Switching Multiple Screens
Amiga (Views) Copper list compiled from ViewPort descriptors Mid-screen, per-scanline Yes — draggable, with priority
Macintosh (1984) Fixed framebuffer at $1A7000 One resolution per boot No — single screen
IBM PC (CGA/EGA) BIOS mode set + direct VRAM Full-screen mode change only No
Atari ST Fixed framebuffer + shifter One resolution per boot (mono/color switch) No
Acorn Archimedes (1987) VIDC register block Limited — one mode per frame No

What made Views unique. Every other platform in 1985 had a single framebuffer at a fixed address with a single resolution. If you wanted to change modes, you blanked the screen and reprogrammed the video hardware — a 12 second blackout. The Amiga's View system could switch from 320×200×32-color to 640×200×4-color at a specific scanline with zero blanking — the Copper handled the transition, and the user never saw it. This was the hardware foundation for the Amiga's draggable screens.

The Long Catch-Up — How the Industry Reinvented Compositing

The Amiga shipped with a hardware-composited, multi-resolution desktop in 1985. Then the rest of the industry spent two decades catching up. Here's the timeline:

Year Platform Milestone
1985 Amiga (Workbench 1.0) Hardware-composited desktop: multiple screens at different resolutions, mid-screen Copper mode switches, draggable with priority layering
1985 Windows 1.0 GDI (Graphics Device Interface) — a 2D API for lines, rectangles, text. Pure software rendering to a single framebuffer. No compositing, no acceleration
1990 Windows 3.0 GDI adds printer support, color management, but still software-only. Screen "windows" are stored as occlusion regions — dragging a window leaves a hole that other apps must redraw into
1995 Windows 95 GDI unchanged architecturally. The "desktop" is still a single flat surface; overlapping windows are not independently composited layers
1996 DirectX 1.0 Hardware-accelerated 3D and 2D — but only for full-screen games. The desktop itself does not use it
2001 Mac OS X 10.0 Quartz Compositor — the first modern compositing window manager since the Amiga. Every window is an independent OpenGL-backed texture. Windows can be transparent, shadows are real, no more redraw holes. 16 years after the Amiga
2001 Windows XP GDI+ arrives — anti-aliased 2D, alpha blending, gradient brushes. But the desktop is still software-composited; no GPU acceleration for window management
2005 Mac OS X 10.4 Quartz Composer — a visual programming tool for real-time GPU-accelerated compositions. Core Image brings GPU shader pipelines to the desktop
2006 Windows Vista Desktop Window Manager (DWM) — finally, a hardware-composited desktop on Windows. Every window is a Direct3D texture. GDI apps render to off-screen surfaces; DWM composites with the GPU. 21 years after the Amiga
2008 Compiz / KDE 4 Linux gets GPU-composited desktops. KWin and Compiz use OpenGL to composite windows with effects (wobbly windows, desktop cube)
2011 Qt 5 QPainter gains an OpenGL paint engine. Qt 4 had introduced a software rasterizer (replacing native GDI/X11 backends); Qt 5 made GPU rendering the default path
2013 Wayland 1.0 The compositor becomes the display server. Every frame goes through the GPU. Windows are client-rendered buffers; the compositor arranges them — conceptually identical to the Amiga's Copper merging ViewPorts, 28 years later

The Amiga was 1621 years ahead. Every modern technique on that timeline — independent window layers, GPU compositing, zero-copy buffer passing, per-layer mode switching — was present in AmigaOS 1.0, implemented in hardware. The Copper merged ViewPorts at the scanline level. MrgCop compiled layer transitions. LoadView atomically swapped display lists. The rest of the industry had to wait for 3D GPUs with enough fill rate to composite the desktop in real time before they could replicate what Agnus and Denise did in silicon.

Note

The key difference between the Amiga's approach and modern compositors is where the merge happens. Amiga: the Copper merges ViewPorts into a single hardware display list — the CRT receives one signal, no framebuffer blending. Modern: the GPU renders each window to an off-screen texture, then composites them into a final framebuffer — the display receives the blended result. The Amiga's approach was bandwidth-efficient (no off-screen memory) but inflexible (no transparency between ViewPorts). The modern approach is flexible but memory-hungry. The Amiga got there first, with a fraction of the transistor budget.

Modern Analogies

Amiga Concept Modern Equivalent
View + ViewPort chain Display configuration + CRTC mode list
MakeVPort Shader/GPU command buffer compilation
MrgCop GPU command buffer linking/merging
LoadView vkQueuePresentKHR / SwapBuffers with V-Sync
Copper list Display list / GPU command stream
ViewPort chaining Multi-monitor spanning / MPV (multiple planes within one display)
ColorMap + LoadRGB4 GPU descriptor set with palette binding
UCopIns User-defined GPU commands inserted into system command stream

References

  • NDK39: graphics/view.hstruct View, struct ViewPort, struct RasInfo, struct ColorMap
  • NDK39: graphics/gfxbase.hActiView, copinit, system Copper list pointers
  • ADCD 2.1: InitView, InitVPort, MakeVPort, MrgCop, LoadView, LoadRGB4, FreeVPortCopLists, FreeCprList, GetColorMap, FreeColorMap
  • HRM: Amiga Hardware Reference Manual — Display Construction chapter, Copper chapter
  • See also: GfxBase — Graphics Library Global State — View lifecycle, ActiView, system Copper lists
  • See also: Copper — Coprocessor Instructions and UCopListCMOVE/CWAIT macros, UCopList construction, instruction format
  • See also: Copper Programming — Deep Dive — gradient effects, raster bars, mid-frame palette changes
  • See also: Display Modes — ModeID system, resolution/color-depth combinations, DMA slot budget
  • See also: BitMap — planar allocation, interleaved layout, BMF_DISPLAYABLE
  • See also: Screens — Intuition — OS-managed screen lifecycle built on Views