Copyright (c) Hyperion Entertainment and contributors.

Display Database

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

The graphics library supports a variety of video monitors and programmable video modes. Inquiries about the availability of these modes, their dimensions and currently accessible options can be made through a database indexed by the same key information used to open Intuition screens. This design provides a good degree of compatibility with existing software, between differently equipped hardware platforms and for both static and dynamic data storage.

The software may be running on A1000 computers which will not have ECS, on A500 computers which may not have the latest ECS upgrade, and on A2000 computers which generally have the latest ECS but may not have a multi-sync monitor currently attached. This means that there are compatibility issues to consider–what should happen when a required ECS or monitor resource is not available for the desired mode.

Here are the compatibility criteria, in a simplified fashion:

Requires Release 2, and ECS Chips only
SuperHires mode (35nS pixel resolutions). This allows for very high horizontal resolutions with the ECS chip set and a standard NTSC or PAL monitor. (SuperHires has twice as much horizontal resolution as the old Hires mode.)
Requires Release 2, ECS Chips, and appropriate monitor
Productivity mode. This allows for flicker-free 640 x 480 color displays with the addition of a multi-sync or bi-sync 31 Khz monitor. (Productivity mode conforms, in a general way, to the VGA Mode 3 Standard 8514/A.)
Requires Release 2 (or the V35 of graphics.library under 1.3) and appropriate monitor only
A2024 Scan Conversion. This allows for a very high resolution grayscale display, typically 1,008<math>\times</math>800, suitable for desktop publishing or similar applications. A special video monitor is required (the monitor also supports the normal Amiga modes in greyscale).
Requires Release 2 but not ECS Chips or appropriate monitor
Display database inquiries. This allows for programmers to determine if the required resources are currently available for the requested mode.

In addition, there are fallback modes which resort to some reasonable display when a required resource is not available.

Monitors

Currently, there are five possible monitor settings in the display database (more may be added in future releases):

default.monitor
Since the default system monitor must be capable of displaying an image no matter what chips are installed or what software revision is in ROM, the graphics.library default.monitor is defined as a 15 kHz monitor which can display NTSC in the U.S. or PAL in Europe.
ntsc.monitor
Since the ECS chip set allows for dynamic choice of standard scan rates, NTSC applications running on European machines may choose to be displayed on the ntsc.monitor to preserve the aspect ratio.
pal.monitor
Since the ECS chip set allows for dynamic choice of standard scan rates, PAL applications running on American machines may choose to be displayed on the pal.monitor to preserve the aspect ratio.
multisync.monitor
Programmably variable scan rates from 15 kHz through 31 kHz or more. Responds to signal timings to decide what scan rate to display. Required for Productivity (640x480x2 non-interlaced) display.
A2024.monitor
Scan converter monitor which provides 1,008x800x2 (U.S.) or 1,008x1,024x2 (European) high-resolution, greyscale display. Does not require ECS. Does require Release 2 (or 1.3 V35) graphics library.

Modes

In V1.3 and earlier versions of the OS, the mode for a display was determined by a 16 bit-value specified either in the ViewPort.Modes field (for displays set up with the graphics library) or in the NewScreen.ViewModes field (for displays set up with Intuition). Prior to Release 2, it was sufficient to indicate the mode of a display by setting bits in the ViewPort.Modes field. Furthermore, programs routinely made interpretations about a given display mode based on bit-by-bit testing of this 16-bit value.

The approach taken in Release 2 and later is to utilize a 32-bit display mode specifier called a ModeID. The upper half of this specifier is called the monitor part and the lower half is informally called the mode part. There is a correspondence between the monitor part and the monitor’s operating modes (referred to as virtual monitors or MonitorSpecs after a system data structure).

For example, the A2024 monitor, PAL and NTSC are all different virtual monitors–the actual, physical monitor may be able to support more than one of these virtual types. Another concept added in Release 2 is the default monitor. The default monitor, represented by a zero value for the ModeID monitor part, may be either PAL or NTSC depending on a jumper on the motherboard.

Compatibility considerations-especially for IFF files and their CAMG chunk–have dictated very careful choices for the bit values which make up the mode part of the 32-bit ModeIDs. For example, the ModeIDs corresponding to the older, 1.3 display modes have been constructed out of a zero in the monitor part and the old 16-bit ViewPort.Modes bits in the lower part (after several extraneous bits such as SPRITES and VP_HIDE are cleared).

There are other such coincidences, but steps for compatibility with old programs notwithstanding, there is a new rule:

Rule
Programmers shall never interpret ModeIDs on a bit-by-bit basis.

For example, if the HIRES bit is set it does not mean the display is 640 pixels wide because there can also be a doubling of the beam scan rate. Programs should not attempt to interpret modes directly from the ViewPort.Modes field. The graphics library provides a suitable substitute for this information through its display database facility (explained below).

Likewise, the Mode of a ViewPort is no longer set directly. Instead it is set indirectly by associating the ViewPort with an abstract, 32-bit ModeID via the VideoControl() function.

These 32-bit ModeIDs have been carefully designed so that their lower 16 bits, when passed to graphics in the ViewPort.Modes field, provide some degree of compatibility between different systems. Older V1.3 programs will continue to work within the new scheme. (They will, however, not gain the benefits of the new modes and monitors available.)

Refer to the example program, WBClone.c, at the end of this section for examples on opening ViewPorts using a ModeID specification.

Mode Specification, Screen Interface

Opening an Intuition screen in one of the modes requires the specification of 32 bits of mode data. The NewScreen.ViewModes field is a UWORD (16 bits). Therefore, the function OpenScreenTags() must be used along with a SA_DisplayID tag which specifies the 32-bit ModeID. See Intuition Screens for more on this.

The display modes also introduce some complexity for applications that want to support “mode-sensitive” processing. If a program wishes to open a screen in the highest resolution that a user has available there are many more cases to handle. Therefore, it will become increasingly important to algorithmically layout a screen for correct, functional and aesthetic operation. All the information needed to be mode-flexible is available through the display database functions (explained below).

Mode Specification, ViewPort Interface

When working directly with graphics, the interface is based on View and ViewPort structures, rather than on Intuition’s Screen structure. As previously mentioned, information must be associated with the ViewPort to specify the modes and also with the View to specify what virtual monitor the whole View will be displayed on. There is also a lot of information to associate with a ViewPort regarding enhanced genlock capabilities.

This association of this data with the View is made through a display database system which has been added to graphics library. All correctly written programs that allocate a ColorMap structure for a ViewPort use the GetColorMap() function to do it. Hence, the ColorMap structure is used as the general purpose black box extension of the ViewPort data.

To set or obtain the data in the extended structures, use a function named VideoControl() takes a ColorMap as an argument. This allows the setting and getting of the extended display data. This mechanism is used to associate a DisplayInfo handle (not a ModeID) with a ViewPort. A DisplayInfo handle is an abstract link to the display database area associated with a particular ModeID. This handle is passed to the graphics database functions when getting or setting information about the mode.

Using VideoControl(), a program can enable, disable, or obtain the state of a ViewPort’s ColorMap, mode, genlock and other features. The function uses a tag based interface and returns NULL if no error occurred.

BOOL error = VideoControl( struct ColorMap *cm, struct TagItem *tag );

The first argument is a pointer to a ColorMap structure as returned by the GetColorMap() function. The second argument is a pointer to an array of video control tag items, used to indicate whether information is being given or requested as well as to pass (or receive the information). The tags you can use with VideoControl() include the following:

VTAG_ATTACH_CM_GET (or _SET) is used to obtain the ColorMap structure from the indicated ViewPort or attach a given ColorMap to it.

VTAG_VIEWPORTEXTRA_GET (or _SET) is used to obtain the ViewPortExtra structure from the indicated ColorMap structure or attach a given ViewPortExtra to it. A ViewPortExtra structure is an extension of the ViewPort structure and should be allocated and freed with GfxNew() and GfxFree() and associated with the ViewPort with VideoControl().

VTAG_NORMAL_DISP_GET (or _SET) is used to obtain or set the DisplayInfo structure for the standard or “normal” mode.

See <graphics/videocontrol.h> for a list of all the available tags. See the section on genlocking for information on using VideoControl() to interact with the Amiga’s genlock capabilities. Note that the graphics library will modify the tag list passed to VideoControl().

Coexisting Modes

Each display mode specifies (among other things) a pixel resolution and a monitor scan rate. Though the Amiga has the unique ability to change pixel resolutions on the fly, it is not possible to change the speed of a monitor beam in mid-frame.

Therefore, if you set up a display of two or more ViewPorts in different display modes requiring different scan rates, at least one of the ViewPorts will be displayed with the wrong scan rate.

Such ViewPorts can be coerced into a different mode designed for the scan rate currently in effect. You can do this in a couple of ways, introducing or removing interlace to adjust the vertical dimension, and changing to faster or slower pixels (higher or lower resolution) for the horizontal dimension.

The disadvantage of introducing interlace is flicker. The disadvantage of increasing resolution is the lessening of the video bus bandwidth and possibly a reduction in the number of colors or palette resolution.

Under Intuition, the frontmost screen determines which of the conflicting modes will take precedence. With the graphics library, the Modes field of the View and its frontmost ViewPort or the MonitorSpec of the ViewExtra determine the scan rate. For some monitors (such as the A2024), simultaneous display is excluded. This is a requirement only because the A2024 modes require very special and intricate display Copper list management.

ModeID Identifiers

The following definitions appear in the include file <graphics/displayinfo.h>. These values form the 32-bit ModeID which consists of a _MONITOR_ID in the upper word, and a _MODE_KEY in the lower word. Never interpret these bits directly. Instead use them with the display database to obtain the information you need about the display mode.

/* normal identifiers */
 
#define MONITOR_ID_MASK                 0xFFFF1000
 
#define DEFAULT_MONITOR_ID              0x00000000
#define NTSC_MONITOR_ID                 0x00011000
#define PAL_MONITOR_ID                  0x00021000
 
/* the following 20 composite keys are for Modes on the default Monitor */
/* NTSC & PAL "flavors" of these particular keys may be made by OR'ing  */
/* the NTSC or PAL MONITOR_ID with the desired MODE_KEY... */
 
#define LORES_KEY                       0x00000000
#define HIRES_KEY                       0x00008000
#define SUPER_KEY                       0x00008020
#define HAM_KEY                         0x00000800
#define LORESLACE_KEY                   0x00000004
#define HIRESLACE_KEY                   0x00008004
#define SUPERLACE_KEY                   0x00008024
#define HAMLACE_KEY                     0x00000804
#define LORESDPF_KEY                    0x00000400
#define HIRESDPF_KEY                    0x00008400
#define SUPERDPF_KEY                    0x00008420
#define LORESLACEDPF_KEY                0x00000404
#define HIRESLACEDPF_KEY                0x00008404
#define SUPERLACEDPF_KEY                0x00008424
#define LORESDPF2_KEY                   0x00000440
#define HIRESDPF2_KEY                   0x00008440
#define SUPERDPF2_KEY                   0x00008460
#define LORESLACEDPF2_KEY               0x00000444
#define HIRESLACEDPF2_KEY               0x00008444
#define SUPERLACEDPF2_KEY               0x00008464
#define EXTRAHALFBRITE_KEY              0x00000080
#define EXTRAHALFBRITELACE_KEY          0x00000084
 
/* vga identifiers */
 
#define VGA_MONITOR_ID                  0x00031000
 
#define VGAEXTRALORES_KEY               0x00031004
#define VGALORES_KEY                    0x00039004
#define VGAPRODUCT_KEY                  0x00039024
#define VGAHAM_KEY                      0x00031804
#define VGAEXTRALORESLACE_KEY           0x00031005
#define VGALORESLACE_KEY                0x00039005
#define VGAPRODUCTLACE_KEY              0x00039025
#define VGAHAMLACE_KEY                  0x00031805
#define VGAEXTRALORESDPF_KEY            0x00031404
#define VGALORESDPF_KEY                 0x00039404
#define VGAPRODUCTDPF_KEY               0x00039424
#define VGAEXTRALORESLACEDPF_KEY        0x00031405
#define VGALORESLACEDPF_KEY             0x00039405
#define VGAPRODUCTLACEDPF_KEY           0x00039425
#define VGAEXTRALORESDPF2_KEY           0x00031444
#define VGALORESDPF2_KEY                0x00039444
#define VGAPRODUCTDPF2_KEY              0x00039464
#define VGAEXTRALORESLACEDPF2_KEY       0x00031445
#define VGALORESLACEDPF2_KEY            0x00039445
#define VGAPRODUCTLACEDPF2_KEY          0x00039465
#define VGAEXTRAHALFBRITE_KEY           0x00031084
#define VGAEXTRAHALFBRITELACE_KEY       0x00031085
 
/* a2024 identifiers */
 
#define A2024_MONITOR_ID                0x00041000
 
#define A2024TENHERTZ_KEY               0x00041000
#define A2024FIFTEENHERTZ_KEY           0x00049000
 
/* prototype identifiers */
 
#define PROTO_MONITOR_ID                0x00051000

The Display Database and the DisplayInfo Record

For each ModeID, the graphics library has a body of data that enables the set up of the display hardware and provides applications with information about the properties of the display mode.

The display information in the database is accessed by searching it for a record with a given ModeID. For performance reasons, a look-up function named FindDisplayInfo() is provided which, given a ModeID, will return a handle to the internal data record about the attributes of the display.

This handle is then used for queries to the display database and specification of display mode to the low-level graphics routines. It is never used as a pointer. The private data structure associated with a given ModeID is called a DisplayInfo. From the <graphics/displayinfo.h> include file:

/* The "public" handle to a DisplayInfo  */
typedef APTR DisplayInfoHandle;

In order to obtain database information about an existing ViewPort, you must first gain reference to its 32-bit ModeID. A graphics function GetVPModeID() simplifies this operation:

uint32 modeID = GetVPModeID(struct ViewPort *vp )

The vp argument is pointer to a ViewPort structure. This function returns the normal display ModeID, if one is currently associated with this ViewPort. If no ModeID exists this function returns INVALID_ID.

Each valid 32-bit ModeID is associated with data initialized by the graphics library at powerup. This data is accessed by obtaining a handle to it with the graphics function FindDisplayInfo().

DisplayInfoHandle handle = FindDisplayInfo(uint32 modeID);

Given a 32-bit ModeID key (modeID in the prototype above) FindDisplayInfo() returns a handle to a valid DisplayInfoRecord found in the graphics database, or NULL. Using this handle, you can obtain information about this video mode, including its default dimensions, properties and whether it is currently available for use.

For instance, you can use a DisplayInfoHandle with the GetDisplayInfoData() function to look up the properties of a mode (see below). Or use the DisplayInfoHandle with VideoControl() and the VTAG_NORMAL_DISP_SET tag to set up a custom ViewPort.

Accessing the DisplayInfo

Basic information about a display can be obtained by calling the graphics function GetDisplayInfoData(). You also call this function during the set up of a ViewPort.

uint32 result = GetDisplayInfoData( DisplayInfoHandle handle, uint8 *buf,
                                    uint32 size, uint32 tagID, uint32 modeID );

Set the handle argument to the DisplayInfoHandle returned by a previous call to FindDisplayInfo(). This function will also accept a 32-bit ModeID directly as an argument. The handle argument should be set to NULL in that case.

The buf argument points to a destination buffer you have set up to hold the information about the properties of the display. The size argument gives the size of the buffer which depends on the type of inquiry you make.

The tagID argument specifies the type information you want to know about and may be set as follows:

DTAG_DISP
Returns display properties and availability information (the buffer should be set to the size of a DisplayInfo structure).
DTAG_DIMS
Returns default dimensions and overscan information (the buffer should be set to the size of a DimensionInfo structure).
DTAG_MNTR
Returns monitor type, view position, scan rate, and compatibility (the buffer should be set to the size of a MonitorInfo structure).
DTAG_NAME
Returns the user friendly name for this mode (the buffer should be set to the size of a NameInfo structure).

If the call succeeds, result is positive and reports the number of bytes actually transferred to the buffer. If the call fails (no information for the ModeID was available), result is zero.

Mode Availability

Even if the video monitor (NTSC, PAL, VGA, A2024) or ECS chips required to support a given mode are not available, there will be a DisplayInfo for all of the display modes. (This will not be the case for disk-based modes such as Euro36, Euro72, etc.)

Thus, the graphics library provides the ModeNotAvailable() function to determine whether a given mode is available, and if not, why not. Data corruption might cause the look-up function, GetVPModeID(), to fail even when it should not, so the careful programmer will always test the look-up function’s return value.

uint32 error = ModeNotAvailable( uint3modeID );

The modeID argument is again a 32-bit ModeID as shown in <graphics/displayinfo.h>. This function returns an error code, indicating why this modeID is not available, or NULL if there is no known reason why this mode should not be there. The ULONG return values from this function are a proper superset of the DisplayInfo.NotAvailable field (defined in <graphics/displayinfo.h>).

The graphics library checks for the presence of the ECS chips at power up, but the monitor attached to the system cannot be detected and so must be specified by the user through a separate utility named “AddMonitor”.

Accessing the MonitorSpec

The OpenMonitor() function will locate and open the requested MonitorSpec. It is called with either the name of the monitor or a ModeID.

struct MonitorSpec *mspc = OpenMonitor(STRPTR name, uint32 modeID);

If the name argument is non-NULL, the MonitorSpec is chosen by name. If the name argument is NULL, the MonitorSpec is chosen by ModeID. If both the name and ModeID arguments are NULL, a pointer to the MonitorSpec for the default monitor is returned. OpenMonitor() returns either a pointer to a MonitorSpec structure, or NULL if the requested MonitorSpec could not be opened. The CloseMonitor() function relinquishes access to a MonitorSpec previously acquired with OpenMonitor().

To set up a View a ViewExtra structure must also be created and attached to it. The ViewExtra.Monitor field must be initialized to the address of a valid MonitorSpec structure before the View is displayed. Use OpenMonitor() to initialize the Monitor field.

Mode Properties

Here is an example of how to query the properties of a given mode from a DisplayInfoHandle.

#include <graphics/displayinfo.h>
 
void check_properties(DisplayInfoHandle handle)
{
    struct DisplayInfo queryinfo;
 
    /* fill in the displayinfo buffer with basic Mode display data */
 
    if(IGraphics->GetDisplayInfoData(handle, (uint8 *)& queryinfo, sizeof(queryinfo), DTAG_DISP, NULL))
    {
        /* check for Properties of this Mode */
 
        if(queryinfo.PropertyFlags)
        {
            if(queryinfo.PropertyFlags & DIPF_IS_LACE)
                IDOS->Printf("mode is interlaced");
            if(queryinfo.PropertyFlags & DIPF_IS_DUALPF)
                IDOS->Printf("mode has dual playfields");
            if(queryinfo.PropertyFlags & DIPF_IS_PF2PRI)
                IDOS->Printf("mode has playfield two priority");
            if(queryinfo.PropertyFlags & DIPF_IS_HAM)
                IDOS->Printf("mode uses hold-and-modify");
            if(queryinfo.PropertyFlags & DIPF_IS_ECS)
                IDOS->Printf("mode requires the ECS chip set");
            if(queryinfo.PropertyFlags & DIPF_IS_PAL)
                IDOS->Printf("mode is naturally displayed on pal.monitor");
            if(queryinfo.PropertyFlags & DIPF_IS_SPRITES)
                IDOS->Printf("mode has sprites");
            if(queryinfo.PropertyFlags & DIPF_IS_GENLOCK)
                IDOS->Printf("mode is compatible with genlock displays");
            if(queryinfo.PropertyFlags & DIPF_IS_WB)
                IDOS->Printf("mode will support workbench displays");
            if(queryinfo.PropertyFlags & DIPF_IS_DRAGGABLE)
                IDOS->Printf("mode may be dragged to new positions");
            if(queryinfo.PropertyFlags & DIPF_IS_PANELLED)
                IDOS->Printf("mode is broken up for scan conversion");
            if(queryinfo.PropertyFlags & DIPF_IS_BEAMSYNC)
                IDOS->Printf("mode supports beam synchronization");
        }
    }
}

Nominal Values

Some of the display information is initialized in ROM for each mode such as recommended nominal (or default) dimensions. Even though this information is presumably static, it would still be a mistake to hardcode assumptions about these nominal values into your code.

Gathering information about the nominal dimensions of various modes is handled in a fashion similar to to the basic queries above. Here is an example of how to query the nominal dimensions of a given mode from a DisplayInfoHandle.

#include <graphics/displayinfo.h>
 
void check_dimensions(DisplayInfoHandle handle)
{
    struct DimensionInfo query;
 
    /* fill the buffer with Mode dimension information */
 
    if(IGraphics->GetDisplayInfoData(handle, (uint8 *)&query, sizeof(query), DTAG_DIMS, NULL))
    {
        /* display Nominal dimensions of this Mode */
 
        IDOS->Printf("nominal width  = %ld",
                query.Nominal.MaxX - query.Nominal.MinX + 1);
 
        IDOS->Printf("nominal height = %ld",
                query.Nominal.MaxY - query.Nominal.MinY + 1);
    }
}

Preference Items

Some display information is changed in response to user Preference specification. Until further notice, this will be reserved as a system activity and use private interface methods.

One Preferences setting that may affect the display data is the user’s preferred overscan limits to the monitor associated with this mode. Here is an example of how to query the overscan dimensions of a given mode from a DisplayInfoHandle.

#include <graphics/displayinfo.h>
 
void check_overscan(DisplayInfoHandle handle)
{
    struct DimensionInfo query;
 
    /* fill the buffer with Mode dimension information */
 
    if(IGraphics->GetDisplayInfoData(handle, (uint8 *)&query, sizeof(query), DTAG_DIMS, NULL))
    {
        /* display standard overscan dimensions of this Mode */
 
        IDOS->Printf("overscan width  = %ld",
                query.StdOScan.MaxX - query.StdOScan.MinX + 1);
 
        IDOS->Printf("overscan height = %ld",
                query.StdOScan.MaxY - query.StdOScan.MinY + 1);
    }
}

Run-Time Name Binding of Mode Information

It is useful to associate common names with the various display modes. The graphics library includes a provision for binding a name to a display mode so that it will be available via a query. This will be useful in the implementation of a standard screen-format requester. Note however that no names are bound initially since the bound names will take up RAM at all times. Instead defaults are used.

Bound names will override the defaults though, so that, until the screen-format requester is localized to a non-English language, the modes can be localized by binding foreign language names to them. Here is an example of how to query the run-time name binding of a given mode from a DisplayInfoHandle.

#include <graphics/displayinfo.h>
 
void check_name_bound(DisplayInfoHandle handle)
{
    struct NameInfo query;
 
    /* fill the buffer with Mode dimension information */
 
    if(IGraphics->GetDisplayInfoData(handle, (uint8 *)&query, sizeof(query), DTAG_NAME, NULL))
    {
        IDOS->Printf("%s", query.Name);
    }
}

Naming Graphics Display Modes

The graphics.library database contains a list of all available display modes on any given Amiga. Each mode has a given set of attributes, including possibly a name. Only a subset of the available modes in the system have names. The other modes are less significant and don't have names directly associated with them.

When showing a list of available modes to the user, unnamed modes become a problem. How should they be presented to the user? A simple solution is to dynamically construct names for unnamed modes. The name construction can be based on a mode's attributes, resulting in a descriptive name for the mode.

The NameMode() routine in the program below accepts a graphics.library mode id, and a string buffer, and fills-in the string buffer with the name of the given mode. If the mode has a real name entry in the graphics database, that name is returned. If there is no real name entry, a name is constructed for the mode based on the mode's properties. A routine very similar to this is present in the ASL screen mode requester, and in the ScreenMode prefs editor.

The following program outputs the names of all current graphics database modes to the console.

Sample Program

#include <exec/types.h>
#include <graphics/displayinfo.h>
#include <string.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/utility.h>
 
/*****************************************************************************/
 
#define MONITOR_PART(id) ((id) & MONITOR_ID_MASK)
 
/*****************************************************************************/
 
struct GraphicsIFace *IGraphics = 0;
 
/*****************************************************************************/
 
BOOL NameMode (uint32 modeID, STRPTR result, uint32 bufsize)
{
  struct NameInfo nameInfo;
  struct DisplayInfo dispInfo;
  struct DimensionInfo dimInfo;
  struct MonitorInfo monInfo;
  char buffer[DISPLAYNAMELEN + 1];
 
  result[0] = 0;
 
  DisplayInfoHandle dh = IGraphics->FindDisplayInfo (modeID);
  if (IGraphics->GetDisplayInfoData(dh, (APTR) & dispInfo, sizeof (struct DisplayInfo),
                          DTAG_DISP, INVALID_ID), INVALID_ID)
  {
    if (!dispInfo.NotAvailable)
    {
      if (IGraphics->GetDisplayInfoData (dh, (APTR) & dimInfo, sizeof (struct DimensionInfo),
                              DTAG_DIMS, INVALID_ID))
      {
        /* Get name or make one if no name available */
        if (IGraphics->GetDisplayInfoData (dh, (APTR) & nameInfo, sizeof (struct NameInfo),
                          DTAG_NAME, INVALID_ID))
        {
          IUtility->Strlcpy(result, nameInfo.Name, bufsize);
          return (TRUE);
        }
        else
        {
          if (IGraphics->GetDisplayInfoData (dh, (APTR) & monInfo, sizeof (struct MonitorInfo),
                            DTAG_MNTR, INVALID_ID))
          {
            if ((monInfo.Mspc) && (monInfo.Mspc->ms_Node.xln_Name))
            {
              IUtility->Strlcpy(buffer, monInfo.Mspc->ms_Node.xln_Name, bufsize);
              size_t len = strlen (buffer);
              if ((len > 8) && (IUtility->Strnicmp (&buffer[len - 8], ".monitor", len - 8) == 0))
              {
                buffer[len - 8] = 0;
                len -= 8;
              }
 
              while (len > 0)
                buffer[--len] = IUtility->ToUpper(buffer[len]);
            }
          }
 
          IUtility->SNPrintf(result, bufsize, "%s:%lu x %lu %s%s%s",
            buffer,
            (dimInfo.Nominal.MaxX - dimInfo.Nominal.MinX + 1),
            (dimInfo.Nominal.MaxY - dimInfo.Nominal.MinY + 1),
            (dispInfo.PropertyFlags & DIPF_IS_HAM) ? "HAM " :
            (dispInfo.PropertyFlags & DIPF_IS_EXTRAHALFBRITE) ? "EHB " : "",
            (dispInfo.PropertyFlags & DIPF_IS_PF2PRI) ? "DPF2 " :
            (dispInfo.PropertyFlags & DIPF_IS_DUALPF) ? "DPF " : "",
            (dispInfo.PropertyFlags & DIPF_IS_LACE) ? "Laced " : "", "");
 
          return (TRUE);
        }
      }
    }
  }
 
  return (FALSE);
}
 
/*****************************************************************************/
 
int main()
{
  struct Library *gfxBase = IExec->OpenLibrary("graphics.library", 50);
  IGraphics = (struct GraphicsIFace*)IExec->GetInterface(gfxBase, "main", 1, NULL);
 
  if (IGraphics != NULL)
  {
    uint32 modeID = INVALID_ID;
    while ((modeID = IGraphics->NextDisplayInfo (modeID)) != INVALID_ID)
    {
      if (MONITOR_PART (modeID))  /* ignore "default" monitor */
      {
        char name[64];
        if (NameMode (modeID, name, sizeof(name)))
        {
          IDOS->Printf("%s\n", name);
        }
      }
    }
  }
 
  IExec->DropInterface((struct Interface*)IGraphics);
  IExec->CloseLibrary(gfxBase);
 
  return 0;
}