/*
 *  MouseAccelerator.c
 *
 *  Commodity
 *
 *  Author: Stefan Sticht
 *
 *  Copyright: source is public domain, no copyright
 *
 *  Version history:
 *
 *  V1.00   initial release
 *  V1.01   two small changes in messages (thanks to Holger Gzella)
 */

#define VERSION "V1.01"

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

/*
 *  include files
 */

#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <devices/inputevent.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 accelerator(CxMsg *cxm);
void closewindow(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;

/*
 *  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                "Mausbeschleuniger"
#define COM_DESCR               "Beschleunigt Mausbewegungen"
#define MSG_ACC                 "Beschleunigungsfaktor: %-lu  "
#define MSG_THRESH              "Beschleunigungsminimum: %-lu  "
#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_ACC                  "FAKTOR"
#define TT_THRESH               "MINIMUM"

#else

#define RETRY_GADGETS           "Retry|Cancel"
#define RESUME_GADGETS          "Resume"
#define MSG_LIBRARY_OPENERR     "%s (V%ld+) can't be opened!"
#define COM_NAME                "MouseAccelerator"
#define COM_DESCR               "Accelerates mouse movements"
#define MSG_ACC                 "Acceleration: %-lu  "
#define MSG_THRESH              "Threshold: %-lu  "
#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_ACC                  "ACCELERATION"
#define TT_THRESH               "THRESHOLD"

#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_TT_ACC          5
#define DEF_TT_THRESH       4

#define MAX_ACC             20
#define MAX_THRESH          50

#define POP_KEY_ID          100

#define CCMD_NOP                0
#define CCMD_HIDE               1
#define CCMD_QUIT               2

#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 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_ACC "=<n>] [" TT_THRESH "=<n>]\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_ACC "=<n>] [" TT_THRESH "=<n>]\n";
#endif

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

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

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 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    */
    };

/*
 *  acceleration and threshold
 */
unsigned short acceleration;
unsigned short threshold;

/*
 *  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 | SLIDERIDCMP | 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_acc[]    = MSG_ACC;
char msg_thresh[] = MSG_THRESH;
char msg_hide[]   = MSG_HIDE;
char msg_quit[]   = MSG_QUIT;

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

#define GAD_ACC     6
#define GAD_THRESH  7
#define GAD_HIDE    8
#define GAD_QUIT    9

struct Gadget *gad_acc;
struct Gadget *gad_thresh;
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;
    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)) {

            long value;

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

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

            /*
             *  acceleration
             */
            value = ArgInt(tooltypes, TT_ACC, DEF_TT_ACC);
            acceleration = (value > 0l) ? value : 1l;
            if (acceleration > MAX_ACC) acceleration = MAX_ACC;

            /*
             *  acceleration
             */
            value = ArgInt(tooltypes, TT_THRESH, DEF_TT_THRESH);
            threshold = (value > 0l) ? value : 1l;
            if (threshold > MAX_THRESH) threshold = MAX_THRESH;

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

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

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

                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 (mousefilter = CxFilter(NULL)) {

                                    AttachCxObj(broker, mousefilter);

                                    SetFilterIX(mousefilter, &mouseix);

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

                                        AttachCxObj(mousefilter, 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(mousefilter) &&
                                            !(hotkeyfilter && CxObjError(hotkeyfilter))) {

                                            /*
                                             *  activate our commodity
                                             */
                                            ActivateCxObj(broker, 1l);
                                            /*
                                             *  now watch our numerous ports
                                             */
                                            processmessages(window);

                                            } /* if !CxObjError() */

                                        } /* if customobj */

                                    } /* if mousefilter */

                                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 | idcmpsigflag);

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

        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);
                                #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 MOUSEMOVE:
                        if (object) {
                            switch(((struct Gadget *)object)->GadgetID & GADTOOLMASK) {

                                case GAD_ACC:
                                    acceleration = code;
                                    break;

                                case GAD_THRESH:
                                    threshold = code;
                                    break;

                                }
                            }
                        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;

                            }
                        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:
                closewindow();
                break;

            case CCMD_QUIT:
                quit = TRUE;
                break;

            } /* switch (command) */

        command = CCMD_NOP;

        } /* while !quit */

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

/*
 *  commodity functions
 */
void __saveds accelerator(CxMsg *cxm)
{
    register struct InputEvent *ie;
    register short n;
    register short s;

    if ((ie = (struct InputEvent *)CxMsgData(cxm)) && (acceleration > 1)) {

        n = ie->ie_X;
        s = 1;

        if (n < 0) {
            n = -n;
            s = -1;
            }

        if (n > threshold)
            ie->ie_X = s * (short)((n - threshold - 1) * acceleration + threshold + 1);

        n = ie->ie_Y;
        s = 1;
        if (n < 0) {
            n = -n;
            s = -1;
            }

        if (n > threshold)
            ie->ie_Y = s * (short)((n - threshold - 1) * acceleration + threshold + 1);

        }
}


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

    if (win) {
        #ifdef DEBUG
        printf("openwindow(): window already open\n");
        #endif

        /*
         *  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);

                        }

                    #ifdef DEBUG
                    else printf("openwindow(): GetVPModeID() == INVALID_ID\n");
                    #endif

                    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)) */

                    #ifdef DEBUG
                    else printf("openwindow(): OpenWindowTagList() failed\n");
                    #endif

                    } /* if (createallgadgets() */

                #ifdef DEBUG
                else printf("openwindow(): createallgadgets() failed\n");
                #endif

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

            #ifdef DEBUG
            else printf("openwindow(): GetVisualInfo() failed\n");
            #endif

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

        #ifdef DEBUG
        else printf("openwindow(): could LockPubScreen(NULL)\n");
        #endif

        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;
        }
}

#define TAG_Max         1
#define TAG_Level       2
#define TAG_LevelFormat 3
#define TAG_MaxLevelLen 4
struct TagItem slidertags[] = {
    GTSL_Min, 1l,
    GTSL_Max, 0l,
    GTSL_Level, 0l,
    GTSL_LevelFormat, 0l,
    GTSL_MaxLevelLen, 0l,
    GTSL_LevelPlace, PLACETEXT_ABOVE,
    TAG_END
    };

/*
 *  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 accwidth;
    short threshwidth;
    short dummy;

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

    /*
     *  calculate width 
     */    
    accwidth = TextLength(rp, msg_acc, strlen(msg_acc));
    *width = max(accwidth + DBL_INTERWIDTH, *width);
    threshwidth = TextLength(rp, msg_thresh, strlen(msg_thresh));
    *width = max(threshwidth + 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);

    /*
     *  Acceleration gadget:
     */
    ng.ng_TopEdge       = scr->WBorTop + (rp->TxHeight << 1) + 1 + DBL_INTERHEIGHT;
    ng.ng_Width         = *width - DBL_INTERWIDTH;
    ng.ng_LeftEdge      = INTERWIDTH;
    ng.ng_Height        = rp->TxHeight + 4;
    ng.ng_TextAttr      = scr->Font;
    ng.ng_GadgetID      = GAD_ACC;
    ng.ng_VisualInfo    = vi;
    slidertags[TAG_Max].ti_Data = (ULONG)MAX_ACC;
    slidertags[TAG_Level].ti_Data = (ULONG)acceleration;
    slidertags[TAG_LevelFormat].ti_Data = (ULONG)msg_acc;
    slidertags[TAG_MaxLevelLen].ti_Data = strlen(msg_acc);
    gad_acc = gad = CreateGadgetA(SLIDER_KIND, gad, &ng, slidertags);

    /*
     *  threshold gadget
     */
    ng.ng_TopEdge      += ng.ng_Height + DBL_INTERHEIGHT + rp->TxHeight;
    ng.ng_GadgetID      = GAD_THRESH;
    slidertags[TAG_Max].ti_Data = (ULONG)MAX_THRESH;
    slidertags[TAG_Level].ti_Data = (ULONG)threshold;
    slidertags[TAG_LevelFormat].ti_Data = (ULONG)msg_thresh;
    slidertags[TAG_MaxLevelLen].ti_Data = strlen(msg_thresh);
    gad_thresh = gad = CreateGadgetA(SLIDER_KIND, gad, &ng, slidertags);

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

    /*
     *  Hide gadget
     */    
    ng.ng_TopEdge      += ng.ng_Height + DBL_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);
}
