Copyright (c) 2012-2016 Hyperion Entertainment and contributors.

Intuition Screens

From AmigaOS Documentation Wiki
Jump to: navigation, search

Intuition Screens

Intuition screens are the basis of any display Intuition can make. Screens determine the fundamental characteristics of the display such as the resolution and palette and they set up the environment for multiple, overlapping windows that makes it possible for each application to have its own separate visual context. This section shows how to use existing screens and how to create new screens.

Classic Intuition Screens

Intuition screens have evolved since they were closely tied to the Classic Amiga's hardware chip set. See Classic Intuition Screens for more information on the now obsolete API that was used to work with screens.

Types of Screens

Screens are important because they determine the basic resolution and maximum number of colors in the display. Once a screen is set up, these attributes cannot be changed so any graphics work done on a given screen is restricted to that screen's resolution and number of colors. Hence, the type of screen used is a basic design decision.

With Intuition screens, a video display can be created in any one of the many Amiga display modes. The basic parameters of the video display such as resolution, total size, frame rate, genlock compatibility, support of screen movement and number of colors are defined by these modes.

Many display modes are available. All these display modes, including the specialized modes, are integrated through the graphics library display database. See Graphics Primitives for a complete list of all Amiga display modes.

Multiple Screens

All Intuition display objects (such as windows and menus) take graphical characteristics from the screen. These objects are restricted to the same resolution and maximum number of colors as the screen they operate in. Other characteristics such as the palette, pens and fonts are inherited from the screen (but may be changed on a case by case basis).

This is not too much of a restriction because the Amiga can maintain multiple screens in memory at the same time. In other words, one application can use a high resolution screen (with 16 colors) while another application uses a low resolution screen (with 32 colors) at the same time. Screens typically take up the entire viewing area so only one is usually visible. But screens can be moved up and down or rearranged allowing the user (or application) to move between screens easily.

Public Screens and Custom Screens

An application may choose to use an existing screen or to create its own screen. For instance, the normal Amiga startup process opens the Workbench screen (Workbench is the Amiga's default user interface). Any application is free to use the Workbench screen instead of opening a new one. Screens that can be shared this way are called public screens.

Public screens allow applications to open windows on them. Any screen may be set up as a public screen so that other applications may use it.

The use of an existing public screen, like the Workbench screen, requires little effort by the application and does not use up any memory. However, using Workbench or another existing public screen means some flexibility is lost; the resolution, maximum number of colors and other attributes are already set. If the application cannot function under these limitations, it may open its own custom screen.

Custom screens allow for complete control of the display space so an application can get exactly the kind of display it wants. However, since creating a new, custom screen uses up memory, they should only be used when there are no suitable public screens available.

Owners of a custom screen can keep their screen private, or they may allow other applications to share their screen by registering the screen with the operating system as a public screen. See the section on public screens for more about public screens and Workbench.

Screen Components

Screens have very little visual impact, they simply provide a resolution specific area to place other objects such as windows and menus. Screens have no borders. Only the title bar marks the screen limits (specifying the left and top edges, and the width of the screen), and the title bar may be hidden, or obscured by graphics or windows.

The title bar also serves as the menu bar when the user presses the menu button on the mouse. The menu bar area is shared by all applications operating within the screen.

Within the title bar, there are two gadgets: a screen drag gadget and a depth-arrangement gadget. The screen drag gadget allows the screen to be moved up and down. The depth-arrangement gadget allows the screen to be placed in front or behind all other screens.

An Intuition Screen (Workbench)

Screens are always rectangular, and the areas at the sides and bottom of the display that are not within the screen's limits are filled with the background color. The area above all visible screens is filled with the background color of the highest screen. These areas surrounding the screen (normally unused) are known as the overscan area. The Amiga display system allows the overscan area to be used for graphics under special circumstances (see the section on Overscan and the Display Clip).

Screen Attributes

The sections above discuss only the basic functions and screen types that Intuition programmers need to understand to create a custom screen. Intuition supports an astonishing number of additional display features and options. In this section and the sections to follow, the finer points of screen attributes and the functions that control them are presented.

Screen attributes are specified using the tag item scheme described Utility Library. Therefore, the screen attributes are listed here by tag values.

SA_ErrorCode
Extended error code. Data is a pointer to a long which will contain the error code on return if OpenScreenTagList() returns NULL. The error codes are described above.
SA_Left, SA_Top
Initial screen position (left edge and top edge). Data is a long, signed value. Offsets are relative to the text overscan rectangle.
If SA_Left is not specified and a NewScreen structure is not passed in the OpenScreenTags/TagList() call and SA_Width is not specified or is specified as STDSCREENWIDTH, then the left edge of the screen will default to the left edge of the actual display clip of the screen. If the other conditions are met but some explicit SA_Width is specified, then the left edge defaults to zero (text overscan rectangle left edge). Likewise, the top edge may, independent of the left edge value, default to zero or to the top edge of the actual display clip. If SA_Top is not specified and a NewScreen structure is not passed in the OpenScreenTags/TagList() call and SA_Height is not specified or specified as STDSCREENHEIGHT, then the top edge of the screen will default to the top edge of the actual display clip of the screen. If the other conditions are met but some explicit SA_Height is specified, then the top edge defaults to zero (text overscan rectangle top edge).
When opening a full sized overscan screen, SA_Left should be set to the MinX value of the display clip Rectangle used for the screen and SA_Top should be set to the MinY value of the display clip. This may be taken from the defaults, as explained above, or explicitly set by the application. See the section below on "Overscan and the Display Clip" and the OpenScreen() Autodoc for more details.
If your screen is larger than your display clip, you may wish to set the SA_Left and SA_Top to values less than your display clip MinX and MinY in order to center a large screen on a smaller display. For an example of how to open a centered overscan screen, see "module/screen.c" in the IFF Source Code.
SA_Width, SA_Height
Screen dimensions. Data is a long, unsigned value. These may be larger, smaller or the same as the dimensions of the display clip Rectangle. The use of STDSCREENWIDTH and STDSCREENHEIGHT will make the screen size equal to the display clip size.
To calculate the width of the display clip Rectangle, subtract the MinX value from the MaxX value plus one. Similarly, the height of the display clip may be calculated by subtracting the MinY value from the MaxY value plus one.
SA_Depth
Screen bitmap depth. Data is a long, unsigned value. The depth of the screen determines the number of available colors. See the "Graphics Primitives" for more information on hardware limitations of the display. Do not set the depth to a value greater than that supported by the specific display mode. This information is available to the application through the graphics library display database. The default is one bitplane.
SA_DisplayID
Extended display mode key for the screen. Data is a long, unsigned value. By using DisplayIDs and the display database, applications can open a screen in any display mode available on a user's system, including PAL and NTSC modes. See the discussion of the display database in Display Database and the include file <graphics/displayinfo.h> for more information.
SA_Pens
Pen specification for the screen. Data is a pointer to a UWORD array terminated with  0, as found in the DrawInfo structure. Specifying the SA_Pens tag informs the system that the application is prepared to handle a screen rendered with the new 3D look of Intuition. See the section below on "DrawInfo and the 3D Look". Omitting this tag produces a screen with a flat look, but whose color usage is more backwards compatible.
SA_DetailPen
Detail pen for the screen. Data is a long, unsigned value. Used for rendering details in the screen title bar and menus. Use SA_Pens beginning with V36 for more control of pen specification. If SA_Pens is not specified, the screen will not get the new 3D look of Intuition. Instead this value will be used as the detail pen.
SA_BlockPen
Block pen for the screen. Data is a long, unsigned value. Used for rendering block fills in the screen title bar and menus. Use SA_Pens for more control of pen specification. If SA_Pens is not specified, the screen will not get the new 3D look and this value will be used as the block pen.
SA_Title
Default screen title. Data is a pointer to a character string. This is the title displayed when the active window has no screen title or when no window is active on the screen.
SA_Colors
Specifies initial screen palette colors. Data is a pointer to an array of ColorSpec structures, terminated by a ColorSpec structure with ColorIndex = -1. Screen colors may be changed after the screen is opened with the graphics library functions SetRGB4() and LoadRGB4(). ColorSpec colors are right-justified, four bits per gun.
SA_FullPalette
Initialize color table to entire preferences palette (32 colors), rather than the subset from V34 and earlier, namely pens 0-3, 17-19, with remaining palette as returned by GetColorMap(). Data is a boolean value (use TRUE to set the flag). Defaults to FALSE.
SA_Font
Data is a pointer to a TextAttr structure (defined in <graphics/text.h>) which specifies the font, size and style to use for the screen. Equivalent to NewScreen.Font.
SA_SysFont
Alternative to SA_Font. Selects one of the preferences system fonts. Data is a long, unsigned value, with the following values defined:
0 Open screen with the user's preferred fixed width font (the default).
1 Open screen with the user's preferred font, which may be proportional.
The Workbench screen is opened with {SA_SysFont, 1}. The following table summarizes how the font selected at OpenScreen() time effects subsequent text operations in screens and windows.
Intuition Font Selection Chart
What you tell OpenScreen() Screen font Window.RPort font
A NewScreen.Font = myfont myfont myfont
B NewScreen.Font = NULL GfxBase->DefaultFont GfxBase->DefaultFont
C {SA_Font, myfont} myfont myfont
D {SA_SysFont, 0} GfxBase->DefaultFont GfxBase->DefaultFont
E {SA_SysFont, 1} Font Prefs Screen text GfxBase->DefaultFont
Notes:
  • A and B are the options that existed in V34 and earlier OS versions.
  • C and D are tags equivalent to A and B respectively.
  • E is an option for V36 and higher. The Workbench screen uses this option.
  • For myfont, any font may be used including a proportional one. This is true under all releases of the OS.
  • GfxBase->DefaultFont is always monospace. (This is the "System Default Text" from Font Preferences.)
  • "Font Prefs Screen" text (the "Screen Text" choice from Font Preferences) can be monospace or proportional.
The screen's font may not legally be changed after a screen is opened. The menu bar, window titles, menu items, and the contents of a string gadget all use the screen's font. The font used for menu items can be overridden in the menu item's IntuiText structure. Under V36 and higher, the font used in a string gadget can be overridden through the StringExtend structure. The font of the menu bar and window titles cannot be overridden.
The Window.RPort font shown above is the initial font that Intuition sets in your window's RastPort. It is legal to change that subsequently with SetFont(). IntuiText rendered into a window (either through PrintIText() or as a gadget's GadgetText) defaults to the window's RastPort font, but can be overridden using its ITextFont field. Text rendered with the graphics library call Text() uses the window's RastPort font.
SA_Type
Equivalent to the SCREENTYPE bits of the NewScreen.Type field. Data is a long, unsigned value which may be set to either CUSTOMSCREEN or PUBLICSCREEN (WBENCHSCREEN is reserved for system use). See the tags SA_BitMap, SA_Behind, SA_Quiet, SA_ShowTitle and SA_AutoScroll for the other attributes of the NewScreen.Type field.
SA_BitMap
Use a custom bitmap for this screen. Data is a pointer to a BitMap structure. This tag is equivalent to NewScreen.CustomBitMap and implies the CUSTOMBITMAP flag of the NewScreen.Type field. The application is responsible for allocating and freeing the screen's bitmap.
SA_Behind
Open this screen behind all other screens in the system. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SCREENBEHIND flag of the NewScreen.Type field.
SA_Quiet
Disable Intuition rendering into screen. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SCREENQUIET flag of the NewScreen.Type field. The screen will have no visible title bar or gadgets, but dragging and depth arrangement still function. In order to completely prevent Intuition from rendering into the screen, menu operations must be disabled for each window in the screen using WFLG_RMBTRAP.
SA_ShowTitle
Setting this flag places the screen's title bar in front of any backdrop windows that are opened on the screen. Data is a boolean value (TRUE to set flag). This tag is equivalent to the SHOWTITLE flag of the NewScreen.Type field. The title bar of the screen is always displayed behind any non-backdrop windows on that screen. This attribute can be changed after the screen is open with the ShowTitle() function.
SA_AutoScroll
Setting this flag will enable autoscroll for this screen when it is the active screen. (Currently, the screen may only be made active by activating a window in that screen either under user or application control.) Data is a boolean value (TRUE to set flag). This tag is equivalent to the AUTOSCROLL flag of the NewScreen.Type field.
Autoscroll means that screens larger than the visible display will automatically scroll when the user moves the mouse to the edge of the screen. Without this tag, the user moves the screen either by using the screen drag bar, or by pressing the mouse select button anywhere within the screen while holding down the left Amiga key and moving the mouse.
SA_PubName
Presence of this tag means that the screen is to be a public screen. Data is a pointer to a string. The string is the name of the public screen which is used by other applications to find the screen. This tag is order dependent, specify before SA_PubSig and SA_PubTask.
SA_PubSig, SA_PubTask
Task ID (returned by FindTask()) and signal for notification that the last window has closed on a public screen. Data for SA_PubSig is a long, unsigned value. Data for SA_PubTask is a pointer to a Task structure. These two tags are order dependent, and must be specified after the tag SA_PubName.
SA_Overscan
Set to one of the OSCAN_ specifiers to use a system standard overscan display clip and screen dimensions (unless otherwise specified). Data is a long, unsigned value. Do not specify this tag and SA_DClip. SA_Overscan is used to get one of the standard overscan dimensions, while SA_DClip is for custom dimensions. If a display clip is not specified with either SA_Overscan or SA_DClip, the display clip defaults to OSCAN_TEXT. See the section below on "Overscan and the Display Clip" for more information.
SA_DClip
Custom display clip specification. Data is a pointer to a Rectangle structure that defines the screen display clip region.

DrawInfo and the 3D Look

Whenever a new screen is created, Intuition also creates an auxiliary data structure called a DrawInfo. The DrawInfo structure provides information Intuition uses to support the 3D look and specifies graphical information for applications that use the screen. The information includes such items as aspect ratio (resolution), font, number of colors and drawing pens.

struct DrawInfo
{
    UWORD       dri_Version;    /* will be  DRI_VERSION                 */
    UWORD       dri_NumPens;    /* guaranteed to be &gt;= numDrIPens       */
    UWORD       *dri_Pens;      /* pointer to pen array                 */
 
    struct TextFont     *dri_Font;  /* screen default font              */
    UWORD       dri_Depth;          /* (initial) depth of screen bitmap */
 
    struct {    /* from DisplayInfo database for initial display mode   */
        UWORD   X;
        UWORD   Y;
    }           dri_Resolution;
 
    ULONG       dri_Flags;              /* defined below                */
    ULONG       dri_Reserved[7];        /* avoid recompilation ;^)      */
};

Before an application uses fields in the DrawInfo structure, it should check the version of the structure to ensure that all fields are available. If the field dri_Version is greater than or equal to the constant DRI_VERSION that the application was compiled with, it can be assured that all fields in DrawInfo that it knows about are being supported by Intuition.

The Pen Specification in DrawInfo

The drawing pen specification in DrawInfo.dri_Pens allows applications to use appropriate colors for graphic operations such as drawing text, shading 3D objects and filling items selected by the user.

Intuition has two default sets of pens, one for multi-bitplane screens and one for single bitplane screens. In addition, there is a special compatibility mode for screens that do not specify the SA_Pens tag.

3D Look
The is the full 3D look as found by default on the Workbench screen. Objects are drawn so that light appears to come from the upper left of the screen with shadows cast to the lower right giving them a three-dimensional look.
Monochrome Look
It is impossible to produce the full 3D look in a single bitplane (two color) screen. Intuition provides a fallback pen specification that is used in monochrome screens with no loss of information.
Compatible Look
Custom screens that do not provide the SA_Pens tag are assumed to have no knowledge of the pen array. They are rendered in a special version of the monochrome new look, which uses the screen's DetailPen and BlockPen to get its colors. This is provided for compatibility with V34 and older versions of the operating system.

It is very easy for an application to use the default pen specification. Simply specify an empty pen specification (in C, {~0}), and Intuition will fill in all of the values with defaults appropriate for the screen. This technique is demonstrated in the first two examples listed earlier in this article.

For certain applications, a custom pen specification is required. A custom pen specification is set up when the screen is opened by using the SA_Pens tag and a pointer to a pen array. Currently, Intuition uses nine pens to support the 3D look. The application can specify all of these, or only a few pens and Intuition will fill in the rest. Intuition will only fill in pens that are past the end of those specified by the application, there is no facility for using default values for "leading" pens (those at the beginning of the array) without using the defaults for the rest of the pens.

Using the pen specification of an existing public screen is a bit more involved. First, the application must get a pointer to the screen structure of the public screen using the LockPubScreen() call. A copy of the screen's DrawInfo structure may then be obtained by calling GetScreenDrawInfo(). The DrawInfo structure contains a copy of the pen specification for the screen that can be used in the OpenScreenTagList() call with the SA_Pens tag. The pen array is copied to the data structures of the new screen (it is not kept as a pointer to the information passed), so the application may immediately call FreeScreenDrawInfo() and UnlockPubScreen() after the new screen is open.

/* publicscreen.c
** open a screen with the pens from a public screen.
*/
#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
 
VOID usePubScreenPens(void);
 
struct IntuitionIFace *IIntuition = NULL;
 
/* main(): open libraries, clean up when done.
*/
int main(int argc, char **argv)
{
  struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
  IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
  if ( IIntuition != NULL )
  {
    usePubScreenPens();
  }
  IExec->DropInterface((struct Interface*)IIntuition);
  IExec->CloseLibrary(IntuitionBase);
  return 0;
}
 
/* Open a screen that uses the pens of an existing public screen
** (the Workbench screen in this case).
*/
VOID usePubScreenPens(void)
{
struct Screen *my_screen;
struct TagItem screen_tags[2];
UBYTE *pubScreenName = "Workbench";
 
struct Screen *pub_screen = NULL;
struct DrawInfo *screen_drawinfo = NULL;
 
/* Get a lock on the Workbench screen */
pub_screen = IIntuition->LockPubScreen(pubScreenName);
if ( pub_screen != NULL )
    {
    /* get the DrawInfo structure from the locked screen */
    screen_drawinfo = IIntuition->GetScreenDrawInfo(pub_screen);
    if ( screen_drawinfo != NULL)
        {
        /* the pens are copied in the OpenScreenTagList() call,
        ** so we can simply use a pointer to the pens in the tag list.
        **
        ** This works better if the depth and colors of the new screen
        ** matches that of the public screen.  Here we are forcing the
        ** workbench screen pens on a monochrome screen (which may not
        ** be a good idea).  You could add the tag:
        **      (SA_Depth, screen_drawinfo->dri_Depth)
        */
        screen_tags[0].ti_Tag  = SA_Pens;
        screen_tags[0].ti_Data = (ULONG)(screen_drawinfo->dri_Pens);
        screen_tags[1].ti_Tag  = TAG_END;
        screen_tags[1].ti_Data = NULL;
 
        my_screen = IIntuition->OpenScreenTagList(NULL, screen_tags);
        if (my_screen != NULL)
            {
            /* We no longer need to hold the lock on the public screen
            ** or a copy of its DrawInfo structure as we now have our
            ** own screen.  Release the screen.
            */
            IIntuition->FreeScreenDrawInfo(pub_screen,screen_drawinfo);
            screen_drawinfo = NULL;
            IIntuition->UnlockPubScreen(pubScreenName,pub_screen);
            pub_screen = NULL;
 
            IDOS->Delay(90);   /* should be rest_of_program */
 
            IIntuition->CloseScreen(my_screen);
            }
        }
    }
 
/* These are freed in the main loop if OpenScreenTagList() does
** not fail.  If something goes wrong, free them here.
*/
if ( screen_drawinfo != NULL )
    IIntuition->FreeScreenDrawInfo(pub_screen,screen_drawinfo);
if ( pub_screen!= NULL )
    IIntuition->UnlockPubScreen(pubScreenName,pub_screen);
}

The pen specification for the Workbench screen happens to match the Intuition default specification, however, this is not required and may change in the future. To create a screen that uses the pens defined in the Workbench screen, the application must get a copy of the pen array from the Workbench screen and use this copy with the SA_Pens tag as described above.

Here is a list of the pens that support the 3D look along with their uses. To read the value of a particular pen, use UWORD penvalue = myDrawInfo->dri_Pens[PENNAME], where myDrawInfo is a pointer to a DrawInfo structure and PENNAME is taken from the list below:

DETAILPEN
Pen compatible with V34. Used to render text in the screen's title bar.
BLOCKPEN
Pen compatible with V34. Used to fill the screen's title bar.
TEXTPEN
Pen for regular text on BACKGROUNDPEN.
SHINEPEN
Pen for the bright edge on 3D objects.
SHADOWPEN
Pen for the dark edge on 3D objects.
FILLPEN
Pen for filling the active window borders and selected gadgets.
FILLTEXTPEN
Pen for text rendered over FILLPEN.
BACKGROUNDPEN
Pen for the background color. Currently must be zero.
HIGHLIGHTTEXTPEN
Pen for "special color" or highlighted text on BACKGROUNDPEN.

The Font Specification in DrawInfo

Font information for a screen comes from a number of different places.

The application may specify the font to be used in a screen by providing the SA_Font tag with a TextAttr structure. In this case, the font will be used by the screen and will be the default font for the RastPort of any window opening in the screen.

If the application requests the user's preferred monospace font, it is taken from GfxBase->DefaultFont. Any window's RastPorts are also initialized to use this same font.

The screen font selected by the user from the Preferences font editor may be used for the screen by using the SA_SysFont tag. This font, the "preferred screen font", may be proportional. For compatibility reasons, if this font is specified for the screen, the window's RastPort will be initialized to GfxBase->DefaultFont (a non-proportional font).

To access information on an open screen's font, the application may reference Screen.Font or DrawInfo.dri_Font. These fonts are identical, the DrawInfo structure simply provides an alternate method of accessing the information. Note that Screen.Font is a pointer to a TextAttr structure and that DrawInfo.dri_Font is a pointer to a TextFont structure. The application may use whichever form is best suited to its requirements.

It is illegal to change the screen's font after the screen is opened. This means that the font specified in the Screen and DrawInfo structures is guaranteed to remain open as long is the screen is open.

The menu bar, window titles, menu items, and the contents of a string gadget all use the screen's font. The font used for menu items can be overridden in the menu item's IntuiText structure. The font used in a string gadget can be overridden through the StringExtend structure. The font of the menu bar and window titles cannot be overridden.

For more information on screen fonts, see the description of the SA_Font and SA_SysFont tags in the "Screen Attributes" section above.

Overscan and the Display Clip

Screens may be larger or smaller than the defined display area (overscan rectangle or display clip). When a screen is smaller than the display area, the display clip acts as a "container" for the screen. The screen may be moved anywhere within the display clip. When a screen is larger than the display area, the display clip acts as a "window" into the screen. The screen may be moved so that different parts are visible. Each dimension of the screen is independent and may be larger than, the same as, or smaller than the dimensions of the display clip.

The system is very flexible in its specification of screen size. Unless an application fixes its screen size with hard coded values, it should be prepared to handle the possibility that the user has changed the default overscan presets or the default monitor.

Use the constants STDSCREENHEIGHT and STDSCREENWIDTH with the SA_Width and SA_Height tags to open a screen the same size as the display clip. These constants will work with any of the preset overscan values set with SA_Overscan, and with custom overscan values set with SA_DClip.

Preset Overscan Values

Four preset overscan dimensions are provided. Applications that support overscan should use these preset values where possible since they will be tailored to each individual system. Avoid using custom values that happen to look good on a specific system. However, be aware that the size and positioning of overscan screens can be different on every system depending on how the user has set Overscan Preferences. These preset values are also dependent on the underlying display mode so keep in mind that both offset and size parameters will change under different screen modes. Overscan presets can be used, among other things, with the SA_Overscan tag to set the size of the screen's display clip or passed as an argument to QueryOverscan() to find their current overscan settings.

OSCAN_TEXT
This overscan region is based on user preference settings and indicates a display that is completely within the visible bounds of the monitor. The View origin is set to the upper left corner of the text overscan rectangle which is the highest leftmost point known to be visible on the physical display. This position is set by the user through the Overscan Preferences editor. All screen positions and display clips are relative to this origin.
OSCAN_STANDARD
The edges of OSCAN_STANDARD display are also based on user preferences and are set to be just outside the visible bounds of the monitor. OSCAN_STANDARD provides the smallest possible display that will fill the entire screen with no border around it. Parts of the display created with OSCAN_STANDARD may not be visible to the user.
OSCAN_MAX
Create the largest display fully supported by Intuition and the graphics library. This is the largest size for which all enclosed sizes and positions are legal. Parts of the display created with OSCAN_MAX may not be visible to the user.
OSCAN_VIDEO
Create the largest display, restricted by the hardware. This is the only legal size and position that is possibly (but not necessarily) larger than OSCAN_MAX. You must use the exact size and position specified. OSCAN_VIDEO does not support variable left edge, top edge positioning. Parts of the display created with OSCAN_VIDEO may not be visible to the user.

If custom clipping is required, a display clip may be explicitly specified using the SA_DClip tag and a Rectangle structure specification. This custom rectangle must fit within the OSCAN_MAX rectangle, offset included. It is not permitted to specify custom rectangles whose values are in between OSCAN_MAX and OSCAN_VIDEO, nor is it permitted to specify rectangles larger than OSCAN_VIDEO. For an example of how to open a centered overscan screen based on user preferences, see the "module/screen.c" listing in the IFF Source Code.

Use the Graphics library call VideoControl() to find the true display clip of a screen. See the Graphics Autodocs and Graphics Primitives for more information on VideoControl(). The ViewPortExtra structure contains the display clip information.

If any dimension of a screen is not equal to the equivalent display clip dimension, then the screen may be scrolled. If the screen's dimensions are smaller than the display clip, then the screen may be positioned within the display clip. If the screen is larger than the display clip, then it may be positioned such that any part of the screen is visible.

AutoScroll may be activated by setting the tag SA_AutoScroll. Screens will only scroll when they are the active screen. Activate a window in the screen to make the screen active.

About the Default Display Clip
The default display clip for a screen is the entire screen, that is, the rectangle starting from the upper left corner of the screen and ending at the lower right corner of the screen. This display clip is only used if the application does not specify SA_Overscan or SA_DClip. When using this default display clip the screen will not scroll as the screen exactly fits into the clipping region.

When opening a window in an overscanned screen, it is often useful to open it relative to the visible part of the screen rather than relative to the entire screen. Use QueryOverscan() to find the overscan region and where the screen is positioned relative to it.

LONG QueryOverscan(ULONG displayID, struct Rectangle *rect, WORD overscanType );

This example was taken from Intuition Windows in the section "Visible Display Sized Window Example". The complete example is reproduced there.

/* this technique returns the text overscan rectangle of the screen that we
** are opening on.  If you really need the actual value set into the display
** clip of the screen, use the VideoControl() command of the graphics library
** to return a copy of the ViewPortExtra structure.  See the Graphics
** library articles and Autodocs for more details.
**
** GetVPModeID() is a graphics call...
*/
 
screen_modeID = IGraphics->GetVPModeID(&(pub_screen->ViewPort))))
if (screen_modeID != INVALID_ID)
    {
    if ( IGraphics->QueryOverscan(screen_modeID, &rect, OSCAN_TEXT) )
        {
        /* if this screen's origin is up or to the left of the */
        /* view origin then move the window down and to the right */
        left = max(0, -pub_screen->LeftEdge);
        top  = max(0, -pub_screen->TopEdge);
 
        /* get width and height from size of display clip */
        width  = rect.MaxX - rect.MinX + 1;
        height = rect.MaxY - rect.MinY + 1;
 
        /* adjust height for pulled-down screen (only show visible part) */
        if (pub_screen->TopEdge > 0)
            height -= pub_screen->TopEdge;
 
        /* ensure that window fits on screen */
        height = min(height, pub_screen->Height);
        width  = min(width,  pub_screen->Width);
 
        /* make sure window is at least minimum size */
        width  = max(width,  MIN_WINDOW_WIDTH);
        height = max(height, MIN_WINDOW_HEIGHT);
        }
    }

Intuition Screens and the Graphics Library

As previously mentioned, an Intuition screen is related to a number of underlying graphics library structures.

Graphics Data Structures Used with Screens
Structure Name Description Include File
View Root structure of the graphics display system <graphics/view.h>
ViewPort The graphics structure that corresponds to a screen <graphics/view.h>
ColorMap Contains size and pointer to the screen's color table <graphics/view.h>
RastPort Holds drawing, pen and font settings and the BitMap address <graphics/rastport.h>

These data structures are unified in Intuition's Screen structure (which also incorporates higher level Intuition constructs such as menus and windows). Here's a brief explanation of the graphics library structures used with Intuition.

View
The View is the graphics structure that corresponds to the whole display, including all visible screens. The system has just one View; it's what you see on the monitor. The address of the View may be obtained from any screen by using ViewAddress().
ViewPort
The ViewPort is the underlying graphics structure corresponding to a screen. Every screen has one ViewPort. To get the address of the ViewPort from the Screen structure, use (&my_screen->ViewPort). From the ViewPort an application may obtain pointers to all the screen's bitplanes and to its color table.
ColorMap
The ColorMap contains a pointer to the color table, an array of 32 WORDs for the hardware color registers. Use SetRGB4(), GetRGB4(), SetRGB4CM() and LoadRGB4() from the graphics library to access the color table. Do not read or write it directly.
RastPort
A RastPort controls the graphics rendering to any display area (not just screens). Screens have a RastPort to allow direct rendering into the screen. Applications may find the RastPort address of a screen with (&my_screen->RastPort). The screen's BitMap can also be found via the RastPort in the RastPort.BitMap field. This generally is not useful since applications normally render into windows.

Changing Screen Colors

Screen colors are set at the time the screen is opened with the SA_Colors tag. If the colors need to be changed after the screen is opened, the graphics library function, LoadRGB4() should be used. To change a single entry in the color table, use SetRGB4() and SetRGB4CM(). See Graphics Primitives for more information on these functions.

Direct Screen Access

Sometimes an application may want direct access to the custom screen's bitmap to use with low-level graphics library calls. This may be useful if the application needs to do custom manipulation of the display but also needs Intuition functionality. For instance, an application may want to use the graphics library primitives to perform double buffering then, when detecting user input, switch to Intuition control of the screen so that windows, gadgets and menus may be used to process the user input. If an application chooses to combine these techniques, it must take special care to avoid conflicts with Intuition rendered graphics. An example of how to do this is listed in the next section, "Advanced Screen Programming".

Application programs that open custom screens may use the screen's display memory in any way they choose. However, this memory is also used by Intuition for windows and other high level display components on the screen. Writing directly to the screen memory, whether through direct access or through graphics library calls that access the screen's RastPort, is not compatible with many Intuition constructs such as windows and menus.

Techniques such as this require great care and understanding of the Amiga. If possible, the application should avoid these techniques and only use standard Intuition display and input processing. Directly accessing the screen's bitmap, while possible, is not recommended. A better way to access the screen display is through windows. Windows provide access to the screen through layers which perform clipping and arbitration between multiple drawing areas.

Alternatives to writing directly to a screen, such as using a backdrop window, greatly limit the number of cases where an application must access screen memory. The ShowTitle() function allows the screen's title bar layer to be positioned in front of or behind any backdrop windows that are opened on the screen. Hence, a backdrop window may be created that uses the entire visible area of the monitor.

Application programs that use existing public screens do not have the same freedom to access the screen's display memory as they do with custom screens. In general, public screens must be shared through the use of windows and menus rather than directly accessing the screen's display memory.

Use Direct Access Only On Screens You "Own"
An application may not steal the bitmap of a screen that it does not own. Stealing the Workbench screen's bitmap, or that of any other public screen, is strictly illegal. Accessing the underlying graphics structures of a screen may only be done on custom screens opened by the application itself.
Do Not Perform Layers Operations Directly
While layers are not part of the graphics library, it is appropriate to mention them here. Certain types of layers operations are not allowed with Intuition. You may not, for example, call SizeLayer() on a window (use SizeWindow() instead). To access layers library features with screens, use Intuition windows!

A custom screen may be created to allow for modification of the screen's Copper list. The Copper is the display synchronized co-processor that handles the actual video display by directly affecting the hardware registers. See the Amiga Hardware Reference Manual or the graphics library articles for more information on programming the Copper.

Limitations of the Graphics Subsystem

If each of the visible screens does not have the same physical attributes, it may not be possible to display the data in its proper screen mode. Screen coercion is the technique that allows multiple screens with differing physical attributes to be displayed simultaneously. When a coerced screen is visible, its aspect ratio and colors may appear significantly changed. This is normal and the screen will be displayed correctly when it is the frontmost screen.

Hardware restrictions prevent certain types of displays. For instance, screens always use the full width of the display, regardless of the width of the overscan rectangle. This prevents any changes in display mode within a video line. Other modes, such as the VGA modes, require specific revisions of the custom chips and may not be available on all machines. See Graphics Primitives and the Amiga Hardware Reference Manual for more information on Amiga display organization and limitations.

Advanced Screen Programming

This section discusses how to perform double-buffering of Intuition screens and other advanced topics. Dual-playfield Intuition screens are covered in the Classic Intuition Screens section.

Screen Locking

Applications wishing to block all screen rendering so that they can do "exceptional" rendering (eg. dragging icons, pop-up menus, etc.) need to LockScreen(). By using LockScreen() and UnlockScreen(), such applications avoid the risk of deadlocking with Intuition. Intuition will behave much like it does when menus are down; that is to say that layer-related events such as window size, depth, or position changes are deferred until the screen is unfrozen.

The application has the following obligations:

  1. When UnlockScreen() is finally called, the screen's bitmap must be bit-for-bit the same as when you called LockScreen(), with the exception of rendering into your own windows' RastPorts. That is to say, you are responsible for doing non-destructive rendering during the time you have the screen frozen.
  2. You may only make regular rendering calls (graphics calls, plus Intuition imagery functions such as PrintIText(), DrawImage(), DrawBorder()). Do not call any gadget refresh functions, OpenWindow(), Begin/EndRefresh(), etc. or any Intuition function that involves locking IntuitionBase (namely LockIBase()).
  3. Don't freeze the screen on the user. Freeze it only in response to the user. Typically, if you don't unfreeze the screen when the user lets the mouse button go, you're freezing for too long.
  4. Do not freeze a screen you don't own. It's OK to freeze a public screen (including the Workbench screen), or your own custom screen. If it's a public screen, be sure you hold a public-screen lock (see LockPubScreen()) or have a window open on it.

The active window will still receive mouse-, keyboard- and timer-messages. So if you want your window to get the IDCMP messages, the following must be done:

  1. make your window the active one, if it isn't (see ActivateWindow)
  2. wait for IDCMP_ACTIVEWINDOW
  3. then call LockScreen()
  4. do your input processing. When receiving IDCMP_INACTIVEWINDOW abort and call UnlockScreen() (your window could have gone inactive before you had the chance to call LockScreen())
  5. when done, call UnlockScreen()
Note
BOOPSI class implementers must use the LockScreenGI() and UnlockScreenGI() functions.

Locking the Screen List

Sometimes there is a need for applications to lock Intuition's entire screen list. For these situations use the LockScreenList() and UnlockScreenList functions. While the screen list is locked, Intuition is "frozen" from the user's point of view. It is best to copy any required information (e.g. screen titles) and then release the list.

Double Buffering

Intuition supports double (or multiple) buffering inside an Intuition screen, with full support for menus, and support for certain kinds of gadget.

The AllocScreenBuffer() call allows you to create other BitMap buffers for your screen. The SB_SCREEN_BITMAP flag is used to get a buffer handle for the BitMap currently in use in the screen. Subsequent buffers are allocated with the SB_COPY_BITMAP flag instead, which instructs Intuition to copy the current imagery (e.g., the screen title-bar and any of your rendering) into the alternate BitMap. Normally you let Intuition allocate these alternate BitMaps, but if your screen is CUSTOMBITMAP, you should allocate the alternate BitMaps yourself.

To swap buffers, call the ChangeScreenBuffer() function, which attempts to install the new buffer. ChangeScreenBuffer() will fail if Intuition is temporarily unable to make the change (say while gadgets or menus are active). Intuition builds these functions on top of the graphics.library ChangeVPBitMap() function, so the signalling information that graphics provides is also available to the Intuition user.

To clean up, call FreeScreenBuffer() for each screen buffer. It is not necessary to restore the original buffer before freeing things. Consult the autodocs for full details.

When the user accesses the screen's menus, buffer-swapping will stop. The ChangeScreenBuffer() call will return failure during this time. When the user finishes his menu selection, buffer-swapping will be possible again. Only a small subset of gadgets are supportable in double-buffered screens. These gadgets are those whose imagery returns to the initial state when you release them (e.g., action buttons or the screen's depth gadget). To use other kinds of gadgets (such as sliders or string gadgets) you need to put them on a separate screen, which can be an attached screen.

Windows with borders are not supportable on double-buffered screens. Double-buffered screens are expected to consist nearly entirely of custom rendering.

An example program illustrating double-buffering under Intuition, with menu-lending and an attached screen to hold two slider gadgets is provided.

doublebuffer.c

/*
 * doublebuffer.c - shows double-buffering, attached screens, menu lending
 *
 * Example shows use of double-buffering functions to achieve
 * 60 frame-per-second animation.  Also shows use of menus in
 * double-buffered screens, and the use of attached screens with
 * menu-lending to achieve the appearance of slider gadgets in
 * double-buffered screens.
 *
 */
 
/*----------------------------------------------------------------------*/
 
#include <exec/types.h>
#include <graphics/displayinfo.h>
#include <graphics/videocontrol.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
 
#include <stdlib.h>
 
/*----------------------------------------------------------------------*/
 
STRPTR init_all( void  );
void error_exit( STRPTR errorstring );
struct Gadget *createAllGadgets( struct Gadget **glistptr, void *vi );
BOOL handleIntuiMessage( struct IntuiMessage *imsg );
void handleDBufMessage( struct Message *dbmsg );
ULONG handleBufferSwap( void );
struct BitMap *makeImageBM( void );
void CloseWindowSafely( struct Window *win );
 
/*----------------------------------------------------------------------*/
 
/* Some constants to handle the rendering of the animated face */
#define BM_WIDTH	120
#define BM_HEIGHT	60
#define BM_DEPTH	2
 
/* Odd numbers to give a non-repeating bounce */
#define CONTROLSC_TOP		191
#define SC_ID			HIRES_KEY
 
/*----------------------------------------------------------------------*/
 
/* User interface constants and variables */
 
#define GAD_HORIZ	1
#define GAD_VERT	2
 
#define MENU_RUN	1
#define MENU_STEP	2
#define MENU_QUIT	3
#define MENU_HSLOW	4
#define MENU_HFAST	5
#define MENU_VSLOW	6
#define MENU_VFAST	7
 
struct TextAttr Topaz80 =
{
    "topaz.font", 		/* Name */
    8, 				/* YSize */
    FS_NORMAL,			/* Style */
    FPF_ROMFONT|FPF_DESIGNED,	/* Flags */
};
 
struct TagItem vctags[] =
{
    VTAG_BORDERSPRITE_SET, TRUE,
    TAG_END, 0,
};
 
UWORD pens[] =
{
    0, /* DETAILPEN */
    1, /* BLOCKPEN	*/
    1, /* TEXTPEN	*/
    2, /* SHINEPEN	*/
    1, /* SHADOWPEN	*/
    3, /* FILLPEN	*/
    1, /* FILLTEXTPEN	*/
    0, /* BACKGROUNDPEN	*/
    2, /* HIGHLIGHTTEXTPEN	*/
 
    1, /* BARDETAILPEN	*/
    2, /* BARBLOCKPEN	*/
    1, /* BARTRIMPEN	*/
 
    (UWORD)~0,
};
 
struct NewMenu demomenu[] =
{
	{ NM_TITLE, "Project", 		        0 , 0, 0, 0, },
	{  NM_ITEM, "Run", 		           "R", 0, 0, ( APTR ) MENU_RUN, },
	{  NM_ITEM, "Step", 		         "S", 0, 0, ( APTR ) MENU_STEP, },
	{  NM_ITEM, NM_BARLABEL, 	        0 , 0, 0, 0, },
	{  NM_ITEM, "Slower Horizontal", "1", 0, 0, ( APTR ) MENU_HSLOW, },
	{  NM_ITEM, "Faster Horizontal", "2", 0, 0, ( APTR ) MENU_HFAST, },
	{  NM_ITEM, "Slower Vertical", 	 "3", 0, 0, ( APTR ) MENU_VSLOW, },
	{  NM_ITEM, "Faster Vertical", 	 "4", 0, 0, ( APTR ) MENU_VFAST, },
 
	{  NM_ITEM, NM_BARLABEL, 	        0 , 0, 0, 0, },
	{  NM_ITEM, "Quit", 		         "Q", 0, 0, ( APTR ) MENU_QUIT, },
 
	{   NM_END, 0, 			              0 , 0, 0, 0, },
};
 
struct Screen *canvassc = NULL;
struct Screen *controlsc = NULL;
struct Window *controlwin = NULL;
struct Window *canvaswin = NULL;
struct Gadget *glist = NULL;
struct Gadget *horizgad, *vertgad;
struct Menu *menu = NULL;
void *canvasvi = NULL;
void *controlvi = NULL;
 
/*----------------------------------------------------------------------*/
 
#define	OK_REDRAW	1	/* Buffer fully detached, ready for redraw */
#define OK_SWAPIN	2	/* Buffer redrawn, ready for swap-in */
 
struct GraphicsIFace *IGraphics = NULL;
struct IntuitionIFace *IIntuition = NULL;
struct GadToolsIFace *IGadTools = NULL;
 
struct MsgPort *dbufport = NULL;
struct MsgPort *userport = NULL;
 
struct ScreenBuffer *scbuf[] =
{
    NULL,
    NULL,
};
struct RastPort rport[ 2 ];
 
ULONG status[ 2 ];
 
LONG prevx[ 2 ] =
{
    50, 50,
};
 
LONG prevy[ 2 ] =
{
    50, 50,
};
 
ULONG buf_current, buf_nextdraw, buf_nextswap;
ULONG count;
struct BitMap *face = NULL;
LONG x, y, xstep, xdir, ystep, ydir;
 
/*----------------------------------------------------------------------*/
 
int main()
{
    STRPTR errorstring;
    ULONG sigs;
    BOOL terminated = FALSE;
 
    /* Let's get everything initialized */
    if ( errorstring = init_all() )
    {
	error_exit( errorstring );
    }
 
    count = 0;
    buf_current = 0;
    buf_nextdraw = 1;
    buf_nextswap = 1;
    sigs = 0;
 
    while ( !terminated )
    {
	/* Check for and handle any IntuiMessages */
	if ( sigs & ( 1 << userport->mp_SigBit ) )
	{
	    struct IntuiMessage *imsg;
 
	    while ( imsg = IGadTools->GT_GetIMsg( userport ) )
	    {
		terminated |= handleIntuiMessage( imsg );
		IGadTools->GT_ReplyIMsg( imsg );
	    }
	}
 
	/* Check for and handle any double-buffering messages.
	 * Note that double-buffering messages are "replied" to
	 * us, so we don't want to reply them to anyone.
	 */
	if ( sigs & ( 1 << dbufport->mp_SigBit ) )
	{
	    struct Message *dbmsg;
	    while ( dbmsg = IExec->GetMsg( dbufport ) )
	    {
		handleDBufMessage( dbmsg );
	    }
	}
 
 
	if ( !terminated )
	{
	    ULONG held_off = 0;
	    /* Only handle swapping buffers if count is non-zero */
	    if ( count )
	    {
		held_off = handleBufferSwap();
	    }
	    if ( held_off )
	    {
		/* If were held-off at ChangeScreenBuffer() time, then we
		 * need to try ChangeScreenBuffer() again, without awaiting
		 * a signal.  We WaitTOF() to avoid busy-looping.
		 */
		 IGraphics->WaitTOF();
	    }
	    else
	    {
		/* If we were not held-off, then we're all done
		 * with what we have to do.  We'll have no work to do
		 * until some kind of signal arrives.  This will normally
		 * be the arrival of the dbi_SafeMessage from the ROM
		 * double-buffering routines, but it might also be an
		 * IntuiMessage.
		 */
		sigs = IExec->Wait( ( 1 << dbufport->mp_SigBit ) | ( 1 << userport->mp_SigBit ) );
	    }
	}
    }
 
    error_exit( NULL );
}
 
 
/*----------------------------------------------------------------------*/
 
/* Handle the rendering and swapping of the buffers */
 
ULONG handleBufferSwap( void )
{
    ULONG held_off = 0;
    /* 'buf_nextdraw' is the next buffer to draw into.
     * The buffer is ready for drawing when we've received the
     * dbi_SafeMessage for that buffer.  Our routine to handle
     * messaging from the double-buffering functions sets the
     * OK_REDRAW flag when this message has appeared.
     *
     * Here, we set the OK_SWAPIN flag after we've redrawn
     * the imagery, since the buffer is ready to be swapped in.
     * We clear the OK_REDRAW flag, since we're done with redrawing
     */
    if ( status[ buf_nextdraw ] == OK_REDRAW )
    {
	x += xstep*xdir;
	if ( x < 0 )
	{
	    x = 0;
	    xdir = 1;
	}
	else if ( x > canvassc->Width - BM_WIDTH )
	{
	    x = canvassc->Width - BM_WIDTH - 1;
	    xdir = -1;
	}
 
	y += ystep*ydir;
	if ( y < canvassc->BarLayer->Height )
	{
	    y = canvassc->BarLayer->Height;
	    ydir = 1;
	}
	else if ( y >= CONTROLSC_TOP - BM_HEIGHT )
	{
	    y = CONTROLSC_TOP - BM_HEIGHT - 1;
	    ydir = -1;
	}
 
	IGraphics->SetAPen( &rport[ buf_nextdraw ], 0 );
	IGraphics->RectFill( &rport[ buf_nextdraw ],
	    prevx[ buf_nextdraw ], prevy[ buf_nextdraw ],
	    prevx[ buf_nextdraw ] + BM_WIDTH - 1, prevy[ buf_nextdraw ] + BM_HEIGHT - 1 );
	prevx[ buf_nextdraw ] = x;
	prevy[ buf_nextdraw ] = y;
 
	IGraphics->BltBitMapRastPort( face, 0, 0, &rport[ buf_nextdraw ], x, y,
	    BM_WIDTH, BM_HEIGHT, 0xc0 );
 
	IGraphics->WaitBlit(); /* Gots to let the BBMRP finish */
 
	status[ buf_nextdraw ] = OK_SWAPIN;
 
	/* Toggle which the next buffer to draw is.
	 * If you're using multiple ( >2 ) buffering, you
	 * would use
	 *
	 *    buf_nextdraw = ( buf_nextdraw+1 ) % NUMBUFFERS;
	 *
	 */
	buf_nextdraw ^= 1;
    }
 
    /* Let's make sure that the next frame is rendered before we swap...
     */
    if ( status[ buf_nextswap ] == OK_SWAPIN )
    {
	scbuf[ buf_nextswap ]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = dbufport;
 
	if ( IIntuition->ChangeScreenBuffer( canvassc, scbuf[ buf_nextswap ] ) )
	{
	    status[ buf_nextswap ] = 0;
 
	    buf_current = buf_nextswap;
	    /* Toggle which the next buffer to swap in is.
	     * If you're using multiple ( >2 ) buffering, you
	     * would use
	     *
	     *    buf_nextswap = ( buf_nextswap+1 ) % NUMBUFFERS;
	     *
	     */
	    buf_nextswap ^= 1;
 
	    count--;
	}
	else
	{
	    held_off = 1;
	}
    }
    return( held_off );
}
 
/*----------------------------------------------------------------------*/
 
/* Handle Intuition messages */
 
BOOL handleIntuiMessage( struct IntuiMessage *imsg )
{
    UWORD code = imsg->Code;
    BOOL terminated = FALSE;
 
    switch ( imsg->Class )
    {
	case IDCMP_GADGETDOWN:
	case IDCMP_GADGETUP:
	case IDCMP_MOUSEMOVE:
	    switch ( ( ( struct Gadget * )imsg->IAddress )->GadgetID )
	    {
		case GAD_HORIZ:
		    xstep = code;
		    break;
 
		case GAD_VERT:
		    ystep = code;
		    break;
	    }
	    break;
 
	case IDCMP_VANILLAKEY:
	    switch ( code )
	    {
		case 'S':
		case 's':
		    count = 1;
		    break;
 
		case 'R':
		case 'r':
		    count = ~0;
		    break;
 
		case 'Q':
		case 'q':
		    count = 0;
		    terminated = TRUE;
		    break;
	    }
	    break;
 
	case IDCMP_MENUPICK:
	    while ( code != MENUNULL )
	    {
		struct MenuItem *item;
 
		item = IIntuition->ItemAddress( menu, code );
		switch ( ( ULONG ) GTMENUITEM_USERDATA( item ) )
		{
		    case MENU_RUN:
			count = ~0;
			break;
 
		    case MENU_STEP:
			count = 1;
			break;
 
		    case MENU_QUIT:
			count = 0;
			terminated = TRUE;
			break;
 
		    case MENU_HSLOW:
			if ( xstep > 0 )
			{
			    xstep--;
			}
			IGadTools->GT_SetGadgetAttrs( horizgad, controlwin, NULL,
			    GTSL_Level, xstep,
			    TAG_END );
			break;
 
		    case MENU_HFAST:
			if ( xstep < 9 )
			{
			    xstep++;
			}
			IGadTools->GT_SetGadgetAttrs( horizgad, controlwin, NULL,
			    GTSL_Level, xstep,
			    TAG_END );
			break;
 
		    case MENU_VSLOW:
			if ( ystep > 0 )
			{
			    ystep--;
			}
			IGadTools->GT_SetGadgetAttrs( vertgad, controlwin, NULL,
			    GTSL_Level, ystep,
			    TAG_END );
			break;
 
		    case MENU_VFAST:
			if ( ystep < 9 )
			{
			    ystep++;
			}
			IGadTools->GT_SetGadgetAttrs( vertgad, controlwin, NULL,
			    GTSL_Level, ystep,
			    TAG_END );
			break;
		}
		code = item->NextSelect;
	    }
	    break;
    }
    return( terminated );
}
 
/*----------------------------------------------------------------------*/
 
void handleDBufMessage( struct Message *dbmsg )
{
    ULONG buffer;
 
    /* dbi_SafeMessage is followed by an APTR dbi_UserData1, which
     * contains the buffer number.  This is an easy way to extract
     * it.
     * The dbi_SafeMessage tells us that it's OK to redraw the
     * in the previous buffer.
     */
    buffer = ( ULONG ) *( ( APTR ** ) ( dbmsg+1 ) );
    /* Mark the previous buffer as OK to redraw into.
     * If you're using multiple ( >2 ) buffering, you
     * would use
     *
     *    ( buffer + NUMBUFFERS - 1 ) % NUMBUFFERS
     *
     */
    status[ buffer^1 ] = OK_REDRAW;
}
 
/*----------------------------------------------------------------------*/
 
/* Get the resources and objects we need */
 
STRPTR init_all( void )
{
    struct Library *GfxBase = IExec->OpenLibrary("graphics.library", 50);
    IGraphics = (struct GraphicsIFace*)IExec->GetInterface(GfxBase, "main", 1, NULL);
    if ( IGraphics == NULL )
    {
	return( "Couldn't open Gfx V50\n" );
    }
 
    struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
    IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
    if ( IIntuition == NULL )
    {
	return( "Couldn't open Intuition V50\n" );
    }
 
    struct Library *GadToolsBase = IExec->OpenLibrary("gadtools.library", 50);
    IGadTools = (struct GadToolsIFace*)IExec->GetInterface(GadToolsBase, "main", 1, NULL);
    if ( IGadtools == NULL )
    {
	return( "Couldn't open GadTools V50\n" );
    }
 
    if ( !( dbufport = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END) ) )
    {
	return( "Failed to create port\n" );
    }
 
    if ( !( userport = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END) ) )
    {
	return( "Failed to create port\n" );
    }
 
    if ( !( canvassc = IIntuition->OpenScreenTags( NULL,
	SA_DisplayID, SC_ID,
	SA_Overscan, OSCAN_TEXT,
	SA_Depth, 2,
	SA_AutoScroll, 1,
	SA_Pens, pens,
	SA_ShowTitle, TRUE,
	SA_Title, "Intuition double-buffering example",
	SA_VideoControl, vctags,
	SA_Font, &Topaz80,
	TAG_END ) ) )
    {
	return( "Couldn't open screen\n" );
    }
 
    if ( !( canvasvi = IGadTools->GetVisualInfo( canvassc, TAG_END ) ) )
    {
	return( "Couldn't get VisualInfo\n" );
    }
 
    if ( !( canvaswin = IIntuition->OpenWindowTags( NULL,
	WA_NoCareRefresh, TRUE,
	WA_Activate, TRUE,
	WA_Borderless, TRUE,
	WA_Backdrop, TRUE,
	WA_CustomScreen, canvassc,
	WA_NewLookMenus, TRUE,
	TAG_END ) ) )
    {
	return( "Couldn't open window\n" );
    }
    canvaswin->UserPort = userport;
 
    IIntuition->ModifyIDCMP( canvaswin, IDCMP_MENUPICK | IDCMP_VANILLAKEY );
 
    if ( !( controlsc = IIntuition->OpenScreenTags( NULL,
	SA_DisplayID, SC_ID,
	SA_Overscan, OSCAN_TEXT,
	SA_Depth, 2,
	SA_Pens, pens,
	SA_Top, CONTROLSC_TOP,
	SA_Height, 28,
	SA_Parent, canvassc,
	SA_ShowTitle, FALSE,
	SA_Draggable, FALSE,
	SA_VideoControl, vctags,
	SA_Quiet, TRUE,
	SA_Font, &Topaz80,
	TAG_END ) ) )
    {
	return( "Couldn't open screen\n" );
    }
 
    if ( !( controlvi = IGadTools->GetVisualInfo( controlsc, TAG_END ) ) )
    {
	return( "Couldn't get VisualInfo\n" );
    }
 
    if ( !( menu = IGadTools->CreateMenus( demomenu, TAG_END ) ) )
    {
	return( "Couldn't create menus\n" );
    }
 
    if ( !IGadTools->LayoutMenus( menu, canvasvi,
	GTMN_NewLookMenus, TRUE,
	TAG_END ) )
    {
	return( "Couldn't layout menus\n" );
    }
 
    if ( !createAllGadgets( &glist, controlvi ) )
    {
	return( "Couldn't create gadgets\n" );
    }
 
    /* A borderless backdrop window so we can get input */
    if ( !( controlwin = IIntuition->OpenWindowTags( NULL,
	WA_NoCareRefresh, TRUE,
	WA_Activate, TRUE,
	WA_Borderless, TRUE,
	WA_Backdrop, TRUE,
	WA_CustomScreen, controlsc,
	WA_NewLookMenus, TRUE,
	WA_Gadgets, glist,
	TAG_END ) ) )
    {
	return( "Couldn't open window\n" );
    }
 
    controlwin->UserPort = userport;
    IIntuition->ModifyIDCMP( controlwin, SLIDERIDCMP | IDCMP_MENUPICK | IDCMP_VANILLAKEY );
 
    IGadTools->GT_RefreshWindow( controlwin, NULL );
    IIntuition->SetMenuStrip( canvaswin, menu );
    IIntuition->LendMenus( controlwin, canvaswin );
 
    if ( !( scbuf[ 0 ] = IIntuition->AllocScreenBuffer( canvassc, NULL, SB_SCREEN_BITMAP ) ) )
    {
	return( "Couldn't allocate ScreenBuffer 1\n" );
    }
 
    if ( !( scbuf[ 1 ] = IIntuition->AllocScreenBuffer( canvassc, NULL, SB_COPY_BITMAP ) ) )
    {
	return( "Couldn't allocate ScreenBuffer 2\n" );
    }
 
    /* Let's use the UserData to store the buffer number, for
     * easy identification when the message comes back.
     */
    scbuf[ 0 ]->sb_DBufInfo->dbi_UserData1 = ( APTR ) ( 0 );
    scbuf[ 1 ]->sb_DBufInfo->dbi_UserData1 = ( APTR ) ( 1 );
    status[ 0 ] = OK_REDRAW;
    status[ 1 ] = OK_REDRAW;
 
    if ( !( face = makeImageBM() ) )
    {
	return( "Couldn't allocate image bitmap\n" );
    }
    IGraphics->InitRastPort( &rport[ 0 ] );
    IGraphics->InitRastPort( &rport[ 1 ] );
    rport[ 0 ].BitMap = scbuf[ 0 ]->sb_BitMap;
    rport[ 1 ].BitMap = scbuf[ 1 ]->sb_BitMap;
 
    x = 50;
    y = 70;
    xstep = 1;
    xdir = 1;
    ystep = 1;
    ydir = -1;
 
    /* All is OK */
    return( 0 );
}
 
/*----------------------------------------------------------------------*/
 
/* Draw a crude "face" for animation */
 
#define MAXVECTORS	10
 
struct BitMap *makeImageBM( void )
{
    struct BitMap *bm;
    struct RastPort rport;
    struct AreaInfo area;
    struct TmpRas tmpRas;
    PLANEPTR planePtr;
 
    BYTE areabuffer[ MAXVECTORS*5 ];
 
    if ( bm = ( struct BitMap * )IGraphics->AllocBitMap( BM_WIDTH, BM_HEIGHT,
	BM_DEPTH, BMF_CLEAR, NULL ) )
    {
	if ( planePtr = IGraphics->AllocRaster( BM_WIDTH, BM_HEIGHT ) )
	{
	    IGraphics->InitRastPort( &rport );
	    rport.BitMap = bm;
 
	    IGraphics->InitArea( &area, areabuffer, MAXVECTORS );
	    rport.AreaInfo = &area;
 
	    IGraphics->InitTmpRas( &tmpRas, planePtr, RASSIZE( BM_WIDTH, BM_HEIGHT ) );
	    rport.TmpRas = &tmpRas;
 
	    IGraphics->SetABPenDrMd( &rport, 3, 0, JAM1 );
	    IGraphics->AreaEllipse( &rport, BM_WIDTH/2, BM_HEIGHT/2,
		BM_WIDTH/2-4, BM_HEIGHT/2-4 );
	    IGraphics->AreaEnd( &rport );
 
	    IGraphics->SetAPen( &rport, 2 );
	    IGraphics->AreaEllipse( &rport, 5*BM_WIDTH/16, BM_HEIGHT/4,
		BM_WIDTH/9, BM_HEIGHT/9 );
	    IGraphics->AreaEllipse( &rport, 11*BM_WIDTH/16, BM_HEIGHT/4,
		BM_WIDTH/9, BM_HEIGHT/9 );
	    IGraphics->AreaEnd( &rport );
 
	    IGraphics->SetAPen( &rport, 1 );
	    IGraphics->AreaEllipse( &rport, BM_WIDTH/2, 3*BM_HEIGHT/4,
		BM_WIDTH/3, BM_HEIGHT/9 );
	    IGraphics->AreaEnd( &rport );
 
	    IGraphics->FreeRaster( planePtr, BM_WIDTH, BM_HEIGHT );
	}
	else
	{
	    IGraphics->FreeBitMap( bm );
	    bm = NULL;
	}
    return( bm );
    }
}
 
/*----------------------------------------------------------------------*/
 
/* Make a pair of slider gadgets to control horizontal and vertical
 * speed of motion.
 */
struct Gadget *createAllGadgets( struct Gadget **glistptr, void *vi )
{
    struct NewGadget ng;
    struct Gadget *gad;
 
    gad = IGadTools->CreateContext( glistptr );
 
    ng.ng_LeftEdge = 100;
    ng.ng_TopEdge = 1;
    ng.ng_Width = 100;
    ng.ng_Height = 12;
    ng.ng_GadgetText = "Horiz:  ";
    ng.ng_TextAttr = &Topaz80;
    ng.ng_VisualInfo = vi;
    ng.ng_GadgetID = GAD_HORIZ;
    ng.ng_Flags = 0;
 
    horizgad = gad = IGadTools->CreateGadget( SLIDER_KIND, gad, &ng,
	GTSL_Min, 0,
	GTSL_Max, 9,
	GTSL_Level, 1,
	GTSL_MaxLevelLen, 1,
	GTSL_LevelFormat, "%ld",
	TAG_END );
 
    ng.ng_LeftEdge += 200;
    ng.ng_GadgetID = GAD_VERT;
    ng.ng_GadgetText = "Vert:  ";
    vertgad = gad = IGadTools->CreateGadget( SLIDER_KIND, gad, &ng,
	GTSL_Min, 0,
	GTSL_Max, 9,
	GTSL_Level, 1,
	GTSL_MaxLevelLen, 1,
	GTSL_LevelFormat, "%ld",
	TAG_END );
 
    return( gad );
}
 
/*----------------------------------------------------------------------*/
 
/* Clean up everything and exit, printing the errorstring if any */
void error_exit( STRPTR errorstring )
{
    if ( controlwin )
    {
	IIntuition->ClearMenuStrip( controlwin );
	CloseWindowSafely( controlwin );
    }
 
    if ( canvaswin )
    {
	IIntuition->ClearMenuStrip( canvaswin );
	CloseWindowSafely( canvaswin );
    }
 
    if ( controlsc )
    {
	IIntuition->CloseScreen( controlsc );
    }
 
    if ( canvassc )
    {
	IIntuition->FreeScreenBuffer( canvassc, scbuf[ 1 ] );
	IIntuition->FreeScreenBuffer( canvassc, scbuf[ 0 ] );
	IIntuition->CloseScreen( canvassc );
    }
 
    if ( dbufport )
    {
	IExec->FreeSysObject(ASOT_PORT, dbufport );
    }
 
    if ( userport )
    {
	IExec->FreeSysObject(ASOT_PORT, userport );
    }
 
    if ( IGadTools )
    {
	IGadTools->FreeGadgets( glist );
	IGadTools->FreeMenus( menu );
	IGadTools->FreeVisualInfo( canvasvi );
	IGadTools->FreeVisualInfo( controlvi );
  struct Library *libBase = IGadTools->Data.LibBase;
  IExec->DropInterface((struct Interface*)IGadTools);
	IExec->CloseLibrary( libBase );
    }
 
    if ( IIntuition )
    {
  struct Library *libBase = IIntuition->Data.LibBase;
  IExec->DropInterface((struct Interface*)IIntuition);
	IExec->CloseLibrary( libBase );
    }
 
    if ( face )
    {
	IGraphics->FreeBitMap( face );
    }
 
    if ( IGraphics )
    {
  struct Library *libBase = IGraphics->Data.LibBase;
  IExec->DropInterface((struct Interface*)IGraphics);
	IExec->CloseLibrary( libBase );
    }
 
    if ( errorstring )
    {
	IDOS->Printf( errorstring );
	exit( 20 );
    }
 
    exit( 0 );
}
 
 
/*----------------------------------------------------------------------*/
 
/* these functions close an Intuition window
 * that shares a port with other Intuition
 * windows or IPC customers.
 *
 * We are careful to set the UserPort to
 * null before closing, and to free
 * any messages that it might have been
 * sent.
 */
#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/ports.h"
#include "intuition/intuition.h"
 
void
CloseWindowSafely( struct Window *win )
{
    /* we forbid here to keep out of race conditions with Intuition */
    IExec->Forbid();
 
    /* send back any messages for this window
     * that have not yet been processed
     */
    IIntuition->StripIntuiMessages( win->UserPort, win );
 
    /* clear UserPort so Intuition will not free it */
    win->UserPort = NULL;
 
    /* tell Intuition to stop sending more messages */
    IIntuition->ModifyIDCMP( win, 0L );
 
    /* turn multitasking back on */
    IExec->Permit();
 
    /* and really close the window */
    IIntuition->CloseWindow( win );
}

Other Screen Functions

Other screen functions provided by Intuition control screen depth arrangement, screen movement, the screen title bar and provide a visual "error beep".

Screen Depth Arrangement

ScreenToFront() and ScreenToBack() make a screen either the frontmost or the backmost screen. If an application needs to render into a screen before the screen becomes visible to the user, the screen may be opened behind all other screens and later moved to the front when ready with ScreenToFront().

VOID ScreenToFront( struct Screen * );
VOID ScreenToBack ( struct Screen * );

Depth control of screens is also available through the depth arrangement gadget in the screen's title bar or through keyboard shortcuts. The N key with the Left-Amiga qualifier moves the Workbench screen to front. The M key with the Left-Amiga qualifier moves the frontmost screen to back. Repeated selection of Left-Amiga-M will cycle through available screens. These keys are processed through the keymap and will retain their value even if the key location changes.

Screen Movement and Scrolling

The MoveScreen() function moves the screen origin by the number of pixels specified in dx and dy.

VOID MoveScreen( struct Screen *myscreen, WORD dx, WORD dy );

Calls to MoveScreen() are asynchronous; the screen is not necessarily moved upon return of this function. If the calls happen too quickly, there may be unexpected results. One way to pace these calls is to call the function one time for each IDCMP_INTUITICKS event.

Screen movement is also available through the screen's drag gadget in the title bar and through a keyboard/mouse shortcut. Left-Amiga with the select button of the mouse anywhere within the screen will drag the screen (even if the title bar is totally concealed by a window). Dragging a screen down will reveal any screen(s) behind it. Screens are never revealed to the left, right or bottom of another screen.

Additionally, oversized screens may be moved with the autoscroll feature. With autoscroll, the screen is automatically scrolled as the pointer reaches one of the edges of the display. Autoscroll only works on the active screen.

Another screen movement feature is menu snap. When a screen much larger than the viewing area is scrolled such that the upper left corner is not visible (scrolled down or to the right), menus may could be out of the visible portion of the screen. To prevent this, menu snap moves the screen to a position where the menus will be visible before rendering them. The screen appears to snap to the home position as the menus are selected, moving back when the operation is complete. If the Left-Amiga qualifier is held when the menus are selected then the screen will remain in the home position when the menu button is released.

The Intuition preferences editor, IControl, allows the user to change a number of Intuition features. Some of these features include the ability to globally disable menu snap, and to change the select qualifier for dragging the screen. See the User's Manual for more information on Preferences editors.

Miscellaneous Screen Functions

Three other functions used with screens are DisplayBeep(), ShowTitle() and GetScreenData(). DisplayBeep() flashes the screen colors to inform the user of an error or problem.

VOID DisplayBeep( struct Screen *myscreen );

Since not all users will have speakers attached to the system, DisplayBeep() can be used to provide a visible bell. DisplayBeep() can beep any single screen or, if myscreen is set to NULL, all screens.

ShowTitle() determines whether the screen's title bar will be displayed in front of or behind any backdrop windows on the screen.

VOID ShowTitle( struct Screen *myscreen, BOOL infront );

By default, the screen's title bar is set to display in front of backdrop windows. Call this function with infront set to FALSE to put the screen title bar behind backdrop windows. This can also be set when the screen is opened with the SA_ShowTitle tag.

Function Reference

The following are brief descriptions of the Intuition functions that relate to the use of Intuition screens. See the SDK for details on each function call.

Function Description
OpenScreenTagList() Open a screen.
OpenScreenTags() Alternate calling sequence for OpenScreenTagList().
OpenScreen() Pre-V36 open screen function.
CloseScreen() Close an open screen.
MoveScreen() Change the position of an open screen.
ScreenToBack() Move a screen behind all other screens.
ScreenToFront() Move a screen in front of all other screens.
ShowTitle() Show the screen in front of through backdrop windows.
GetScreenDrawInfo() Get the DrawInfo information for an open screen.
FreeScreenDrawInfo() Free the DrawInfo information for a screen.
QueryOverscan() Find overscan information for a specific display type.
LockPubScreen() Obtain a lock on a public screen.
UnlockPubScreen() Release a lock on a public screen.
NextPubScreen() Return the name of the next public screen in the list.
PubScreenStatus() Make a public screen private or private screen public.
LockPubScreenList() Lock the public screen list (for a public screen utility).
UnlockPubScreenList() Unlock the public screen list.
SetDefaultPubScreen() Change the default public screen.
SetPubScreenModes() Establish global public screen behavior.
GetDefaultPubScreen() Copies the name of the default public screen to a buffer.
OpenWorkBench() Open the Workbench screen, if closed.
CloseWorkBench() Close the Workbench screen, if possible.
WBenchToBack() Move the Workbench screen behind all other screens.
WBenchToFront() Move the Workbench screen in front of all other screens.
GetScreenData() Pre-V36 way to return information on an open screen.
GetScreenAttr() Query a screen attribute.
GetScreenAttr() / GetScreenAttrsA() Query screen attributes.
ViewAddress() Return the address of a screen's View.
ViewPortAddress() Use &screen->ViewPort instead.
MakeScreen() Low level screen handling-rebuild Copper list.
RethinkDisplay() Low level screen handling-incorporate Copper list changes.
RemakeDisplay() MakeScreen() for all screens, then RethinkDisplay().