Copyright (c) Hyperion Entertainment and contributors.

Window Display Preservation

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Preserving the Window Display

The layers library is what allows the display and manipulation of multiple overlapping rectangles, or layers. Intuition uses the layers library to manage its windows, by associating a layer to each window.

Each window is a virtual display. When rendering, the application does not have to worry about the current size or position of its window, and what other windows might be partly or fully obscuring its window. The window's RastPort is the handle to the its virtual display space. Intuition and graphics library rendering calls will recognize that this RastPort belongs to a layer, and act accordingly.

As windows are moved, resized, rearranged, opened, or closed, the on-screen representation changes. When part of a window which was visible now needs to appear in a new location, the layers library will move that imagery without involving the application. However, when part of a window that was previously obscured is revealed, or when a window is made larger, the imagery for the newly-visible part of the window needs to be redrawn. Intuition, through layers, offers three choices for how this is managed, trading off speed, memory usage, and application complexity.

  • The most basic type of window is called Simple Refresh. When any graphics operation takes place in this kind of window, the visible parts are updated, but rendering to the obscured parts is discarded. When the window arrangement changes to reveal a previously obscured part of such a window, the application must refresh that area.
  • Alternately, a window may be made Smart Refresh, which means that when rendering occurs, the system will not only update the visible parts of the window, but it will maintain the obscured parts as well, by using off-screen buffers. This means that when an obscured part of the window is revealed, the system will restore the imagery that belongs there. The application needs only to refresh parts of the window that appear when the window is made bigger. Smart Refresh windows use more memory than Simple Refresh windows (for the storage of obscured areas), but they are faster.
  • The third kind of window is called SuperBitMap. In such a window, the system can refresh the window even when it is sized bigger. For this to work, the application must store a complete bitmap for the window's maximum size. Such a window is more work to manage, and uses yet more memory. SuperBitMap windows are used less often than the other two types.

Intuition helps your application manage window refresh. First, Intuition will take care of redrawing the window border and any system and application gadgets in the window. Your application never has to worry about that. Second, Intuition will notify your application when it needs to refresh its window (by sending the IDCMP_REFRESHWINDOW event). Third, Intuition provides functions that restrict your rendering to the newly-revealed (damaged) areas only, which speeds up your refresh rendering and makes it look cleaner.

The Intuition, layers, and graphics libraries work together to make rendering into and managing windows easy. You obtain your windows through Intuition, which uses the Layers library to manage the overlapping, resizing, and re-positioning of the window layers. The layers library is responsible for identifying the areas of each window that are visible, obscured but preserved off-screen, or obscured and not preserved. The rendering functions in the graphics library and Intuition library know how to render into the multiple areas that layers library establishes.

Note that you may not directly manipulate layers on an Intuition screen. You cannot create your own layers on an Intuition screen, nor can you use the layers movement, sizing, or arrangement functions on Intuition windows. Use the corresponding Intuition calls instead. Some other Layers library calls (such as the locking calls) are sometimes used on Intuition screens and windows.

Damage Regions

The layers library and Intuition maintain a damage region for each window, which is the part of the window whose imagery is in need of repair, or refreshing. Several things can add areas of the window to the damage region:

  • Revealing an obscured part of a Simple Refresh window adds that area to the damage region
  • Sizing a Simple or Smart Refresh window bigger along either axis adds the new area to the damage region
  • Resizing a Simple or Smart Refresh window (smaller or bigger) adds the old and new border areas, and the areas occupied by certain gadgets (those whose position or size depend on window size) to the damage region.

Refreshing Intuition Windows

When the user or an application performs an Intuition operation which causes damage to a window, Intuition notifies that window's application. It does this by sending a message of the class IDCMP_REFRESHWINDOW to that window's IDCMP.

In response to this message, your application should update the damaged areas. Rendering proceeds faster and looks cleaner if it is restricted to the damaged areas only. The BeginRefresh()/EndRefresh() pair achieve that. The application should call BeginRefresh() for the window, and then do its rendering. Any rendering that would have gone into undamaged areas of the window is automatically discarded; only the area in need of repair is affected. Finally, the application should call EndRefresh(), which removes the restriction on rendering, and informs the system that the damage region has been dealt with. Even if your application intends to do no rendering, it must at least call BeginRefresh()/EndRefresh(), to inform the system that the damage region is no longer needed. If your application never needs to render in response to a refresh event, it can avoid having to call BeginRefresh()/EndRefresh() by setting the WFLG_NOCAREREFRESH flag or the WA_NoCareRefresh tag in the OpenWindowTagList() call.

Note that by the time that your application receives notification that refresh is needed, Intuition will have already refreshed your window's border and all gadgets in the window, as needed. Thus, it is unnecessary to use any of the gadget-refreshing functions in response to an IDCMP_REFRESHWINDOW event.

Operations performed between the BeginRefresh()/EndRefresh() pair should be restricted to simple rendering. All of the rendering functions in Intuition library and Graphics library are safe. Avoid RefreshGList() or RefreshGadgets(), or you risk deadlocking the computer. Avoid calls that may lock the LayerInfo or get complicated in Intuition, since BeginRefresh() leaves the window's layer or layers locked. Avoid AutoRequest() and EasyRequest(), and therefore all direct or indirect disk related DOS calls. See the Intuition Gadgets section for more information on gadget restrictions with BeginRefresh() and EndRefresh().

Simple Refresh

For a Simple Refresh window, only those pixels actually on-screen are maintained by the system. When part of a Simple Refresh window is obscured, the imagery that was there is lost. As well, any rendering into obscured portions of such a window is discarded.

When part of the window is newly revealed (either because the window was just made larger, or because that part used to be obscured by another window), the application must refresh any rendering it wishes to appear into that part. The application will learn that refresh is needed because Intuition sends an IDCMP_REFRESHWINDOW event.

Smart Refresh

If a window is of the Smart Refresh type, then the system will not only preserve those pixels which are actually on-screen, but it will save all obscured pixels that are within the current window's size. The system will refresh those parts of the window revealed by changes in the overlapping with other windows on the screen, without involving the application. However, any part of the window revealed through the sizing of the window must be redrawn by the application. Again, Intuition will notify the application through the IDCMP_REFRESHWINDOW event.

Because the obscured areas are kept in off-screen buffers, Smart Refresh windows are refreshed faster than Simple Refresh windows are, and often without involving the application. Of course, for the same reason, they use more display memory.

SuperBitMap Refresh

The SuperBitMap refresh type allows the application to provide and maintain bitmap memory for graphics in the window. The bitmap can be any size as long as the window sizing limits respect the maximum size of the bitmap.

SuperBitMap windows have their own memory for maintaining all obscured parts of the window up to the size of the defined bitmap, including those parts outside of the current window. Intuition will update all parts of the window that are revealed through changes in sizing and changes in window overlapping. The application never needs to redraw portions of the window that were revealed by sizing or positioning windows in the screen.

SuperBitMap windows require the application to allocate a bitmap for use as off-screen memory, instead of using Intuition managed buffers. This bitmap must be as large as, or larger than, the inner window's maximum dimensions (that is, the window's outside dimensions less the border sizes).

SuperBitMap windows are almost always WFLG_GIMMEZEROZERO, which renders the borders and system gadgets in a separate bitmap. If the application wishes to create a SuperBitMap window that is not GimmeZeroZero, it must make the window borderless with no system gadgets, so that no border imagery is rendered by Intuition into the application's bitmap.

Intuition Refresh Events

When using a Simple Refresh or a Smart Refresh windows, the program may receive refresh events, informing it to update the display. See the above discussion for information on when refresh events are sent.

A message of the class IDCMP_REFRESHWINDOW arrives at the IDCMP, informing the program of the need to update the display. The program must take some action when it receives a refresh event, even if it is just the acceptable minimum action described below.

On receiving a refresh event, BeginRefresh() must be called, then the program should redraw its display, and, finally, call EndRefresh(). The minimum required action is to call the BeginRefresh()/EndRefresh() pair. This allows Intuition and the Layers library keep things sorted and organized.

Optimized Window Refreshing

Bracketing the display updating in the BeginRefresh()/EndRefresh() pair automatically restricts all rendering to the "damaged" areas.

VOID BeginRefresh( struct Window *window );
VOID EndRefresh  ( struct Window *window, LONG complete );

These functions makes sure that refreshing is done in the most efficient way, only redrawing those portions of the window that really need to be redrawn. The rest of the rendering commands are discarded.

Operations performed between the BeginRefresh()/EndRefresh() pair should be restricted to simple rendering. All of the rendering functions in Intuition library and Graphics library are safe. Calls to RefreshGadgets() are not permitted. Avoid calls that may lock the LayerInfo, or get complicated in Intuition, since BeginRefresh() leaves the window's layer or layers locked. Avoid AutoRequest(), and therefore all direct or indirect disk related DOS calls. See Intuition Gadgets for more information on gadget restrictions with BeginRefresh()/EndRefresh().

Certain applications do not need to receive refresh events, and can avoid having to call BeginRefresh() and EndRefresh() by setting the WFLG_NOCAREREFRESH flag or the WA_NoCareRefresh tag in the OpenWindowTagList() call.

The EndRefresh() function takes a boolean value as an argument (complete in the prototype above). This value determines whether refreshing is completely finished. When set to FALSE, further refreshing may be performed between subsequent BeginRefresh()/ EndRefresh() pairs. Set the boolean to TRUE for the last call to EndRefresh().

It is critical that applications performing multiple BeginRefresh() and EndRefresh() pairs using EndRefresh(win,FALSE) hold layers locked through the entire process. The layer lock may only be released after the final call to EndRefresh(win,TRUE). See Layers Library for more details.

The procedures outlined in this section take care of refreshing what is inside the window. Another function named RefreshWindowFrame() refreshes window borders, including the title region and gadgets:

VOID RefreshWindowFrame( struct Window *window );

Applications can use this function to update window borders after overwriting them with graphics.

Setting up a SuperBitMap Window

SuperBitMap windows are created by setting the WFLG_SUPER_BITMAP flag, or by specifying the WA_SuperBitMap tag in the OpenWindowTagList() call. A pointer to an allocated and initialized BitMap structure must be provided.

A SuperBitMap window requires the application to allocate and initialize its own bitmap. This entails allocating a BitMap structure, initializing the structure and allocating memory for the bit planes.

Allocate a BitMap structure with the Exec AllocMem() function. Then use the graphics function InitBitMap() to initialize the BitMap structure:

VOID InitBitMap( struct BitMap *bitMap, LONG depth, LONG width, LONG height );

InitBitMap() fills in fields in the BitMap structure describing how a linear memory area is organized as a series of one or more rectangular bit-planes.

Once you have allocated and initialized the BitMap structure, use the graphics library function AllocRaster() to allocate the memory space for all the bit planes.

PLANEPTR AllocRaster( ULONG width, ULONG height );

The example listed in the next section shows how to allocate a BitMap structure, initialize it with InitBitMap() and use AllocRaster() function to set up memory for the bitplanes.

Graphics and Layers Functions for SuperBitMap Windows

The portion of the bitmap showing within a SuperBitMap window is controlled by the application. Initially, the window shows the bitmap starting from its origin (0,0) and clipped to fit within the window layer. The visible portion of the bitmap can be scrolled around within the window using the layers library ScrollLayer() function:

VOID ScrollLayer(LONG unused, struct Layer *layer, LONG dx, LONG dy)

Pass this function a pointer to the window's layer in layer and the scroll offsets in dx and dy. (A pointer to the window's layer can be obtained from Window.RPort->Layer.)

When rendering operations are performed in a SuperBitMap window, any rendering that falls outside window boundaries is done in the application's bitmap. Rendering that falls within window bounds is done in the screen's bitmap. Before performing an operation such as a save on the application bitmap, the graphics library function SyncSBitMap() should be called:

VOID SyncSBitMap(struct Layer *layer)

Pass this function a pointer to the window's layer. SyncSBitMap() copies the window contents to the corresponding part of the application bitmap, bringing it up to date. (If no rendering operations have been performed this call is not necessary.)

Similarly, after making any changes to the application bitmap such as loading a new one, the window's layer should be locked and the CopySBitMap() function should be called.

VOID CopySBitMap(struct Layer *)

This function copies the new information in the appropriate area of the underlying bitmap to the window's layer.

For more information about bitmaps and layers, see the Graphics Primitives and Layers Library. Also see the <graphics/clip.h>, <graphics/gfx.h>, <graphics/layers.h > files in the SDK.

SuperBitMap Window Example

This example shows how to implement a superbitmap, and uses a host of Intuition facilities. Further reading of other Intuition, BOOPSI and graphics chapters may be required for a complete understanding of this example.

 **  lines.c -- Window SuperBitMap example.
 **  gcc -o lines lines.c
#include <dos/dos.h>
#include <intuition/icclass.h>
#include <classes/window.h>
#include <gadgets/scroller.h>
#include <gadgets/layout.h>
#include <proto/intuition.h>
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/layers.h>
#include <proto/graphics.h>
#define WIDTH_SUPER (800)
#define HEIGHT_SUPER (600)
#define MINWIDTH (150)
#define MINHEIGHT (150)
#define LAYERXOFFSET(x) (x->RPort->Layer->Scroll_X)
#define LAYERYOFFSET(x) (x->RPort->Layer->Scroll_Y)
    WID_MAIN = 0,
    OID_MAIN = 0,
struct IntuitionIFace *IIntuition;
struct GraphicsIFace *IGraphics;
struct LayersIFace *ILayers;
static struct Window *windows[WID_LAST];
static Object *objects[OID_LAST];
static struct Hook idcmphook;
/* Prototypes for our functions */
void initBorderProps(struct Screen *screen);
void doNewSize();
void doDrawStuff();
void doMsgLoop();
void superWindow(struct Screen *screen);
void slideBitMap(struct Window *win, int16 dX, int16 dY);
int main()
    struct Screen *screen;
    struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
    IIntuition = (struct IntuitionIFace *) IExec->GetInterface(IntuitionBase,
        "main", 1, NULL);
    struct Library *GfxBase = IExec->OpenLibrary("graphics.library", 50);
    IGraphics = (struct GraphicsIFace *) IExec->GetInterface(GfxBase, "main", 1, NULL);
    struct Library *LayersBase = IExec->OpenLibrary("layers.library", 50);
    ILayers = (struct LayersIFace *) IExec->GetInterface(LayersBase, "main", 1, NULL);
    if (IIntuition != NULL && IGraphics != NULL && ILayers != NULL) {
        if ((screen = IIntuition->LockPubScreen(NULL)) != NULL) {
            IIntuition->UnlockPubScreen(NULL, screen);
    IExec->DropInterface((struct Interface *) ILayers);
    IExec->DropInterface((struct Interface *) IGraphics);
    IExec->DropInterface((struct Interface *) IIntuition);
    return 0;
void IDCMPHook(struct Hook *hook, Object *object, struct IntuiMessage *msg)
    if (msg->Class == IDCMP_IDCMPUPDATE) {
        int16 dX = 0;
        int16 dY = 0;
        int32 pos;
        struct Window *win = windows[WID_MAIN];
        if (win != NULL) {
            uint32 gadgetid = IUtility->GetTagData(GA_ID, 0, (struct TagItem *) msg->IAddress);
            if (IIntuition->GetAttr(SCROLLER_Top, objects[gadgetid], (uint32 *) &pos)) {
                switch (gadgetid) {
                case OID_VPROP:
                    dY = pos - LAYERYOFFSET(win);
                case OID_HPROP:
                    dX = pos - LAYERXOFFSET(win);
                if (dX || dY) {
                    slideBitMap(win, dX, dY);
void superWindow(struct Screen *myscreen)
    struct BitMap *bigBitMap = IGraphics->AllocBitMapTags(WIDTH_SUPER, HEIGHT_SUPER, myscreen->BitMap.Depth,
            BMATags_Friend, myscreen->BitMap, BMATags_Displayable, TRUE, BMATags_Clear, 0, TAG_END);
    if (bigBitMap != NULL) {
        struct MsgPort *AppPort = IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE);
        if ( AppPort != NULL )
            idcmphook.h_Entry = (uint32 (*)())IDCMPHook;
            idcmphook.h_SubEntry = NULL;
            if ((objects[OID_MAIN] = IIntuition->NewObject(NULL, "window.class",
                WA_ScreenTitle, "SuperBitmap",
                WA_Title, "Lines",
                WA_Activate, TRUE,
                WA_DepthGadget, TRUE,
                WA_DragBar, TRUE,
                WA_CloseGadget, TRUE,
                WA_SizeGadget, TRUE,
                WA_Top, 10,
                WA_Left, 10,
                WA_MinWidth, MINWIDTH,
                WA_MinHeight, MINHEIGHT,
                WA_Width, MINWIDTH,
                WA_Height, MINHEIGHT,
                WA_PubScreen, myscreen, WA_SuperBitMap, bigBitMap,
                WA_NoCareRefresh, TRUE,
                WA_GimmeZeroZero, TRUE,     
                WINDOW_IDCMPHook, &idcmphook,
                WINDOW_HorizProp, 1,
                WINDOW_VertProp, 1,
                WINDOW_AppPort, AppPort, TAG_DONE)) != NULL) {
                //  Open the window.
                if (windows[WID_MAIN] = (struct Window *) IIntuition->IDoMethod(objects[OID_MAIN], WM_OPEN)) {   
                    /* adjust props to represent portion visible */
                    /* process the window, return on IDCMP_CLOSEWINDOW */
            IExec->FreeSysObject(ASOT_PORT, AppPort);
void slideBitMap(struct Window *win, int16 Dx, int16 Dy)
    ILayers->ScrollLayer(0, win->RPort->Layer, Dx, Dy);
** Update the prop gadgets and bitmap positioning when the size changes.
void doNewSize()
    struct Window *win = windows[WID_MAIN];
    if (win != NULL) {
        IIntuition->GetAttr(WINDOW_HorizObject, objects[OID_MAIN], (uint32 *) &objects[OID_HPROP]);
        if (objects[OID_HPROP]) {       
            if (win->GZZWidth >= WIDTH_SUPER)
                slideBitMap(win, -LAYERXOFFSET(win), 0);
            IIntuition->RefreshSetGadgetAttrs((APTR) objects[OID_HPROP], win, NULL,
                GA_ID, OID_HPROP,
                SCROLLER_Top, LAYERXOFFSET(win),
                SCROLLER_Total, WIDTH_SUPER,
                SCROLLER_Visible, win->GZZWidth,
        IIntuition->GetAttr(WINDOW_VertObject, objects[OID_MAIN], (uint32 *) &objects[OID_VPROP]);
        if (objects[OID_VPROP]) {
            if (win->GZZHeight >= HEIGHT_SUPER)
                slideBitMap(win, 0, -LAYERYOFFSET(win));
            IIntuition->RefreshSetGadgetAttrs((APTR) objects[OID_VPROP], win, NULL,
                GA_ID, OID_VPROP,
                SCROLLER_Top, LAYERYOFFSET(win),
                SCROLLER_Total, HEIGHT_SUPER,
                SCROLLER_Visible, win->GZZHeight,
int16 RangeRand(uint32 maxValue)
    static struct RandomState range = {0xFFFF, 0};
    return IUtility->Random(&range) % maxValue;
void doDrawStuff()
    struct Window *win = windows[WID_MAIN];
    if (win != NULL) {
        /* clear the bitplanes */
        IGraphics->SetRast(win->RPort, 0);
        IGraphics->SetDrMd(win->RPort, JAM1);
        int16 x1, y1, x2, y2;
        int16 pen, ncolors, deltx, delty;
        ncolors = 1 << win->WScreen->BitMap.Depth;
        deltx = RangeRand(6) + 2;
        delty = RangeRand(6) + 2;
        pen = RangeRand(ncolors - 1) + 1;
        IGraphics->SetAPen(win->RPort, pen);
        for (x1 = 0, y1 = 0, x2 = WIDTH_SUPER - 1, y2 = HEIGHT_SUPER - 1;
            x1 < WIDTH_SUPER; x1 += deltx, x2 -= deltx) {
            IGraphics->Move(win->RPort, x1, y1);
            IGraphics->Draw(win->RPort, x2, y2);
        pen = RangeRand(ncolors - 1) + 1;
        IGraphics->SetAPen(win->RPort, pen);
        for (x1 = 0, y1 = 0, x2 = WIDTH_SUPER - 1, y2 = HEIGHT_SUPER - 1;
             y1 < HEIGHT_SUPER; y1 += delty, y2 -= delty) {
            IGraphics->Move(win->RPort, x1, y1);
            IGraphics->Draw(win->RPort, x2, y2);
void doMsgLoop()
    uint32 signal = 0;
    BOOL done = FALSE;
    // Obtain the window wait signal mask.
    IIntuition->GetAttr(WINDOW_SigMask, objects[OID_MAIN], &signal);
    // Input Event Loop
    while (!done) {
        uint32 wait = IExec->Wait(signal | SIGBREAKF_CTRL_C);
        if ( wait & SIGBREAKF_CTRL_C ) {
            done = TRUE;
        if ( wait & signal ) {
            uint32 result = WMHI_LASTMSG;
            int16 code = 0;
            while ((result = IIntuition->IDoMethod(objects[OID_MAIN], WM_HANDLEINPUT, &code)) != WMHI_LASTMSG) {
                switch (result & WMHI_CLASSMASK) {
                case WMHI_NEWSIZE:
                case WMHI_CLOSEWINDOW:
                    windows[WID_MAIN] = NULL;
                    done = TRUE;
                case WMHI_ICONIFY:
                    IIntuition->IDoMethod(objects[OID_MAIN], WM_ICONIFY);
                    windows[WID_MAIN] = NULL;
                case WMHI_UNICONIFY:
                    windows[WID_MAIN] = (struct Window *) IIntuition->IDoMethod(objects[OID_MAIN], WM_OPEN);