;/* ScreenDisplayModes.c - V36 Screen Displaymode selector example
; Compiled with SAS C 5.05
lc -cfist -v -L -j73 ScreenDisplayModes.c
quit

Copyright (c) 1990 Commodore-Amiga, Inc.

This example is provided in electronic form by Commodore-Amiga,
Inc. for use with the Amiga Mail Volume II technical publication.
Amiga Mail Volume II contains additional information on the correct
usage of the techniques and operating system functions presented in
these examples.  The source and executable code of these examples may
only be distributed in free electronic form, via bulletin board or
as part of a fully non-commercial and freely redistributable
diskette.  Both the source and executable code (including comments)
must be included, without modification, in any copy.  This example
may not be published in printed form or distributed with any
commercial product. However, the programming techniques and support
routines set forth in these examples may be used in the development
of original executable software products for Commodore Amiga
computers.

All other rights reserved.

This example is provided "as-is" and is subject to change; no
warranties are made.  All use is at your own risk. No liability or
responsibility is assumed.
*/

#include <string.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <libraries/gadtools.h>
#include <graphics/displayinfo.h>
#include <graphics/text.h>
#include <exec/memory.h>
#include <exec/ports.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/alib_protos.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/gadtools_pragmas.h>

#define SWITCH 888
#define QUIT   999

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *GadToolsBase;
extern struct Library *SysBase;

void main(void);
void ShowDisplayModes(struct List * dlist);
struct Screen *OpenAScreen(struct DimensionInfo * di, UBYTE * name,
			   ULONG overscantype);
struct Window *OpenAWindow(struct Screen * screen, ULONG overscantype);

struct DisplayNode {
    struct Node dn_Node;
    struct DimensionInfo dn_Dimensioninfo;
};

/* The workbench compatible pens we'll use for that New Look. */
static UWORD dri_Pens[] = {0, 1, 1, 2, 1, 3, 1, 0, 3, ~0};

/* Gadtools menu stuff */
struct NewMenu sdm_menu[] =
{
    {NM_TITLE, "Project", 0, 0, 0, 0,},
    {NM_ITEM, "Switch Mode", "S", 0, 0, (void *) SWITCH,},
    {NM_ITEM, "Set Overscan", 0, 0, 0, 0,},
    {NM_SUB, "Text", 0, CHECKIT | CHECKED, ~1, (void *) OSCAN_TEXT,},
    {NM_SUB, "Standard", 0, CHECKIT, ~2, (void *) OSCAN_STANDARD,},
    {NM_SUB, "Max", 0, CHECKIT, ~4, (void *) OSCAN_MAX,},
    {NM_SUB, "Video", 0, CHECKIT, ~8, (void *) OSCAN_VIDEO,},
    {NM_ITEM, NM_BARLABEL, 0, CHECKIT, 0, 0,},
    {NM_ITEM, "Quit", "Q", 0, 0, (void *) QUIT,},
    {NM_END, NULL, 0, 0, 0, 0,},
};

UBYTE *OScanDescr[] = {NULL, "OSCAN_TEXT", "OSCAN_STANDARD",
			 "OSCAN_MAX", "OSCAN_VIDEO"};

struct EasyStruct failedES =
{
    sizeof(struct EasyStruct), 0, "SDM",
    "%s",
    "OK",
};

void
main(void)
{
    struct List *dlist;
    struct DisplayNode *dnode;
    struct DisplayNode *wnode, *nnode;

    ULONG modeID;
    ULONG skipID;

    ULONG result;
    struct DisplayInfo displayinfo;
    struct NameInfo nameinfo;

    /* Fails silently if not V36 */
    if (IntuitionBase = OpenLibrary("intuition.library", 36)) {
        if (GfxBase = OpenLibrary("graphics.library", 36)) {
            if (GadToolsBase = OpenLibrary("gadtools.library", 36)) {
                if (dlist = AllocMem(sizeof(struct List), MEMF_CLEAR)) {
                    NewList(dlist);

                    /*
                     * Don't want duplicate entries in the list for the
                     * 'default monitor', so we'll skip the the videomode
                     * for which default.monitor is the alias.
                     */

                    /* INVALID_ID indicates the beginning and the end
		     * of the list of available keys.
		     */
                    modeID = INVALID_ID;

                    GetDisplayInfoData(NULL, (UBYTE *) & displayinfo,
				       sizeof(struct DisplayInfo),
				       DTAG_DISP, LORES_KEY);
                    if (displayinfo.PropertyFlags & DIPF_IS_PAL)
                        skipID = PAL_MONITOR_ID;
                    else
                        skipID = NTSC_MONITOR_ID;
                    while ((modeID = NextDisplayInfo(modeID)) != INVALID_ID) {
                        if ((modeID & MONITOR_ID_MASK) != skipID) {
			    /*
			     * For this example,only 'named' keys are accepted.  Others
			     * which have no description are left out
 			     * even though they may be available.
 			     * HAM and EXTRAHALFBRIGHT are examples of this.
 			     * If needed a name could be made like '320x400 HAM Interlace'.
 			     */

                            if (result = GetDisplayInfoData(NULL,
							    (UBYTE *) & nameinfo,
							    sizeof(struct NameInfo),
							    DTAG_NAME,
							    modeID)) {
                                result = GetDisplayInfoData(NULL,
							    (UBYTE *) & displayinfo,
							    sizeof(struct DisplayInfo),
							    DTAG_DISP,
							    modeID);
                                if (!(displayinfo.NotAvailable)) {
                                    if (dnode =
					(struct DisplayNode *)
					AllocMem(sizeof(struct DisplayNode),
						 MEMF_CLEAR)) {
                                        result = GetDisplayInfoData(NULL,
						   (UBYTE *) &
						   (dnode->dn_Dimensioninfo),
						   sizeof(struct DimensionInfo),
						   DTAG_DIMS,
						   modeID);
                                        /* to keep it short: if NOMEM,
					 * just don't copy
					 */
                                        if (dnode->dn_Node.ln_Name
					    = AllocMem(strlen(nameinfo.Name)
						       + 1, MEMF_CLEAR))
                                            strcpy(dnode->dn_Node.ln_Name,
						   nameinfo.Name);
                                        AddTail(dlist, (struct Node *) dnode);
                                    } else {
                                        EasyRequest(NULL, &failedES, NULL,
						    "Out of memory");
                                        /* Force modeID to INVALID to break */
                                        modeID = INVALID_ID;
                                    }
                                }
                            }
                        }
                    }
                    ShowDisplayModes(dlist);

                    wnode = (struct DisplayNode *) dlist->lh_Head;
                    while (nnode = (struct DisplayNode *)
			   (wnode->dn_Node.ln_Succ)) {

                        if (wnode->dn_Node.ln_Name)
                            FreeMem(wnode->dn_Node.ln_Name,
				    strlen(wnode->dn_Node.ln_Name) +1);
                        Remove((struct Node *) wnode);
                        FreeMem(wnode, sizeof(struct DisplayNode));
                        wnode = nnode;
                    }
                    FreeMem(dlist, sizeof(struct List));
                } else
                    EasyRequest(NULL, &failedES, NULL, "Out of memory");
                CloseLibrary(GadToolsBase);
            }
            CloseLibrary(GfxBase);
        }
        CloseLibrary(IntuitionBase);
    }
}

void
ShowDisplayModes(struct List * dlist)
{
    struct Screen *screen;
    struct Window *window;
    struct Gadget *glist, *gadget, *hitgadget;
    struct DrawInfo *drawinfo;
    struct TextFont *defaultfont;
    struct TextAttr *textattr;
    struct IntuiMessage *imsg;
    struct NewGadget *ng;
    struct Menu *menu;
    struct MenuItem *item;
    void *vi;
    ULONG iclass, icode, code;
    struct DisplayNode *dnode;
    struct DimensionInfo *dimensioninfo;
    ULONG overscantype = OSCAN_TEXT;
    ULONG curmode = 0;
    BOOL ABORT = TRUE, OK;
    int i;


    if (ng = AllocMem(sizeof(struct NewGadget), MEMF_CLEAR)) {
        if (textattr = AllocMem(sizeof(struct TextAttr), MEMF_CLEAR)) {
            if (textattr->ta_Name = AllocMem(48, MEMF_CLEAR)) {

                dnode = (struct DisplayNode *) dlist->lh_Head;

                if (menu = CreateMenus(sdm_menu, TAG_DONE)) {
                    do {
                        dimensioninfo = &(dnode->dn_Dimensioninfo);
                        OK = FALSE;
                        if (screen = OpenAScreen(dimensioninfo,
						 dnode->dn_Node.ln_Name,
						 overscantype)) {
                            drawinfo = GetScreenDrawInfo(screen);
                            defaultfont = drawinfo->dri_Font;
                            strcpy(textattr->ta_Name,
				   defaultfont->tf_Message.mn_Node.ln_Name);

                            textattr->ta_YSize = defaultfont->tf_YSize;
                            textattr->ta_Style = defaultfont->tf_Style;
                            textattr->ta_Flags = defaultfont->tf_Flags;

                            if (window = OpenAWindow(screen, overscantype)) {
                                vi = GetVisualInfo(screen, TAG_END);
                                if (LayoutMenus(menu, vi, TAG_DONE)) {
                                    if (gadget = CreateContext(&glist)) {
                                        ng->ng_LeftEdge =
					  window->BorderLeft + 10;
                                        ng->ng_TopEdge =
					  window->BorderTop + 10;
                                        ng->ng_Width =
					  window->Width -
					    (window->BorderLeft + 10) -
					      (window->BorderRight + 10);
                                        ng->ng_Height =
					  window->Height -
					    (window->BorderTop + 10) -
					      (window->BorderBottom + 10) -
						(4 + defaultfont->tf_YSize + 2);
                                        ng->ng_TextAttr = textattr;
                                        ng->ng_GadgetText = NULL;
                                        ng->ng_VisualInfo = vi;
                                        ng->ng_GadgetID = 1;
                                        ng->ng_Flags = PLACETEXT_ABOVE;
                                        gadget =
					  CreateGadget(LISTVIEW_KIND, gadget,
						       ng, GTLV_Labels, dlist,
						       GTLV_ShowSelected, NULL,
						       GTLV_Selected, curmode,
						       TAG_END);

                                        ng->ng_TopEdge +=
					  gadget->Height +
					    defaultfont->tf_YSize + 10;
                                        ng->ng_Width = 80;
                                        ng->ng_LeftEdge =
					  ((window->Width - window->BorderLeft
					    - window->BorderRight) / 2) - 30;
                                        ng->ng_Height = defaultfont->tf_YSize + 8;
                                        ng->ng_GadgetID = 2;
                                        ng->ng_GadgetText = "OK";
                                        ng->ng_VisualInfo = vi;
                                        ng->ng_Flags = PLACETEXT_IN;
                                        gadget =
					  CreateGadget(BUTTON_KIND, gadget,
						       ng, TAG_END);

                                        AddGList(window, glist, -1, -1, NULL);
                                        RefreshGList(glist, window, NULL, -1);
                                        GT_RefreshWindow(window, NULL);
                                        SetMenuStrip(window, menu);
                                        ABORT = FALSE;
                                    } else
                                        EasyRequest(window, &failedES, NULL,
						    "Can't create gadget context");
                                } else
                                    EasyRequest(window, &failedES, NULL,
						"Can't layout menus");

                                do {
                                    WaitPort(window->UserPort);
                                    while (imsg = GT_GetIMsg(window->UserPort)) {
                                        iclass = imsg->Class;
                                        icode = imsg->Code;
                                        hitgadget =
					  (struct Gadget *) imsg->IAddress;
                                        GT_ReplyIMsg(imsg);

                                        switch (iclass) {
                                        case GADGETUP:
                                            if (hitgadget->GadgetID == 1) {
                                                dnode =
						  (struct DisplayNode *)
						    dlist->lh_Head;
                                                for (i = 0; i < icode; i++)
                                                dnode =
						  (struct DisplayNode *)
						    dnode->dn_Node.ln_Succ;
                                                curmode = i;
                                            }
                                            if (hitgadget->GadgetID == 2)
                                                OK = TRUE;
                                            break;
                                        case MENUPICK:
                                            while ((icode != MENUNULL)
						   && (ABORT == FALSE)) {
                                                item = ItemAddress(menu, icode);

                                                code = (ULONG) MENU_USERDATA(item);
                                                if (code == QUIT)
                                                    ABORT = TRUE;
                                                else if
                                                    (code == SWITCH)
                                                    OK = TRUE;
                                                else
                                                    overscantype = (ULONG) code;
                                                icode = item->NextSelect;
                                            }
                                            break;
                                        case CLOSEWINDOW:
                                            ABORT = TRUE;
                                            break;
                                        }
                                    }
                                } while (ABORT == FALSE && OK == FALSE);
                                ClearMenuStrip(window);
                                CloseWindow(window);
                                FreeVisualInfo(vi);
                                FreeGadgets(glist);
                            } else
                                EasyRequest(NULL, &failedES, NULL,
					    "Can't open window");
                            FreeScreenDrawInfo(screen, drawinfo);
                            CloseScreen(screen);
                        } else
                            EasyRequest(NULL, &failedES, NULL,
					"Can't open screen");
                    } while (ABORT == FALSE);
                    FreeMenus(menu);
                } else
                    EasyRequest(NULL, &failedES, NULL, "Can't create menus");
                FreeMem(textattr->ta_Name, 48);
            } else
                EasyRequest(NULL, &failedES, NULL, "Out of memory");
            FreeMem(textattr, sizeof(struct TextAttr));
        } else
            EasyRequest(NULL, &failedES, NULL, "Out of memory");
        FreeMem(ng, sizeof(struct NewGadget));
    } else
        EasyRequest(NULL, &failedES, NULL, "Out of memory");
}


/*
 * It's advised to use one of the overscan constants and
 * STDSCREENWIDTH and STDSCREENHEIGHT. For this example however, we'll
 * skip all that an use QueryOverscan to get the rectangle describing the
 * requested overscantype and pass that as the displayclip description.
 * Actually, since we pass the standard rectangle from the display database,
 * it is equivalent to the prefered:
 *
 * screen = OpenScreenTags(NULL,
 *                         SA_DisplayID, di->Header.DisplayID,
 *                         SA_Overscan, overscantype,
 *                         SA_Width, STDSCREENWIDTH,
 *                         SA_Height, STDSCREENHEIGHT,
 *                         SA_Title, name,
 *                         SA_Depth, 2,
 *                         SA_SysFont, 1,
 *                         SA_Pens, dri_Pens,
 *                         TAG_END);
 */

struct Screen *
OpenAScreen(struct DimensionInfo * di, UBYTE * name, ULONG overscantype)
{

    struct Rectangle rectangle;

    /* Can't fail, already made sure it's a valid displayID */
    QueryOverscan(di->Header.DisplayID, &rectangle, overscantype);


    return (OpenScreenTags(NULL,
                           SA_DisplayID, di->Header.DisplayID,
                           SA_DClip, &rectangle,
                           SA_Title, name,
                           SA_Depth, 2,
                           SA_SysFont, 1,   /* Use the prefered WB screen font */
                           SA_Pens, dri_Pens,
                           TAG_END));
}

struct Window *
OpenAWindow(struct Screen * screen, ULONG overscantype)
{
    return (OpenWindowTags(NULL,
                           WA_Top, screen->BarHeight + 1,
                           WA_Height, screen->Height - (screen->BarHeight + 1),
                           WA_CustomScreen, screen,
                           WA_IDCMP, CLOSEWINDOW | LISTVIEWIDCMP | MENUPICK,
                           WA_Flags, WINDOWCLOSE | ACTIVATE,
                           WA_Title, OScanDescr[overscantype],
                           TAG_END));
}


