Copyright (c) Hyperion Entertainment and contributors.

Intuition Screens

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
WIP.png This page is currently being updated to AmigaOS 4.x. Some of the information contained here may not yet be applicable in part or totally.

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 Screen Functions" below 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" later in this chapter).

Custom Screen Functions

All applications require a screen to work in. This can be an existing, public screen or a new, custom screen created by the application itself. To create a new, custom screen to work with, you call OpenScreenTags().

Custom Screen Functions
OpenScreenTags() Create a new, custom screen from a tag list. Use either one of these to open any screen.
OpenScreenTagList()
CloseScreen() Close a custom screen and free the memory it used.

Creating a new Custom Screen

There are two functions you can use to open a custom screen: OpenScreenTags() or OpenScreenTagList().

struct Screen *OpenScreenTagList( struct NewScreen * , struct TagItem *);
struct Screen *OpenScreenTags( struct NewScreen *, ULONG, ULONG, ... );

The "Screen Attributes" section below contains a complete list of all the tag options available for setting up an Intuition screen. For a general description of tag items, see Utility Library.

A Custom Screen Example

There are so many tag options available with screens it can be a bit overwhelming. Before discussing more details, it may be helpful to look at a simple example. The code below opens a new, custom screen using the OpenScreenTags() call. The example uses just two tag items (SA_Depth and SA_Pens) which provide the minimum attributes needed to make a screen that uses the 3D look of Intuition. (See the section on "DrawInfo and the 3D Look" below for more information.)

/* newlookscreen.c
** open a screen with the "new look".
*/
 
#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
 
/* Simple routine to demonstrate opening a screen with the new look.
** Simply supply the tag SA_Pens along with a minimal pen specification,
** Intuition will fill in all unspecified values with defaults.
** Since we are not supplying values, all are Intuition defaults.
*/
int main(int argc, char **argv)
{
    uint16 pens[] = { ~0 };
 
    struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
    struct IntuitionIFace *IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
    if (IIntuition != NULL)
    {
        /* The screen is opened two bitplanes deep so that the
        ** new look will show-up better.
        */
        struct Screen *my_screen = IIntuition->OpenScreenTags(NULL,
            SA_Pens, pens,
            SA_Depth, 2,
            TAG_END);
        if (my_screen != NULL)
        { 
            /* screen successfully opened */
            IDOS->Delay(30);  /* normally the program would be here */
 
            IIntuition->CloseScreen(my_screen);
        }
 
        IExec->DropInterface((struct Interface*)IIntuition);
        IExec->CloseLibrary(IntuitionBase);
        return 0;
    }
}

Return Values from OpenScreenTagList()

OpenScreenTagList() and its variants return a pointer to a Screen structure on the successful creation of a new screen and NULL on failure. The call also supports extended error codes on failure. The error returns provide information on the type of failure, giving the application a greater chance of recovery. To get the extended error code, you need to use the SA_ErrorCode tag; the code itself will be placed into the LONG pointed to by the Item.ti_Data field. Here are the codes:

OSERR_NOMONITOR
The monitor needed to display the requested mode is not available. An example of this error would be opening a Productivity mode screen on a system without a VGA or multisync monitor.
OSERR_NOCHIPS
Newer custom chips are required for this screen mode. For instance, the ECS Denise is required for the productivity modes.
OSERR_NOMEM
Could not allocate enough memory.
OSERR_NOCHIPMEM
Could not allocate enough Chip memory.
OSERR_PUBNOTUNIQUE
Could not create public screen'name already used. The system requires that public screen names be unique.
OSERR_UNKNOWNMODE
Display mode requested was not recognized. The system does not understand the value specified with the SA_DisplayID tag.

Closing the Screen

When an application has finished using a screen, the memory that the screen occupied should be returned to the system by calling CloseScreen(). Normally, an application should close only those screens that it created. CloseScreen() returns a boolean value, TRUE for success and FALSE for failure. CloseScreen() can fail if the screen is public and another task is still using the screen.

Public Screen Functions

A public screen allows multiple applications to share a single screen thus saving memory. If your application opens a public screen, then other applications will be able to open their windows on your screen. In older versions of the operating system, only the Workbench screen could be shared so applications had to live within its limitations or use up Chip memory creating their own private, custom screens.

Now the system allows any screen to be set up as a public screen so there may be many public screens in memory at once, not just Workbench. This permits the power user to set up different work environments that multiple applications can share in a way that is memory efficient (each one with a display mode appropriate to a particular job).

Workbench is a special case public screen because it is the initial default public screen. The default public screen is the screen applications will get when they ask for a public screen but don't specify a name. Under normal conditions, Workbench is the default public screen and is created by the system at startup time. However, keep in mind that the default public screen can be changed (it's not always guaranteed to be Workbench).

Screens for the Novice
If you're not sure what kind of screen to use, then use the default public screen. You can open a window on the default public screen without doing any screen set-up work. See the Intuition Windows for more details.

Generally, it is much easier to use an existing, public screen than to set up one of your own. Here are the basic functions you use to work with an existing public screen.

Public Screen Functions
LockPubScreen() Find Workbench or any other public screen; prevent it from closing while a window is opened or its attributes copied.
UnlockPubScreen() Release the lock allowing the screen to later be closed.
SetDefaultPubScreen() Establishes a given public screen as the default.
GetDefaultPubScreen() Copies the name of the default screen to a user supplied buffer for use by the screen manager utility (the name is not needed by normal applications, use LockPubScreen(NULL) instead).
PubScreenStatus() Converts a screen to private or public status.
SetPubScreenModes() Controls the public screen global mode bits.

By using an existing public screen, an application is no longer responsible for setting up the display, however, it also loses flexibility and control. It can no longer set the palette or depth, and it cannot write directly into screen memory without cooperation from the owner of the public screen. (If these limitations are too confining, the application can create a new screen instead.)

Accessing a Public Screen by Name

The main calls for accessing an existing public screen are LockPubScreen() and UnlockPubScreen(). To use these functions you need to know the name of the public screen you want to access. If you do not know the name of the public screen or if you are not sure, you can lock the default public screen with LockPubScreen(NULL).

struct Screen *LockPubScreen( UBYTE * );
VOID           UnlockPubScreen( UBYTE * , struct Screen *);

These calls enable the application to determine that a public screen exists, and to ensure its continued existence while opening a window on it. This function also serves as an improvement over the old GetScreenData() function from V34 by returning a pointer to the Screen structure of the locked screen so that its attributes can be examined.

Be sure to unlock the public screen when done with it. Note that once a window is open on the screen the program does not need to hold the screen lock, as the window acts as a lock on the screen. The pointer to the screen structure is valid as long as a lock on the screen is held by the application, or the application has a window open on the screen.

Locks should not be held without reason. Holding unnecessary locks on screens may prevent the user from closing a public screen that has no apparent activity. Keep in mind that as long as you have a window open on a public screen, the window acts as a lock preventing the screen from closing.

Shown here is a simple example of how to find the Workbench public screen using LockPubScreen() and UnlockPubScreen().

/* pubscreenbeep.c
 */
 
#include <exec/types.h>               /* Amiga data types.               */
#include <exec/libraries.h>
#include <intuition/intuition.h>      /* Lots of important Intuition     */
#include <intuition/screens.h>        /* structures we will be using.    */
 
#include <proto/exec.h>
#include <proto/intuition.h>
 
struct IntuitionIFace *IIntuition = NULL;
 
/* Simple example of how to find a public screen to work with.
 */
 
int main(int argc, char **argv)
{
  struct Screen  *my_wbscreen_ptr;     /* Pointer to the Workbench screen */
 
  /* Open the library before you call any functions */
  struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
  IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
 
  if (NULL != IIntuition)
  {
    if(NULL != (my_wbscreen_ptr = IIntuition->LockPubScreen("Workbench")))
    {
      /* OK found the Workbench screen.                      */
      /* Normally the program would be here.  A window could */
      /* be opened or the attributes of the screen copied    */
      IIntuition->DisplayBeep(my_wbscreen_ptr);
 
      IIntuition->UnlockPubScreen(NULL, my_wbscreen_ptr);
     }
  }
 
  IExec->DropInterface((struct Interface*)IIntuition);
  IExec->CloseLibrary(IntuitionBase);
  return 0;
}

The Default Public Screen and Workbench

As mentioned earlier, Workbench is a special case public screen because it is the initial default public screen. There are other reasons Workbench has a special status. Normally, it's the first thing the user sees because it is the default user interface on all Amiga computers. Many older applications written for V34 and earlier versions of the OS expect to run in the Workbench screen.

Because of its close ties with the operating system, there are some extra functions available to manipulate the Workbench screen. One function which controls both Workbench and other public screens is SetPubScreenModes(). This function controls the global public screen mode bits, SHANGHAI and POPPUBSCREEN. If the SHANGHAI mode bit is set, older applications which expect to open on the Workbench screen will open instead on the default public screen (which may or may not be the Workbench screen). The POPPUBSCREEN bit controls whether public screens will be popped to the front when a window is opened. These modes are documented in the "Intuition Windows" chapter in the section on "Windows and Screens".

Other functions which control the Workbench screen are listed in the table below.

Workbench Public Screen Functions
WBenchToBack() Move the Workbench screen behind all other screens.
WBenchToFront() Move the Workbench screen in front of all other screens.
OpenWorkBench() Open the Workbench screen. If the screen is already open, this call has no effect. This call will re-awaken the Workbench application if it was active when CloseWorkBench() was called.
CloseWorkBench() Attempt to reclaim memory used for the Workbench screen. If successful, this call closes the screen and puts the Workbench application to sleep. This call fails if any application has windows open or locks on the Workbench screen.

Programs can attempt to reclaim memory used by the Workbench screen by calling CloseWorkBench(). Programs that have closed Workbench, should call OpenWorkBench() as they exit or allow the user to re-open the screen through a menu or gadget.

If Workbench is closed, any of the following events can re-open it: calling OpenWorkBench(); opening a window on the Workbench (including EasyRequests() such as the DOS "Insert Disk" requester); calling LockPubScreen("Workbench"); calling LockPubScreen(NULL) when Workbench is the default public screen.

Taking a new Custom Screen Public

Applications that open a new screen should consider taking the screen public. If the screen's characteristics are not very esoteric, making the screen public is useful because it allows other applications to share the working context. This makes an application more powerful and more attractive to the user because it allows the user to add supporting applications and utilities from other vendors to make a customized and integrated work environment.

To make your own custom screen into a public screen that other applications may use, you give the screen a public name and then register the screen with Intuition. The screen must be declared as public in the OpenScreenTagList() call by specifying a public name string with the SA_PubName tag. The application's task ID and a signal bit may also be registered when the screen is opened with the SA_PubTask and SA_PubSig tags. If these tags are given, the system will signal your task when the last window on the screen closes.

When a new public screen is opened, it starts out private so the application can perform any desired initialization (for instance, opening a backdrop window) before the screen is made public. Use the PubScreenStatus() function to make the screen public and available to other applications (or to take the screen private again, later). The screen may not be taken private or closed until all windows on the screen are closed and all locks on the screen are released. However, the screen does not need to be made private before closing it.

CloseScreen() will fail if an attempt is made to close a public screen that still has visitor windows or locks on it. If the user selects close screen, but the screen will not close due to visitor windows, a requester should be displayed informing the user of the condition and instructing them to close any windows before closing the screen.

Searching the Public Screen List

To access an existing public screen the application may take one of three approaches. To get a lock on the default public screen, either LockPubScreen(NULL) or {WA_PubScreenName, NULL} may be used.

If the name of the screen is known, the application may use LockPubScreen(Name) to gain a lock on the screen as shown in the example above (or use OpenWindowTagList() with the WA_PubScreenName tag as described in the "Intuition Windows" chapter). Failure to lock the screen or open the window probably indicates that the screen does not exist.

A third approach is to search the public screen list for a screen that meets the requirements of the application. These requirements may be related to the name or attributes of the screen. Here are the functions to use with the public screen list maintained by Intuition.

Public Screen List Functions
LockPubScreenList() Lock the public screen list maintained by Intuition so that it may be quickly copied
UnlockPubScreenList() Release the lock on the public screen list
NextPubScreen() Find the next screen in the public screen list

The main function used to access the public screen list is LockPubScreenList(). This function, intended for use by the public screen manager utility, locks the list to allow data from it to be quickly copied. The list is stored in an Exec List structure, with each node in the list being a PubScreenNode structure. See <intuition/screens.h> for details.

Do not interpret the list while in a locked state, instead, copy any values required to local variables and release the lock. All required data must be copied, including the name of the screen which is not part of the structure. Pointers that reference the list or structures attached to the list are not valid after releasing the lock. Once the lock is released, the screen pointers in the list (psn_Screen) may be tested for equality against other screen pointers, but referencing any part of the screen structure from this pointer is strictly illegal. After the lock is released with UnlockPubScreenList(), the application may access the data in the screen structure by obtaining a lock on the screen using LockPubScreen() with the name of the screen.

The application should only require accessing three fields in the PubScreenNode, these are ln_Name, psn_Screen and psn_Flags. The name of the public screen is maintained in the ln_Name field of the Node (psn_Node) structure. Access to other information on the screen may be done by getting a lock on this name and reading the data from the Screen structure. The screen pointer (psn_Screen) may only be used for testing against other screen pointers, never reference the screen structure from this value. Finally, the public screen flags are maintained in psn_Flags. Currently, only PSNF_PRIVATE is defined for this field. PSNF_PRIVATE indicates that the screen is not currently public.

Remember that all information in the public screen list is transitory, that is, it may change at any time. Do not rely on the values in the list. The only way to ensure the existence or mode of a screen is to lock it, either directly with LockPubScreen() or by opening a window on the screen. To update the copy of the list, lock it and copy the data again. Don't forget to release the lock when finished.

As an alternative to dealing with the public screen list, NextPubScreen() can be used. This call takes the name of a public screen as its argument and returns the name of the next screen in the public screen list. This helps an application move a window through the entire rotation of public screens. Repeated calls to NextPubScreen() could be used to get the names of all public screens one at a time. Keep in mind though that the list of public screens is subject to sudden change; the task that owns a public screen might close it after you obtain the name, but before you access the screen.

Always use LockPubScreen() to access screen information after scanning the public screen list.

Cloning a Public Screen (Workbench)

User preferences for screen attributes are generally reflected in the Workbench screen or in the default public screen. In some cases it may be useful to create a new screen with the same attributes.

LockPubScreen() returns a pointer to the Screen structure of a specific screen. GetScreenDrawInfo() returns rendering information on the screen, such as the pen array and font used. QueryOverscan() returns the overscan information of a specific display mode (for more information, see the section on "Overscan and the Display Clip").

The example below shows how to use GetScreenDrawInfo() to examine the attributes of the Workbench screen so that a new screen with the same attributes can be created.

struct DrawInfo *GetScreenDrawInfo( struct Screen * );

The attributes required to clone an existing screen are its width, height, depth, pens and mode. The pens and screen depth are available through the DrawInfo structure. The width and height may be obtained from the Screen structure. (The width and height may be larger than the overscan area if the screen is scrollable, and autoscroll may always be enabled as it does not effect displays smaller than or equal to the overscan area.)

The screen's display mode can be obtained using the graphics library call GetVPModeID(). This call returns the display ID of an existing screen which can then be used as the data for the SA_DisplayID tag in OpenScreenTagList(). Note that the example assumes the screen should be open to the user's text overscan preference. If an exact copy of the display clip of the existing screen is required, use the VideoControl() command of the graphics library to access the ViewPortExtra structure.

The colors of the screen may be copied using the graphics library calls GetRGB4(), SetRGB4(), SetRGB4CM() and LoadRGB4(). The example code does not copy the colors.

The example copies the font from the cloned screen. A reasonable alternative would be to use the user's preference font, which may be accessed through the SA_SysFont tag.

/* clonescreen.c
** clone an existing public screen.
*/
 
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
 
#include <string.h>
 
VOID cloneScreen(CONST_STRPTR);
 
struct IntuitionIFace *IIntuition = NULL;
struct GraphicsIFace *IGraphics = NULL;
 
/*
** Open all libraries for the cloneScreen() subroutine.
*/
int main(int argc, char **argv)
{
  CONST_STRPTR pub_screen_name = "Workbench";
 
  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);
 
  if (IIntuition != NULL && IGraphics != NULL)
  {
    cloneScreen(pub_screen_name);
  }
 
  IExec->DropInterface((struct Interface*)IGraphics);
  IExec->CloseLibrary(GfxBase);
  IExec->DropInterface((struct Interface*)IIntuition);
  IExec->CloseLibrary(IntuitionBase);
  return 0;
}
 
/* Clone a public screen whose name is passed to the routine.
**    Width, Height, Depth, Pens, Font and DisplayID attributes are
** all copied from the screen.
**    Overscan is assumed to be OSCAN_TEXT, as there is no easy way to
** find the overscan type of an existing screen.
**    AutoScroll is turned on, as it does not hurt.  Screens that are
** smaller than the display clip will not scroll.
*/
 
VOID cloneScreen(CONST_STRPTR pub_screen_name)
{
struct Screen *my_screen;
ULONG  screen_modeID;
UBYTE *pub_scr_font_name;
UBYTE *font_name;
ULONG  font_name_size;
struct TextAttr pub_screen_font;
struct TextFont *opened_font;
 
struct Screen   *pub_screen = NULL;
struct DrawInfo *screen_drawinfo = NULL;
 
/* name is a pointer to the name of the public screen to clone */
pub_screen = IIntuitino->LockPubScreen(pub_screen_name);
if (pub_screen != NULL)
    {
    /* Get the DrawInfo structure from the locked screen
    ** This returns pen, depth and font info.
    */
    screen_drawinfo = IIntuition->GetScreenDrawInfo(pub_screen);
    if (screen_drawinfo != NULL)
        {
        screen_modeID = IGraphics->GetVPModeID(&(pub_screen->ViewPort));
        if( screen_modeID != INVALID_ID )
            {
            /* Get a copy of the font
            ** The name of the font must be copied as the public screen may
            ** go away at any time after we unlock it.
            ** Allocate enough memory to copy the font name, create a
            ** TextAttr that matches the font, and open the font.
            */
            pub_scr_font_name = screen_drawinfo->dri_Font->tf_Message.mn_Node.ln_Name;
            font_name_size = 1 + strlen(pub_scr_font_name);
            font_name = IExec->AllocVecTags(font_name_size, AVT_ClearWithValue, 0, TAG_END);
            if (font_name != NULL)
                {
                strcpy(font_name, pub_scr_font_name);
                pub_screen_font.ta_Name  = font_name;
                pub_screen_font.ta_YSize = screen_drawinfo->dri_Font->tf_YSize;
                pub_screen_font.ta_Style = screen_drawinfo->dri_Font->tf_Style;
                pub_screen_font.ta_Flags = screen_drawinfo->dri_Font->tf_Flags;
 
                opened_font = IGraphics->OpenFont(&pub_screen_font);
                if (opened_font != NULL)
                    {
                    /* screen_modeID may now be used in a call to
                    ** OpenScreenTagList() with the tag SA_DisplayID.
                    */
                    my_screen = IIntuition->OpenScreenTags(NULL,
                        SA_Width,      pub_screen->Width,
                        SA_Height,     pub_screen->Height,
                        SA_Depth,      screen_drawinfo->dri_Depth,
                        SA_Overscan,   OSCAN_TEXT,
                        SA_AutoScroll, TRUE,
                        SA_Pens,       screen_drawinfo->dri_Pens,
                        SA_Font,       &pub_screen_font,
                        SA_DisplayID,  screen_modeID,
                        SA_Title,      "Cloned Screen",
                        TAG_END);
                    if (my_screen != NULL)
                        {
                        /* Free the drawinfo and public screen as we don't
                        ** need them any more.  We now have our own screen.
                        */
                        IIntuition->FreeScreenDrawInfo(pub_screen,screen_drawinfo);
                        screen_drawinfo = NULL;
                        IIntuition->UnlockPubScreen(pub_screen_name,pub_screen);
                        pub_screen = NULL;
 
                        IDOS->Delay(300);   /* should be rest_of_program */
 
                        IIntuition->CloseScreen(my_screen);
                        }
                    IGraphics->CloseFont(opened_font);
                    }
                IExec->FreeVec(font_name);
                }
            }
        }
    }
 
/* 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(pub_screen_name,pub_screen);
}

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 chapter.

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 the chapter "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 the chapter "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 chapter 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 chapters 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().