
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/sprite.h>
#include <intuition/intuition.h>

#define SPMXWD      (16)
#define SPMXHT      (16)
#define SPRWD       (14)
#define SPRHT       (16)

#define LREV        (1)     /* generic library revision     */
#define INAM        ("intuition.library")
#define GNAM        ("graphics.library")

typedef struct IntuitionBase *  ib_t;
typedef struct GfxBase *        gb_t;
typedef struct Preferences *    pr_t;

struct SpriteImage {            /* should be in graphics/sprite.h   */
    UWORD posctl[2];
    UWORD sprdata[2][SPRHT];
    UWORD reserved[2];
};

#define MEMFLAGS    (MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR)

ib_t IntuitionBase;
gb_t GfxBase;

struct Window *wp;
struct Screen *sp;
struct SpriteImage *si;
struct Window *mywin;

APTR *OpenLibrary();

main(c, v)
char **v;
{
    struct Preferences *prefbu;
    register i;
    int opxo, opyo;
    int wd, ht;
    USHORT *optr;
    struct Window *getwin();
    struct IntuiMessage *msg;
    struct IntuiMessage *GetMsg();
    ULONG Class;
    void die(), modsi();

    if (*++v) {
        if ((wd=atoi(*v))>SPMXWD) wd=SPMXWD;
    }
    else wd=SPRWD;
    if (*++v){
        if ((ht=atoi(*v))>SPMXHT) ht=SPMXHT;
    }
    else ht=SPRHT;
    IntuitionBase=GfxBase=sp=si=mywin=NULL;

    if (!(IntuitionBase=(ib_t)OpenLibrary(INAM, LREV))) die();
    if (!(GfxBase=(gb_t)OpenLibrary(GNAM, LREV))) die();
    for (sp=IntuitionBase->FirstScreen; sp; sp=sp->NextScreen)
        if ((sp->Flags&SCREENTYPE)==WBENCHSCREEN)
            break;
    if (!sp) die();     /* not likely   */
    if (!(prefbu=(pr_t)AllocMem(sizeof(struct Preferences), MEMFLAGS)))
        die();
    GetPrefs(prefbu, sizeof(struct Preferences));
    if (!(optr=AllocMem(sizeof(prefbu->PointerMatrix), MEMFLAGS))) die();
    memcpy(optr, prefbu->PointerMatrix, sizeof(prefbu->PointerMatrix));
    opxo=prefbu->XOffset;
    opyo=prefbu->YOffset;
    FreeMem(prefbu, sizeof(struct Preferences));
    if (!(mywin=getwin(sp))) die();
    if (!(si=getsi())) die();
    for (;;){
        if (msg=GetMsg(mywin->UserPort)){
            Class=msg->Class;
            ReplyMsg(msg);
            if (Class==CLOSEWINDOW){
                while (msg=GetMsg(mywin->UserPort))
                    ReplyMsg(msg);
                Forbid();
                for (wp=sp->FirstWindow; wp; wp=wp->NextWindow)
                    SetPointer(wp, optr, SPMXHT, SPMXWD, opxo, opyo);
                Permit();
                die();
            }
        }
        modsi(si, wd, ht);
        wp=IntuitionBase->ActiveWindow;
        WaitTOF();
        SetPointer(wp, si, ht, wd, 0, 0);
    }
    /* NOTREACHED   */
}

struct Window *
getwin(sp)
struct Screen *sp;
{
    struct NewWindow nw;
    char *title="Pointer Abuzz!";

    memset(&nw, 0, sizeof(nw));
    nw.Height=sp->BarHeight&0xFF;
    nw.Width=TextLength(&sp->RastPort, title, strlen(title)&0xFFFF);
    nw.Width+=84;  /* estimate of gadget size  */
    nw.Title=title;
    nw.LeftEdge=sp->Width/2-nw.Width/2;
    nw.DetailPen=nw.BlockPen=-1;
    nw.Flags=WINDOWDEPTH|WINDOWCLOSE|WINDOWDRAG|NOCAREREFRESH;
    nw.IDCMPFlags=CLOSEWINDOW;
    nw.Type=WBENCHSCREEN;
    return(OpenWindow(&nw));
}

struct SpriteImage *
getsi(ht)
{
    struct SpriteImage *ts;

    if (!(ts=AllocMem(sizeof(*ts), MEMFLAGS))) return(NULL);
    ts->posctl[1]=(ht<<8);   /* initialize sprite hite   */
    return(ts);
}

void
modsi(si, wd, ht)
struct SpriteImage *si;
unsigned wd, ht;
{
    register i, color;
    USHORT dot, msk, tmp1, tmp2, dw;
    register USHORT *tsp;
    static ypos, xpos, dx, dy;

    if (!dx){    /* initialize   */
        dx=dy=1;
        Forbid();
        memset(si->sprdata, 0, sizeof(si->sprdata));
        Permit();
    }

    dw=SPMXWD-wd;   /* eliminate left margin when wd < max  */
    i=ypos<<1;
    msk=((1<<xpos)|(1<<((wd-1)-xpos))); /* get points of interest   */
    color=Random(4);

    Forbid();
    tsp=si->sprdata;    /* point to sprite's image          */

    dot=tsp[i]>>dw;     /* get current line's first word    */

    switch(color){
        case 0:
            tmp1=dot&~msk;  /* figure current line's new first word     */
            tmp2=dot&~msk;  /* figure current line's new second word    */
            break;
        case 1:
            tmp1=dot|msk;
            tmp2=dot&~msk;
            break;
        case 2:
            tmp1=dot&~msk;
            tmp2=dot|msk;
            break;
        case 3:
            tmp1=dot|msk;
            tmp2=dot|msk;
            break;
    }
    tmp1<<=dw;      /* left-justify the new line    */
    tmp2<<=dw;
    tsp[i]=tsp[((ht<<1)-2)-i]=tmp1;         /* set new 1st word */
    tsp[i+1]=tsp[(((ht<<1)-2)-i)+1]=tmp2;   /* set new 2nd word */

    Permit();
    ypos+=dy;
    xpos+=dx;
    if (ypos<0||ypos>=(ht>>1)){
        dy*=-1;
        ypos+=dy;
    }
    if (xpos<0||xpos>=(wd>>1)){
        dx*=-1;
        xpos+=dx;
    }
    return;
}

void
die()
{
    if (mywin) CloseWindow(mywin);
    if (si) FreeMem(si, sizeof(*si));
    if (GfxBase) CloseLibrary(GfxBase);
    if (IntuitionBase) CloseLibrary(IntuitionBase);
    exit(700);
}

