
/*
    9/88

    DIRK v0.01 -- Tune workbench colors to system performance

    Copyright (C) 1988 by Daniel Elbaum

    This software is freely redistributable provided:
    the three files which comprise it (dirk, dirk.c, dirk.doc)
    remain intact; that all copyright notices contained in
    any of the aforementioned files remain intact; and that
    no fee beyond reasonable remuneration for collation and
    distribution be charged for use and/or distribution.

*/


#include <exec/types.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <intuition/intuition.h>

#define IREV    (1)
#define GREV    (1)
#define CMAX    (15)    /* max val for color component (hardware)   */

/*
    seems odd, but really makes sense, since background
    has 2 color components, and detail has 3.
    These limits are to ensure enough contrast for readability.
*/

struct g_flags {
    int gran;   /* granularity of mapping from #tasks to pen brightness */
    int bsmax;  /* constant total saturation of background (red+blue)   */
    int dsmax;  /* maximum detail brightness (if high, text glares)     */
    int dsmin;  /* darkest gray for detail pen                          */
    int dstog;  /* set to activate task watch                           */
    int bstog;  /* set to activate memory watch                         */
    int intvl;  /* number of ticks between updates                      */
    int bd_r;   /* base detail pen red component                        */
    int bd_g;   /* base detail pen green component                      */
    int bd_b;   /* base detail pen blue component                       */
}   g_f;    /* initialized in getargs() */


typedef struct IntuitionBase *  t_ib;
typedef struct GfxBase * t_gb;

t_ib IntuitionBase;
t_gb GfxBase;

extern struct ExecBase *SysBase;

struct IntuiMessage *GetMsg();
void *OpenLibrary();
ULONG AvailMem();

main(c, v)
char **v;
{
    register i, active=1;
    register  ULONG tm;
    ULONG fu1, fu2;
    register struct Screen *sp;
    register struct Window *wp;
    register struct IntuiMessage *msg=NULL;
    struct Window *getwin();
    ULONG totmem();

    if (getargs(++v, &g_f)<0) exit(10);

    if (!(IntuitionBase=(t_ib)OpenLibrary("intuition.library", IREV)))
        exit(99);
    if (!(GfxBase=(t_gb)OpenLibrary("graphics.library", GREV))){
        CloseLibrary(IntuitionBase);
        exit(98);
    }
    if (!(sp=IntuitionBase->FirstScreen)){
        CloseLibrary(IntuitionBase);
        CloseLibrary(GfxBase);
        exit(20);
    }
    for (; sp; sp=sp->NextScreen)
        if (!strcmp(sp->Title, "Workbench Screen"))
            break;
    if (!sp) {
        CloseLibrary(IntuitionBase);
        CloseLibrary(GfxBase);
        exit(22);
    }
    if (!(wp=getwin())) exit(FALSE);
    if (g_f.bstog) tm=totmem();
    for (;;) {
        if (!msg) msg=GetMsg(wp->UserPort);
        if (msg){
            ReplyMsg(msg);
            if (msg->Class==ACTIVEWINDOW)
                active=1;
            if (msg->Class==INACTIVEWINDOW)
                active=0;
            if (msg->Class==CLOSEWINDOW){
                CloseWindow(wp);
                CloseLibrary(IntuitionBase);
                CloseLibrary(GfxBase);
                exit(0);
            }
            msg=NULL;
        }
        if (active)
            WaitTOF();
        else{
            for (i=0; i<g_f.intvl; ++i)
                if (msg=GetMsg(wp->UserPort)) break;
                else WaitTOF();
        }
        setcolor(sp, tm);
    }
    /* NOTREACHED   */
}

setcolor(sp, tm)
register struct Screen *sp;
ULONG tm;
{
    register f, db, t;
    int tr, tw;
    int r, g, b;
    ULONG mu, fm;
    ULONG amtfree();

    if (g_f.dstog){
        tqlen(&tr, &tw);

        f=CMAX-(tr+tw)/g_f.gran;
        if (f<g_f.dsmin) f=g_f.dsmin;
        if (f>g_f.dsmax) f=g_f.dsmax;
        if ((t=tr/g_f.gran+f)>CMAX) t=CMAX;
    }

    if (g_f.bstog){
        fm=amtfree();
        mu=tm-fm;
        db=(mu*g_f.bsmax)/tm;
    }

    /* Detail and Block pens seem to be reversed    */

    if ((r=t+g_f.bd_r)>CMAX) r=CMAX;
    if ((g=t+g_f.bd_g)>CMAX) g=CMAX;
    if ((b=t+g_f.bd_b)>CMAX) b=CMAX;
    Forbid();
    if (g_f.dstog) SetRGB4(sp->ViewPort, sp->BlockPen, r, g, b);
    if (g_f.bstog) SetRGB4(sp->ViewPort, sp->DetailPen, db, 0, g_f.bsmax-db);
    Permit();
}

/*
    Find the total amount of system memory
    by cruising the master list.
*/

ULONG
totmem()
{
    register ULONG tu, tl, tm=0;
    register struct Node *n;
    register struct MemHeader *m;

    Disable();
    for (n=SysBase->MemList.lh_Head; n->ln_Succ; n=n->ln_Succ){
        m=(struct MemHeader *)n;
        tu=(ULONG)m->mh_Upper;
        tl=(ULONG)m->mh_Lower;
        tm+=tu-tl;
    }
    Enable();
    return(tm);
}

/*
    Hunt up all ready tasks and waiting tasks;
    put their respective counts in r and w.
*/

tqlen(r, w)
int *r, *w;
{
    register tr=0, tw=0;
    register struct Task *t;

    Disable();
    t=SysBase->TaskReady.lh_Head;
    for (tr=0; t->tc_Node.ln_Succ; t=t->tc_Node.ln_Succ)
        tr++;
    t=SysBase->TaskWait.lh_Head;
    for (tw=0; t->tc_Node.ln_Succ; t=t->tc_Node.ln_Succ)
        tw++;
    Enable();
    *r=tr;
    *w=tw;
    return(tr+tw);
}

/*
    Place amt of free chip mem into cm;
    place amt of free fast mem into fm.
*/

ULONG
amtfree()
{
    ULONG c, f;

    Forbid();
    c=AvailMem(MEMF_CHIP);
    f=AvailMem(MEMF_FAST);
    Permit();
    return(c+f);
}

/*
    Just give me a window and don't make me
    put endless initializations into main()
    or global space, okay?
*/

#define MINWD   (120)
#define MINHT   (10)
#define MAXWD   MINWD
#define MAXHT   MINHT

struct Window *
getwin()
{
    struct NewWindow nw;
    struct Window *Window;

    nw.LeftEdge = 640/2-MAXWD/2;
    nw.TopEdge = 0;
    nw.Width = MAXWD;
    nw.Height = MAXHT;
    nw.DetailPen = 0;
    nw.BlockPen = 1;
    nw.Flags = WINDOWCLOSE|WINDOWDRAG|WINDOWDEPTH|BORDERLESS;
    nw.IDCMPFlags = CLOSEWINDOW|ACTIVEWINDOW|INACTIVEWINDOW;
    nw.FirstGadget = NULL;
    nw.CheckMark = NULL;
    nw.Title="Dirk";
    nw.Type=WBENCHSCREEN;
    nw.Screen = NULL;
    nw.BitMap = NULL;
    nw.MinWidth = MINWD;
    nw.MinHeight = MINHT;
    nw.MaxWidth = MAXWD;
    nw.MaxHeight = MAXHT;
    if (!(Window = OpenWindow(&nw))) {
        printf("can't open window");
        return((struct Window *)NULL);
    }
    else return(Window);
}

getargs(v, f)
char **v;
struct g_flags *f;
{
    register out=0;

    f->gran  = 4;      /*  good when 4-5 processes are in background   */
    f->bsmax = 14;     /*  good background--nor too bright nor dark    */
    f->dsmax = 12;     /*  takes some glare out of text                */
    f->dsmin = 8;      /*  readable even at near-red                   */
    f->dstog = 1;      /*  task watcher on by default                  */
    f->bstog = 1;      /*  memory watcher on by default                */
    f->intvl = 180;    /*  default delay is 3 seconds                  */
    f->bd_r  = 0;      /*  increment for detail pen red                */
    f->bd_g  = 0;      /*  increment for detail pen green              */
    f->bd_b  = 0;      /*  increment for detail pen blue               */

    while (*v){
        if (**v=='-') {
            if (!(*v)[1]) return(usage());
            while (*++*v){
                switch(**v){
                    case 'g': f->gran=atoi(++*v); ++out; break;
                    case 'b': f->bsmax=atoi(++*v); ++out; break;
                    case 'h': f->dsmax=atoi(++*v); ++out; break;
                    case 'l': f->dsmin=atoi(++*v); ++out; break;
                    case 'i': f->intvl=atoi(++*v); ++out; break;
                    case 't': f->bstog=0; break;
                    case 'm': f->dstog=0; break;
                    default: return(usage());
                }                               /* switch (**v) */
                if (out) {++v; break;}
            }                               /* while (*(++*v))  */
            if (!out) ++v;
            else out=0;
        }                               /* if (**v=='-')    */
        else if (**v=='+') {
            if (!(*v)[1]) return(usage());
            while (*++*v){
                switch(**v){
                    case 'r': f->bd_r=atoi(++*v); ++out; break;
                    case 'g': f->bd_g=atoi(++*v); ++out; break;
                    case 'b': f->bd_b=atoi(++*v); ++out; break;
                    default: return(usage());
                }                               /* switch (**v) */
                if (out) {++v; break;}
            }                               /* while (*++*v)  */
            if (!out) ++v;
            else out=0;
        }                               /* else if (**v=='+')   */
        else {
            return(usage());
        }
    }                               /* while (*v)   */
    limit(f);
    if (f->gran==0) f->gran=1;  /* special case for x/gran  */
    return(0);
}

#define LFIX(x, l)   if (x<l) x=l
#define HFIX(x, h)   if (x>h) x=h

/*
    Make sure all members of *f are in bounds.
*/

limit(f)
struct g_flags *f;
{
    int maxint=(1<<sizeof(maxint))-1;


    LFIX(f->gran, 0);
    HFIX(f->gran, maxint);
    LFIX(f->bsmax, 0);
    HFIX(f->bsmax, CMAX);
    LFIX(f->dsmax, 0);
    HFIX(f->dsmax, CMAX);
    LFIX(f->dsmin, 0);
    HFIX(f->dsmin, CMAX);
    LFIX(f->intvl, 0);
    HFIX(f->intvl, maxint);

    LFIX(f->bd_r, 0);
    HFIX(f->bd_r, CMAX);
    LFIX(f->bd_g, 0);
    HFIX(f->bd_g, CMAX);
    LFIX(f->bd_b, 0);
    HFIX(f->bd_b, CMAX);
}

usage()
{
    printf("dirk v0.01 copyright (c) 1988 Daniel Elbaum/Amaranth Software\n");
    printf("\nUsage: dirk [-t|m] [-gN] [-bN] [-hN] [-lN] [-iN]]\n");
    printf("t\ttrack tasks only\n");
    printf("m\ttrack memory only\n");
    printf("g   (4)\tgranularity of task mapping (small for few tasks)\n");
    printf("b  (14)\tbackground saturation\n");
    printf("h  (12)\tmaximum detail saturation\n");
    printf("l   (8)\tminimum detail saturation\n");
    printf("i (180)\tinterval in ticks (60 or 50 per second)\n");
    printf("\n\t[+rN] [+gN] [+bN]\n");
    printf("r   (0)\tamount by which to redden the pen\n");
    printf("g   (0)\tamount by which to greeen the pen\n");
    printf("b   (0)\tamount by which to bluen the pen\n");
    printf("\n");
    return(-1);
}


