/*
 *  MouseBlanker.c
 *
 *  Commodity
 *
 *  Author: Stefan Sticht
 *
 *  Copyright: source is public domain, no copyright
 *
 *  Version history:
 *
 *  V1.00   initial release
 *  V1.03   added window
 *  V1.04   recompiled with window.c V1.01
 *  V1.05   recompiled with window.c V1.02
 *  V1.06   recompiled with main.c V1.02
 *  V1.07   completly rewritten; shared commodity code thrown away; smaller, uses less CPU time
 *  V1.08   uses now Stricmp from utility.library
 *  V1.09   changed parameter parsing
 *  V1.10   changed parameter parsing; introduced an error with V1.09
 *  V1.11   a minor change
 *  V1.12   replaced menu by gadtools underscores & vanillakeys
 *          added blanking on key option
 *          disables inputevent-filters if not needed
 *  V1.13   added some LockIBase() where needed
 */

#define VERSION "V1.13"

/********************************************************************
 *                             interfacing                          *
 ********************************************************************/

/*
 *  include files
 */

#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <devices/inputevent.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuitionbase.h>
#include <libraries/commodities.h>

#include <clib/alib_protos.h>
#include <clib/commodities_protos.h>
#include <pragmas/commodities_pragmas.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/exec_protos.h>
#include <pragmas/exec_pragmas.h>
#include <clib/gadtools_protos.h>
#include <pragmas/gadtools_pragmas.h>
#include <clib/graphics_protos.h>
#include <pragmas/graphics_pragmas.h>
#include <clib/intuition_protos.h>
#include <pragmas/intuition_pragmas.h>
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>

#ifdef DEBUG
#define printf KPrintF
#include <clib/dlib_protos.h>
#endif

/*
 *  prototypes
 */
long openwindow(void);
struct Gadget *
createallgadgets(struct Gadget **glist, APTR vi, struct Screen *scr, ULONG *height, ULONG *width);
struct Library *myopenlibrary(char *name, unsigned long version);
void __saveds mousemon(void);
void __saveds timermon(void);
void __saveds keymon(CxMsg *cxm);
void changestateofmousepointer(unsigned char blanked);
void closewindow(void);
void getseconds(void);
void processmessages(short window);

/*
 *  global data defined in other moduls
 *
 *  libraries opened by startup code; basepointers needed by function pragmas
 */
extern struct Library *DOSBase;
extern struct Library *SysBase;

/*
 *  access to custom chips
 */
extern struct Custom __far custom;

/*
 *  Disable SAS/C CTRL/C handling
 */
void chkabort(void) {}

/********************************************************************
 *                             global data                          *
 ********************************************************************/

/*
 *  definition of all messages (multi language support not completed yet)
 */
#ifdef GERMAN

#define RETRY_GADGETS           "Wiederholen|Abbrechen"
#define RESUME_GADGETS          "Weiter"
#define MSG_LIBRARY_OPENERR     "Die %s (V%ld+) kann nicht geöffnet werden!"
#define COM_NAME                "Mausweg"
#define COM_DESCR               "Versteckt Mauszeiger"
#define MSG_PROJECT             "Projekt"
#define MSG_SECONDS             "_Sekunden:"
#define MSG_SECONDS_SCC         'S'
#define MSG_KEY                 "auf _Tastendruck löschen"
#define MSG_KEY_SCC             'T'
#define MSG_HIDE                "_Verstecken"
#define MSG_HIDE_SCC            'V'
#define MSG_QUIT                "_Beenden"
#define MSG_QUIT_SCC            'B'
#define YES                     "JA"
#define NO                      "NEIN"
#define TT_TIMEOUT              "SEKUNDEN"
#define TT_KEY                  "TASTENDRUCK"

#else

#define RETRY_GADGETS           "Retry|Cancel"
#define RESUME_GADGETS          "Resume"
#define MSG_LIBRARY_OPENERR     "%s (V%ld+) can't be opened!"
#define COM_NAME                "MouseBlanker"
#define COM_DESCR               "Blanks mouse pointer"
#define MSG_PROJECT             "Project"
#define MSG_SECONDS             "_Seconds:"
#define MSG_SECONDS_SCC         'S'
#define MSG_KEY                 "blank on pressing any _key"
#define MSG_KEY_SCC             'K'
#define MSG_HIDE                "_Hide"
#define MSG_HIDE_SCC            'H'
#define MSG_QUIT                "_Quit"
#define MSG_QUIT_SCC            'Q'
#define YES                     "YES"
#define NO                      "NO"
#define TT_TIMEOUT              "SECONDS"
#define TT_KEY                  "KEYPRESS"

#endif

#define COM_TITLE           COM_NAME " " VERSION
#define CX_PRIORITY         "CX_PRIORITY"
#define DEF_CX_PRIORITY     0
#define CX_POPKEY           "CX_POPKEY"
#define DEF_CX_POPKEY       ""
#define CX_POPUP            "CX_POPUP"
#define DEF_CX_POPUP        NO
#define DEF_TIMEOUT         3
#define DEF_KEY             YES

#define POP_KEY_ID      100

#define CCMD_NOP                0
#define CCMD_HIDE               1
#define CCMD_QUIT               2
#define CCMD_SECONDS            3
#define CCMD_KEY                4
#define CCMD_SECONDS_ACTIVATE   5
#define CCMD_KEY_SHORTCUT       6

#define CASE_DIFF   ('A' - 'a')

/*
 *  data for cback.o
 */
long _stack = 2048l;
char *_procname = COM_NAME;
long _priority = 0l;
long _BackGroundIO = 1;
extern BPTR _Backstdout;

/*
 *  library base pointers
 */
struct IntuitionBase *IntuitionBase;
struct Library *CxBase;
struct Library *IconBase;
struct Library *GadToolsBase;
struct Library *GfxBase;
struct Library *UtilityBase;

/*
 *  our task
 */
struct Task *mytask = NULL;

/*
 *  signal we AllocSignal() in main()
 */
long signal = -1l;

/*
 *  message ports
 */
struct MsgPort *idcmpport   = NULL;
struct MsgPort *cxport      = NULL;

/*
 *  signal flags
 */
unsigned long customsigflag = 0l;
unsigned long cxsigflag = 0l;
unsigned long idcmpsigflag = 0l;

/*
 *  programtitle and version for Version command
 */
char versionstring[] ="\0$VER: " COM_NAME " " VERSION;

/*
 *  helpstring
 */
#ifdef GERMAN
char helpstring[] = "\033[1m" COM_NAME "\033[0m " VERSION " (Public Domain) von Stefan Sticht\n"\
                    "Aufruf: " COM_NAME " [" CX_PRIORITY "=<n>] [" CX_POPKEY "=<hotkey>] ["
                    CX_POPUP "=" YES "|" NO "] [" TT_TIMEOUT "=<Sekunden>] ["\
                    TT_KEY "=" YES "|" NO "]\n";
#else
char helpstring[] = "\033[1m" COM_NAME "\033[0m " VERSION " (Public Domain) by Stefan Sticht\n"
                    "Usage: " COM_NAME " [" CX_PRIORITY "=<n>] [" CX_POPKEY "=<hotkey>] ["
                    CX_POPUP "=" YES "|" NO "] [" TT_TIMEOUT "=<seconds>] ["\
                    TT_KEY "=" YES "|" NO "]\n";
#endif

/*
 *  timeout values
 */
unsigned short timeout;
unsigned short timeoutseconds;
unsigned short timeoutperiod;
unsigned short keypress;

#define UNBLANKED   0
#define BLANKED     1
unsigned char blankstatus = UNBLANKED;

/*
 *  the tooltypearray
 */
char **tooltypes;

/*
 *  our broker
 */
CxObj *broker = NULL;
CxObj *hotkeyfilter;
CxObj *mousefilter;
CxObj *timerfilter;
CxObj *keyfilter;

struct NewBroker newbroker = {
    NB_VERSION,                         /* BYTE nb_Version               */
    COM_NAME,                           /* BYTE *nb_Name                 */
    COM_TITLE,                          /* BYTE *nb_Title                */
    COM_DESCR,                          /* BYTE *nb_Descr                */
    NBU_NOTIFY | NBU_UNIQUE,            /* SHORT nb_Unique               */
    COF_SHOW_HIDE,                      /* SHORT nb_Flags                */
    0,                                  /* BYTE nb_Pri                   */
    NULL,                               /* struct MsgPort nb_Port        */
    0                                   /* WORD nb_ReservedChannel       */
};

IX timerix = {
    IX_VERSION,                         /* UBYTE ix_version     */
    IECLASS_TIMER,                      /* UBYTE ix_Class       */
    0,                                  /* UWORD ix_Code        */
    0,                                  /* UWORD ix_CodeMask    */
    0,                                  /* UWORD ix_Qualifier   */
    0,                                  /* UWORD ix_Qualmask    */
    0                                   /* UWORD ix_QualSame    */
    };

IX mouseix = {
    IX_VERSION,                         /* UBYTE ix_version     */
    IECLASS_RAWMOUSE,                   /* UBYTE ix_Class       */
    0,                                  /* UWORD ix_Code        */
    0,                                  /* UWORD ix_CodeMask    */
    IEQUALIFIER_RELATIVEMOUSE,          /* UWORD ix_Qualifier   */
    IEQUALIFIER_RELATIVEMOUSE,          /* UWORD ix_Qualmask    */
    0                                   /* UWORD ix_QualSame    */
    };

IX keyix = {
    IX_VERSION,                         /* UBYTE ix_version     */
    IECLASS_RAWKEY,                     /* UBYTE ix_Class       */
    0,                                  /* UWORD ix_Code        */
    0,                                  /* UWORD ix_CodeMask    */
    0,                                  /* UWORD ix_Qualifier   */
    0,                                  /* UWORD ix_Qualmask    */
    0                                   /* UWORD ix_QualSame    */
    };

/*
 *  data for our window
 */
struct Window *win          = NULL;
APTR vi                     = NULL;
struct Gadget *gadgetlist   = NULL;
struct Screen *scr          = NULL;
#define WINDOWTITLE_LEN 63
char windowtitle[WINDOWTITLE_LEN + 1] = COM_TITLE;

/*
 *  space for zoomed window pos & size
 */
WORD zoompos[4];

#define P_WA_Left       0
#define P_WA_Top        1
#define P_WA_Width      2
#define P_WA_Height     3
#define P_WA_Title      6
#define P_WA_PubScreen  7
#define P_WA_Gadgets    8
struct TagItem mywindowtag[] = {
    WA_Left,        0l,
    WA_Top,         20l,
    WA_Width,       400l,
    WA_Height,      150l,
    WA_IDCMP,       IDCMP_CLOSEWINDOW | BUTTONIDCMP | IDCMP_REFRESHWINDOW | IDCMP_VANILLAKEY,
    WA_Flags,       WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SMART_REFRESH |
                    WFLG_ACTIVATE | WFLG_RMBTRAP,
    WA_Title,       (ULONG)windowtitle,
    WA_PubScreen,   0l,
    WA_Gadgets,     0l,
    WA_Zoom,        (ULONG)zoompos,
    TAG_END
    };

char msg_seconds[] = MSG_SECONDS;
char msg_key[]     = MSG_KEY;
char msg_hide[]    = MSG_HIDE;
char msg_quit[]    = MSG_QUIT;

/*
 *  gadget definitions
 */
#define DBL_INTERWIDTH  (INTERWIDTH << 1)
#define DBL_INTERHEIGHT (INTERHEIGHT << 1)

#define GAD_SECONDS 6
#define GAD_KEY     7
#define GAD_HIDE    8
#define GAD_QUIT    9

struct Gadget *gad_seconds;
struct Gadget *gad_key;
struct Gadget *gad_hide;
struct Gadget *gad_quit;

/********************************************************************
 *                             functions                            *
 ********************************************************************/

/*
 *  request(): a glue routine to EasyRequest as simple as printf plus
 *             titlestring, gadgettexts
 *
 *  Input: char *title:         pointer to the title of the requester
 *         char *gadgets:       pointer to gadgettext
 *         char *text:          text displayed in requester
 *
 *  Result: same as EasyrequestArgs()
 *
 * !!! for more info see EasyRequestArgs() in Autodocs/intuition.doc !!!
 */
long request(char *title, char *gadgets, char *text, ...)
{
    /*
     *  structure textreq only needed in this function, so hide it here
     *  must be static, in order to be initialized only once
     */
    static struct EasyStruct textreq = {
        sizeof (struct EasyStruct), /* ULONG es_StructSize      */
        0l,                         /* ULONG es_Flags           */
        NULL,                       /* UBYTE *es_Title          */
        NULL,                       /* UBYTE *es_TextFormat     */
        NULL,                       /* UBYTE *es_GadgetFormat   */
        };
    va_list ap;
    long rc;

    /*
     *  get start of variable arguments
     */
    va_start(ap, text);

    /*
     *  update textreq
     */
    textreq.es_Title = (UBYTE *)title;
    textreq.es_TextFormat = (UBYTE *)text;
    textreq.es_GadgetFormat = (UBYTE *)gadgets;

    /*
     *  win may be NULL
     */
    rc = EasyRequestArgs(NULL, &textreq, NULL, ap);

    va_end(ap);

    return(rc);
}

/*
 *  myopenlibrary(): same as OpenLibrary(), but opens a retry-requester
 *                   if OpenLibrary() fails, to give the user a chance to
 *                   copy the library to libs: and retry
 *                   requires request(), see above
 */
struct Library *myopenlibrary(char *name, unsigned long version)
{
    static char errortext[] = MSG_LIBRARY_OPENERR;
    struct Library *libptr;
    long ok = TRUE;

    do {
        if (!(libptr = OpenLibrary((UBYTE *)name, version))) {
            if (IntuitionBase) {
                ok = request(COM_NAME ":", RETRY_GADGETS, errortext, name, version);
                }
            else ok = FALSE;
            }
        } while (!libptr && ok);

    #ifdef DEBUG
    printf("myopenlibrary(%s, %ld) = 0x%lx\n", name, version, libptr);
    #endif
    return(libptr);
}

void main(int argc, char *argv[])
{
    CxObj *customobj;
    char *hotkey;
    struct Message *msg;
    long seconds;
    short window = FALSE;

    if ((argc > 1) && (*argv[1] == '?')) {
        /*
         *  display help string
         */
        if (_Backstdout) {
            Write(_Backstdout, helpstring, sizeof(helpstring) - 1l);
            Close(_Backstdout);
            }
        return;
        }
    else if (argc && _Backstdout) Close(_Backstdout);

    if (IntuitionBase = (struct IntuitionBase *)myopenlibrary("intuition.library", 37l)) {

        /*
         *  parse command line or tool types
         *
         *  we need icon.library therefore
         */
        if (IconBase = myopenlibrary("icon.library", 37l)) {

            /*
             * create tooltypes array (requires icon.library open!!!)
             */
            tooltypes = (char **)ArgArrayInit(argc, argv);

            newbroker.nb_Pri = ArgInt(tooltypes, CX_PRIORITY, DEF_CX_PRIORITY);

            /*
             *  check for a hotkey
             */
            hotkey = ArgString(tooltypes, CX_POPKEY, DEF_CX_POPKEY);

            /*
             *  get the user's prefered timeout
             */
            seconds = ArgInt(tooltypes, TT_TIMEOUT, DEF_TIMEOUT);
            if (seconds < 1) timeoutseconds = 0;
            else timeoutseconds = seconds;
            timeoutperiod = timeoutseconds * 10;

            if (UtilityBase = myopenlibrary("utility.library", 37l)) {

                if (Stricmp(ArgString(tooltypes, CX_POPUP, DEF_CX_POPUP), YES) == 0l)
                    window = TRUE;

                /*
                 *  blank on key press?
                 */
                keypress = (Stricmp(ArgString(tooltypes, TT_KEY, DEF_KEY), YES) == 0l) ?
                           TRUE : FALSE;

                CloseLibrary(UtilityBase);

                } /* if UtilityBase */

            if (CxBase = myopenlibrary("commodities.library", 37l)) {

                if (GfxBase = myopenlibrary("graphics.library", 37l)) {

                    if (GadToolsBase = myopenlibrary("gadtools.library", 37l)) {

                        /*
                         *  create our message port
                         */
                        if (cxport = CreateMsgPort()) {

                            cxsigflag = 1l << cxport->mp_SigBit;
                            newbroker.nb_Port = cxport;

                            if (broker = CxBroker(&newbroker, NULL)) {

                                if (timerfilter = CxFilter(NULL)) {

                                    AttachCxObj(broker, timerfilter);

                                    if (mousefilter = CxFilter(NULL)) {

                                        AttachCxObj(broker, mousefilter);

                                        if (keyfilter = CxFilter(NULL)) {

                                            AttachCxObj(broker, keyfilter);

                                            SetFilterIX(timerfilter, &timerix);
                                            SetFilterIX(mousefilter, &mouseix);
                                            SetFilterIX(keyfilter, &keyix);

                                            if ((signal = (long)AllocSignal(-1)) != -1) {

                                                customsigflag = 1 << signal;

                                                if (mytask = FindTask(NULL)) {

                                                    if (customobj = CxCustom(timermon, 0l)) {

                                                        AttachCxObj(timerfilter, customobj);

                                                        if (customobj = CxCustom(mousemon, 0l)) {

                                                            AttachCxObj(mousefilter, customobj);

                                                            if (customobj = CxCustom(keymon, 0l)) {

                                                                AttachCxObj(keyfilter, customobj);

                                                                if (*hotkey) {
                                                                    unsigned long len;

                                                                    if (hotkeyfilter = HotKey(hotkey, cxport, POP_KEY_ID)) {
                                                                        AttachCxObj(broker, hotkeyfilter);
                                                                        len = strlen(windowtitle);
                                                                        strncpy(windowtitle + len, ": HotKey=", WINDOWTITLE_LEN - len);
                                                                        len += sizeof(": HotKey=") - 1;
                                                                        strncpy(windowtitle + len, hotkey, WINDOWTITLE_LEN - len);
                                                                        }

                                                                    } /* if hotkey */

                                                                if (!CxObjError(timerfilter) &&
                                                                    !CxObjError(mousefilter) &&
                                                                    !CxObjError(keyfilter) &&
                                                                    !(hotkeyfilter && CxObjError(hotkeyfilter))) {

                                                                    /*
                                                                     *  deactivate timerfilter if timeout = 0
                                                                     */
                                                                    if (!timeoutseconds) ActivateCxObj(timerfilter, 0l);
                                                                    /*
                                                                     *  deactivate keyfilter if !keypress
                                                                     */
                                                                    if (!keypress) ActivateCxObj(keyfilter, 0l);
                                                                    /*
                                                                     *  activate our commodity
                                                                     */
                                                                    ActivateCxObj(broker, 1l);
                                                                    /*
                                                                     * deactivate again, if no timeout and not keypress
                                                                     */
                                                                    if (!timeoutseconds && !keypress) ActivateCxObj(broker, 0l);
                                                                    /*
                                                                     *  now watch our numerous ports
                                                                     */
                                                                    processmessages(window);

                                                                    } /* if !CxObjError() */

                                                                } /* if keymon */

                                                            } /* if mousemon */

                                                        } /* if timermon */

                                                    } /* if mytask */

                                                FreeSignal(signal);
                                                customsigflag = 0l;

                                                } /* if signal */

                                            } /* if keyfilter */

                                        #ifdef DEBUG
                                        else printf("main(): keyfilter = CxFilter() failed!\n");
                                        #endif

                                        } /* if mousefilter */

                                    #ifdef DEBUG
                                    else printf("main(): mousefilter = CxFilter() failed!\n");
                                    #endif

                                    } /* if timerfilter */

                                #ifdef DEBUG
                                else printf("main(): timerfilter = CxFilter() failed!\n");
                                #endif

                                DeleteCxObjAll(broker);

                                } /* if broker */

                            #ifdef DEBUG
                            else printf("main(): CxBroker() failed!\n");
                            #endif

                            /*
                             *  delete our message port after replying all pending messages
                             */
                            while (msg = GetMsg(cxport)) ReplyMsg(msg);
                            DeleteMsgPort(cxport);
                            } /* if cxport */

                        #ifdef DEBUG
                        else printf("main(): CraeteMsgPort() failed!\n");
                        #endif

                        CloseLibrary(GadToolsBase);
                        } /* if GadToolsBase */

                    CloseLibrary(GfxBase);
                    } /* if GfxBase */

                CloseLibrary(CxBase);
                } /* if CxBase */

            /*
             *  free memory allocated by ArgArrayInit()
             */
            ArgArrayDone();

            CloseLibrary(IconBase);
            } /* if IconBase */

        CloseLibrary((struct Library *)IntuitionBase);
        } /* if IntuitionBase */

} /* main() */

void processmessages(short window)
{
    struct Message *msg;
    unsigned long sigreceived;
    unsigned long msgtype;
    unsigned long msgid;
    unsigned short quit = FALSE;
    unsigned short windowopen = FALSE;
    unsigned char command = CCMD_NOP;

    if (window) {
        if (openwindow()) windowopen = TRUE;
        }

    while (!quit) {

        sigreceived = Wait(SIGBREAKF_CTRL_C | cxsigflag | customsigflag | idcmpsigflag);

        #ifdef DEBUG
        printf("processmessages(): signal received\n");
        #endif

        if (sigreceived & customsigflag) changestateofmousepointer(blankstatus);
        if (sigreceived & SIGBREAKF_CTRL_C) quit = TRUE;

        if (sigreceived & cxsigflag) {

            #ifdef DEBUG
            printf("processmessages(): message at cxport\n");
            #endif

            while (msg = (struct Message *)GetMsg(cxport)) {

                msgid = CxMsgID((CxMsg *)msg);
                msgtype = CxMsgType((CxMsg *)msg);

                ReplyMsg(msg);

                switch (msgtype) {

                    case CXM_IEVENT:
                        if (msgid == POP_KEY_ID) {
                            if (openwindow()) windowopen = TRUE;
                            }
                        break;

                    case CXM_COMMAND:
                        switch (msgid) {

                            case CXCMD_KILL:
                                quit = TRUE;

                            case CXCMD_DISABLE:
                                ActivateCxObj(broker, 0l);
                                blankstatus = UNBLANKED;
                                changestateofmousepointer(blankstatus);
                                #ifdef DEBUG
                                printf("processmessages(): broker disabled\n");
                                #endif
                                break;

                            case CXCMD_ENABLE:
                                ActivateCxObj(broker, 1l);
                                #ifdef DEBUG
                                printf("processmessages(): broker enabled\n");
                                #endif
                                break;

                            case CXCMD_UNIQUE:
                            case CXCMD_APPEAR:
                                if (openwindow()) windowopen = TRUE;
                                break;

                            case CXCMD_DISAPPEAR:
                                windowopen = FALSE;
                                closewindow();
                                break;

                            }
                        break;

                    } /* switch msgtype */

                } /* while CxMsg */

            } /* if (sigreceived & cxsigflag) */

        if (sigreceived & idcmpsigflag) {

            APTR *object;
            ULONG class;
            UWORD code;

            #ifdef DEBUG
            printf("processmessages(): message at idcmpport\n");
            #endif

            while (windowopen && (msg = (struct Message *)GT_GetIMsg(idcmpport))) {
                /*
                 *  handle IDCMP messages
                 */
                class    = ((struct IntuiMessage *)msg)->Class;
                code     = ((struct IntuiMessage *)msg)->Code;
                object   = ((struct IntuiMessage *)msg)->IAddress;
                GT_ReplyIMsg((struct IntuiMessage *)msg);

                switch (class) {

                    case CLOSEWINDOW:
                        command = CCMD_HIDE;
                        break;

                    case REFRESHWINDOW:
                        GT_BeginRefresh(win);
                        GT_EndRefresh(win, TRUE);
                        break;

                    case GADGETUP:
                        if (object) command = (unsigned char)((struct Gadget *)object)->UserData;
                        break;

                    case VANILLAKEY:
                        switch (code) {

                            case 27:
                            case MSG_HIDE_SCC:
                            case MSG_HIDE_SCC - CASE_DIFF:
                                        command = CCMD_HIDE;
                                        break;

                            case MSG_QUIT_SCC:
                            case MSG_QUIT_SCC - CASE_DIFF:
                                        command = CCMD_QUIT;
                                        break;

                            case MSG_SECONDS_SCC:
                            case MSG_SECONDS_SCC - CASE_DIFF:
                                        command = CCMD_SECONDS_ACTIVATE;
                                        break;

                            case MSG_KEY_SCC:
                            case MSG_KEY_SCC - CASE_DIFF:
                                        command = CCMD_KEY_SHORTCUT;
                                        break;

                            }
                        break;

                    } /* switch class */

                } /* while idcmp message */

            } /* if (sigreceived & idcmpsigflag) */

        #ifdef DEBUG
        printf("processmessages(): command = %ld\n", command);
        #endif

        switch(command) {

            case CCMD_NOP:
                break;

            case CCMD_HIDE:
                getseconds();
                closewindow();
                break;

            case CCMD_QUIT:
                quit = TRUE;
                break;

            case CCMD_SECONDS:
                getseconds();
                break;

            case CCMD_KEY_SHORTCUT:
                GT_SetGadgetAttrs(gad_key, win, NULL, GTCB_Checked, !keypress, TAG_END);

            case CCMD_KEY:
                keypress = !keypress;
                ActivateCxObj(keyfilter, keypress);
                #ifdef DEBUG
                printf("processmessages(): ActivateCxObj(keyfilter, %ld)\n", keypress);
                #endif
                if (!keypress && !timeoutseconds) {
                    /*
                     *  unblank mouse pointer
                     */
                    blankstatus = UNBLANKED;
                    changestateofmousepointer(blankstatus);
                    ActivateCxObj(broker, 0l);
                    #ifdef DEBUG
                    printf("processmessages(): broker disabled\n");
                    #endif
                    }
                else {
                    ActivateCxObj(broker, 1l);
                    #ifdef DEBUG
                    printf("processmessages(): broker enabled\n");
                    #endif
                    }
                break;

            case CCMD_SECONDS_ACTIVATE:
                ActivateGadget(gad_seconds, win, NULL);
                break;

            } /* switch (command) */

        command = CCMD_NOP;

        } /* while !quit */

    ActivateCxObj(broker, 0l);
    #ifdef DEBUG
    printf("processmessages(): broker disabled\n");
    #endif
    changestateofmousepointer(UNBLANKED);
    closewindow();
}

/*
 *  commodity functions
 */
void __saveds timermon(void)
{
    if (++timeout >= timeoutperiod) {
        blankstatus = BLANKED;
        if (mytask && customsigflag) Signal(mytask, customsigflag);
        #ifdef DEBUG
        printf(COM_NAME ": timermon blanked\n");
        #endif
        }
}

void __saveds mousemon(void)
{
    #ifdef DEBUG
    printf(COM_NAME ": mousemon\n");
    #endif

    timeout = 0;
    if (blankstatus == BLANKED) {
        blankstatus = UNBLANKED;
        if (mytask && customsigflag) Signal(mytask, customsigflag);
        #ifdef DEBUG
        printf(COM_NAME ": mousemon unblanked\n");
        #endif
        }
}

void __saveds keymon(CxMsg *cxm)
{
    register struct InputEvent *ie = (struct InputEvent *)CxMsgData(cxm);

    #ifdef DEBUG
    printf(COM_NAME ": keymon\n");
    #endif

    if (ie->ie_Code < 0x60) {

        if (blankstatus == UNBLANKED) {
            blankstatus = BLANKED;
            if (mytask && customsigflag) Signal(mytask, customsigflag);
            #ifdef DEBUG
            printf(COM_NAME ": keymon blanked\n");
            #endif
            }

        }

}

void changestateofmousepointer(unsigned char blankstatus)
{
    if (blankstatus == BLANKED) {
        /*
         *  clear mouse pointer
         */
        WaitTOF();            
        OFF_SPRITE;
        custom.spr[0].dataa=0;
        custom.spr[0].datab=0;
        timeout = 0;

        if (keypress) {
            /*
             *  no mousepointer =>
             *  disable keyfilter, no rawkey event needs to be monitored
             *  if no timeout specified, enable mousefilter
             */
            if (!timeoutseconds) {
                ActivateCxObj(mousefilter, 1l);
                #ifdef DEBUG
                printf("changestateofmousepointer(): mousefilter enabled\n");
                #endif
                }
            ActivateCxObj(keyfilter, 0l);
            #ifdef DEBUG
            printf("changestateofmousepointer(): keyfilter disabled\n");
            #endif
            }

        } /* if blanked */

    else {
        ON_SPRITE;
        timeout = 0;

        if (keypress) {
            /*
             *  mouse pointer on =>
             *  enable keyfilter and disbale mousefilter if no timeout specified
             */
            if (!timeoutseconds) {
                ActivateCxObj(mousefilter, 0l);
                #ifdef DEBUG
                printf("changestateofmousepointer(): mousefilter disabled\n");
                #endif
                }
            ActivateCxObj(keyfilter, 1l);
            #ifdef DEBUG
            printf("changestateofmousepointer(): keyfilter enabled\n");
            #endif
            }

        }
}

/*
 *  window functions
 */
long openwindow(void)
{
    struct DimensionInfo di;
    struct Rectangle *rect;
    unsigned long lock;
    unsigned long vpmid;
    long ok;

    if (win) {
        /*
         *  dont't open window but pop it to front, unzip it
         *
         *  unzip window if zipped
         */
        if (win->Height < mywindowtag[P_WA_Height].ti_Data) ZipWindow(win);

        /*
         *  window to front
         */
        WindowToFront(win);

        /*
         *  activate window
         */
        lock = LockIBase(0l);
        if (win != IntuitionBase->ActiveWindow) {
            UnlockIBase(lock);
            ActivateWindow(win);
            }
        else UnlockIBase(lock);

        /*
         *  screen to front
         */
        lock = LockIBase(0l);
        if (win->WScreen != IntuitionBase->FirstScreen) {
            UnlockIBase(lock);
            ScreenToFront(win->WScreen);
            }
        else UnlockIBase(lock);

        ok = TRUE;

        }

    else {
        /*
         *  lock the default public screen
         */
        if (scr = LockPubScreen(NULL)) {
            /*
             *  GetVisualInfo() for gadtools
             */
            if (vi = GetVisualInfo(scr, TAG_DONE)) {
                /*
                 *  update some window information
                 */
                mywindowtag[P_WA_Width].ti_Data = zoompos[2] =
                    TextLength(&scr->RastPort, (STRPTR)windowtitle, strlen(windowtitle)) + 100;
                mywindowtag[P_WA_Height].ti_Data = zoompos[3] =
                    scr->WBorTop + scr->RastPort.TxHeight + 1;
                /*
                 *  create all gadgets:
                 *
                 *  this function may also change the width and height of our window
                 */
                if (createallgadgets(&gadgetlist, vi, scr,
                                     &mywindowtag[P_WA_Height].ti_Data,
                                     &mywindowtag[P_WA_Width].ti_Data)) {
                    /*
                     *  center the window in the visible box of the screen
                     */
                    if ((vpmid = GetVPModeID(&scr->ViewPort )) != INVALID_ID) {

                        GetDisplayInfoData(0, (UBYTE *)&di, sizeof(struct DimensionInfo), DTAG_DIMS, vpmid);
                        rect = &di.TxtOScan;
                        zoompos[0] = mywindowtag[P_WA_Left].ti_Data =
                            (scr->LeftEdge < 0 ? -scr->LeftEdge : 0) +
                            ((min((rect->MaxX - rect->MinX + 1), scr->Width) -
                              mywindowtag[P_WA_Width].ti_Data) >> 1);
                        zoompos[1] = mywindowtag[P_WA_Top].ti_Data =
                            (scr->TopEdge < 0 ? -scr->TopEdge : 0) +
                            ((min((rect->MaxY - rect->MinY + 1), scr->Height) -
                              mywindowtag[P_WA_Height].ti_Data) >> 1);

                        }

                    mywindowtag[P_WA_PubScreen].ti_Data = (ULONG)scr;

                    mywindowtag[P_WA_Gadgets].ti_Data = (ULONG)gadgetlist;
                    if (win = OpenWindowTagList(NULL, mywindowtag)) {
                        /*
                         *  save IDCMP port
                         */
                        idcmpport = win->UserPort;
                        idcmpsigflag = 1L << idcmpport->mp_SigBit;
                
                        GT_RefreshWindow(win, NULL);
                
                        /*
                         *  switch screen to front
                         */
                        lock = LockIBase(0l);
                        if (win->WScreen != IntuitionBase->FirstScreen) {
                            UnlockIBase(lock);
                            ScreenToFront(win->WScreen);
                            }
                        else UnlockIBase(lock);

                        ok = TRUE;

                        } /* if (win = OpenWindowTagList(NULL, mywindowtag)) */

                    } /* if (createallgadgets() */

                } /*  if (vi = GetVisualInfo(scr, TAG_DONE)) */

            } /* if (scr = LockPubScreen(NULL)) */

        if (!ok) {
                /*
                 *  window could not be opened - free all resources
                 */
                closewindow();
                }

        } /* if (!win) */
        
    return(ok);
}

void closewindow(void)
{

    if (win) {
        CloseWindow(win);
        win = NULL;
        idcmpport = NULL;
        idcmpsigflag = 0l;
        }

    if (gadgetlist) {
        FreeGadgets(gadgetlist);
        gadgetlist = NULL;
        }

    if (vi) {
        FreeVisualInfo(vi);
        vi = NULL;
        }

    if (scr) {
        UnlockPubScreen(NULL, scr);
        scr = NULL;
        }
}

void getseconds(void)
{
    long seconds;

    if (blankstatus == BLANKED && !keypress) {
        /*
         *  unblank mouse pointer
         */
        blankstatus = UNBLANKED;
        changestateofmousepointer(blankstatus);
        }

    if ((seconds = ((struct StringInfo *)gad_seconds->SpecialInfo)->LongInt) > 0) {
        /*
         *  input value ok
         */
        timeoutseconds = seconds;
        timeoutperiod  = timeoutseconds * 10;
        timeout = 0;

        ActivateCxObj(timerfilter, 1l);
        #ifdef DEBUG
        printf("getseconds(): timerfilter enabled\n");
        #endif

        ActivateCxObj(broker, 1l);
        #ifdef DEBUG
        printf("getseconds(): broker enabled\n");
        #endif
        }

    else {
        /*
         *  value given was < 1
         */
        timeoutseconds = timeout = 0;

        /*
         *  disable timerfilter
         */
        ActivateCxObj(timerfilter, 0l);

        /*
         *  update gadget contents
         */
        GT_SetGadgetAttrs(gad_seconds, win, NULL, GTIN_Number, timeoutseconds, TAG_END);

        }

    /*
     *  disable broker if all blanking actions are disabled
     */
    if (!keypress && !timeoutseconds) {
        ActivateCxObj(broker, 0l);
        #ifdef DEBUG
        printf("getseconds(): broker disabled\n");
        #endif
        }
}

/*
 *  createallgadgets(): create all gadgets and put required width and height
 *                      of window in height and width
 */
struct Gadget *
createallgadgets(struct Gadget **glist, APTR vi, struct Screen *scr, ULONG *height, ULONG *width)
{
    static struct NewGadget ng;
    struct Gadget *gad;
    struct RastPort *rp;
    short buttonwidth;
    short keywidth;
    short secondswidth;
    short dummy;

    /*
     *  first get RastPort of screen to determine some TextLength()
     */
    rp = &scr->RastPort;

    /*
     *  calculate width of the seconds gadget:
     *      I tried out that 8 times the width of the digit "0" gives a nice value
     *      for the integer gadget; add INTERWIDTH and length of gadgettitle
     */    
    secondswidth = ((TextLength(rp, (STRPTR)"0", 1l)) << 3) + INTERWIDTH +
                   TextLength(rp, (STRPTR)&msg_seconds[1], strlen(msg_seconds) - 1l);

    *width = max(secondswidth + DBL_INTERWIDTH, *width);

    /*
     *
     */
    keywidth = TextLength(rp, (STRPTR)msg_key, strlen(msg_key)) -
               TextLength(rp, (STRPTR)"_", 1l) + 26 + INTERWIDTH;
    *width = max(keywidth + DBL_INTERWIDTH, *width);

    buttonwidth = TextLength(rp, (STRPTR)&msg_hide[1], strlen(msg_hide) - 1l);
    dummy       = TextLength(rp, (STRPTR)&msg_quit[1], strlen(msg_quit) - 1l);
    buttonwidth = max(buttonwidth, dummy);
    buttonwidth += DBL_INTERWIDTH;
    /*
     *  check out if the window's width has to be enlarged
     */
    *width = max((buttonwidth << 1) + (DBL_INTERWIDTH + INTERWIDTH), *width);

    /*
     *  now create the gadgets
     */
    gad = CreateContext(glist);

    /*
     *  seconds gadget
     */
    ng.ng_TopEdge = scr->WBorTop + rp->TxHeight + 1 + DBL_INTERHEIGHT;
    ng.ng_Width   = (TextLength(rp, (STRPTR)"0", 1l)) << 3;
    ng.ng_LeftEdge = TextLength(rp, (STRPTR)&msg_seconds[1], strlen(msg_seconds) - 1l);
    ng.ng_LeftEdge += (*width - secondswidth) >> 1;

    ng.ng_Height        = rp->TxHeight + 4;
    ng.ng_GadgetText    = (UBYTE *)msg_seconds;
    ng.ng_TextAttr      = scr->Font;
    ng.ng_GadgetID      = GAD_SECONDS;
    ng.ng_Flags         = PLACETEXT_LEFT;
    ng.ng_VisualInfo    = vi;
    ng.ng_UserData      = (APTR)CCMD_SECONDS;

    gad_seconds = gad = CreateGadget(INTEGER_KIND, gad, &ng,
                                    GTIN_MaxChars, 3l,
                                    GTIN_Number, (ULONG)timeoutseconds,
                                    GT_Underscore, '_',
                                    TAG_DONE);

    /*
     *  key pressed gadget
     */
    ng.ng_TopEdge  += ng.ng_Height + INTERHEIGHT;
    ng.ng_LeftEdge  = (*width - keywidth) >> 1;
    ng.ng_GadgetText    = (UBYTE *)msg_key;
    ng.ng_GadgetID      = GAD_KEY;
    ng.ng_Flags         = PLACETEXT_RIGHT;
    ng.ng_UserData      = (APTR)CCMD_KEY;

    gad_key = gad = CreateGadget(CHECKBOX_KIND, gad, &ng, GTCB_Checked, keypress,
                                                          GT_Underscore, '_',
                                                          TAG_DONE);

    /*
     *  dummy now gets the spacing between the button gadgets
     */
    dummy = (*width - (buttonwidth << 1)) / 3;

    /*
     *  Hide gadget
     */    
    ng.ng_TopEdge      += ng.ng_Height + INTERHEIGHT;
    ng.ng_Width         = buttonwidth;
    ng.ng_LeftEdge      = dummy;
    ng.ng_GadgetText    = (UBYTE *)msg_hide;
    ng.ng_GadgetID      = GAD_HIDE;
    ng.ng_Flags         = 0l;
    ng.ng_UserData      = (APTR)CCMD_HIDE;

    gad_hide = gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE);

    /*
     *  Quit gadget
     */    
    ng.ng_LeftEdge     += ng.ng_Width + dummy;
    ng.ng_GadgetText    = (UBYTE *)msg_quit;
    ng.ng_GadgetID      = GAD_QUIT;
    ng.ng_UserData      = (APTR)CCMD_QUIT;

    gad_quit = gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE);

    *height = gad->TopEdge + gad->Height + DBL_INTERHEIGHT;

    return(gad);
}
