/*
 *  DMOUSE-HANDLER.C            compile 32 bit integers (+L)
 *                              AZTEC COMPILATION
 *  18 May 1988
 */
#include "dmouse.h"
#define IBASE IntuitionBase
DMS     *Dms;
struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
long                 *LayersBase;
NS      Ns = {  0, 0, 64, -1, 1, -1, -1, 0, CUSTOMSCREEN|SCREENQUIET };
IE *handler();
_main()
{
    register DMS *dms;
    IOR  *ior;
    INT addhand;
    {
        register PROC *proc = (PROC *)FindTask(NULL);
        proc->pr_ConsoleTask = NULL;
    }
    dms = Dms = FindPort(PORTNAME);
    if (!dms)
        _exit(0);
    dms->Port.mp_Flags = PA_SIGNAL;
    dms->Port.mp_SigBit = AllocSignal(-1);
    dms->Port.mp_SigTask = FindTask(NULL);
    dms->HandTask = dms->Port.mp_SigTask;
    ior = CreateStdIO(&dms->Port);
    IntuitionBase = OpenLibrary("intuition.library", 0);
    GfxBase = OpenLibrary("graphics.library", 0);
    LayersBase = OpenLibrary("layers.library", 0);
    if (!IntuitionBase || !GfxBase || !LayersBase)
        goto startupfail;
    addhand.is_Node.ln_Pri = 51;
    addhand.is_Code = (FPTR)handler;
    addhand.is_Data = NULL;
    if (OpenDevice("input.device", 0, ior, 0)) {
        goto startupfail;
    } else {
        SCR *scr = NULL;
        uword *SprSavePtr = NULL;
        Signal(dms->ShakeTask, 1 << dms->ShakeSig);
        ior->io_Command = IND_ADDHANDLER;
        ior->io_Data = (APTR)&addhand;
        DoIO(ior);
        for (;;) {
            register long sigs = Wait(SBF_C|(1<<dms->Port.mp_SigBit));
            if (sigs & (1 << dms->Port.mp_SigBit)) {
                register MSG *msg;
                while (msg = GetMsg(&dms->Port)) {
                    switch((long)msg->mn_Node.ln_Name) {
                    case REQ_SCREENON:
                        if (scr)
                            CloseScreen(scr);
                        scr = NULL;
                        break;
                    case REQ_SCREENOFF:
                        if (scr) {
                            ScreenToFront(scr);
                        } else {
                            if (scr = OpenScreen(&Ns))
                                SetRGB4(&scr->ViewPort, 0, 0, 0, 0);
                        }
                        break;
                    case REQ_MOUSEON:
                        if (SprSavePtr) {
                            register COPINIT *ci = GfxBase->copinit;
                            ci->sprstrtup[1] = (ulong)SprSavePtr >> 16;
                            ci->sprstrtup[3] = (uword)(long)SprSavePtr;
                            SprSavePtr = NULL;
                        }
                        break;
                    case REQ_MOUSEOFF:
                        {
                            register COPINIT *ci = GfxBase->copinit;
                            if (!SprSavePtr)
                                SprSavePtr = (uword *)((ci->sprstrtup[1] << 16) | ci->sprstrtup[3]);
                            ci->sprstrtup[1] = (ulong)dms->NoSprData >> 16;
                            ci->sprstrtup[3] = (uword)(long)dms->NoSprData;
                        }
                        break;
                    case REQ_DOCMD:
                        {
                            long fh = Open("nil:", 1006);
                            Execute(dms->Cmd, NULL, fh);
                            if (fh)
                                Close(fh);
                        }
                        break;
                    }
                    FreeMem(msg, msg->mn_Length);
                }
            }
            if (sigs & SBF_C)
                break;
        }
        Forbid();
        ior->io_Command = IND_REMHANDLER;
        ior->io_Data = (APTR)&addhand;
        DoIO(ior);
        Permit();
        CloseDevice(ior);
        {
            register MSG *msg;
            while (msg = GetMsg(&dms->Port))
                FreeMem(msg, msg->mn_Length);
        }
        if (scr)
            CloseScreen(scr);
        if (SprSavePtr) {
            register COPINIT *ci = GfxBase->copinit;
            ci->sprstrtup[1] = (ulong)SprSavePtr >> 16;
            ci->sprstrtup[3] = (uword)(long)SprSavePtr;
            SprSavePtr = NULL;
        }
    }
    goto closedown;
startupfail:
    dms->StartupError = 1;
    Signal(dms->ShakeTask, 1 << dms->ShakeSig);
    Wait(SBF_C);
closedown:
    DeleteStdIO(ior);
fail:
    if (IntuitionBase)
        CloseLibrary(IntuitionBase);
    if (GfxBase)
        CloseLibrary(GfxBase);
    if (LayersBase)
        CloseLibrary(LayersBase);
    Forbid();
    Signal(dms->ShakeTask, 1 << dms->ShakeSig);
}
#asm
            ;   A0 = pointer to event linked list
            ;   A1 = pointer to my data segment
            ;   return new event linked list in D0
            public  _CHandler
_handler:
            movem.l D2/D3/A0/A1/A4/A6,-(sp)
            jsr     _CHandler
            movem.l (sp)+,D2/D3/A0/A1/A4/A6
            rts
#endasm
/*
 *  (1) Accellerate mouse movements.
 *  (2) Auto-Select window
 */
IE *
CHandler(scr0, scr1, Ev)
IE *Ev;
{
    register IE *ev;
    register DMS *dms;
    static char STimedout;
    static char MTimedout;
    static long STime, MTime;
    geta4();
    dms = Dms;
    for (ev = Ev; ev; ev = Ev->ie_NextEvent) {
        switch(ev->ie_Class) {
        case IECLASS_RAWMOUSE:
            /*
             *  Mouse events restore both the screen and mouse pointer.
             */
            STime = ev->ie_TimeStamp.tv_secs + dms->STo;
            MTime = ev->ie_TimeStamp.tv_secs + dms->MTo;
            if (STimedout)
                sendrequest(REQ_SCREENON);
            if (MTimedout)
                sendrequest(REQ_MOUSEON);
            STimedout = MTimedout = 0;
            /*
             *  Mouse Acceleration
             */
            {
                register short n;
                register short s;
                if (dms->Acc != 1) {
                    n = ev->ie_X;
                    s = 1;
                    if (n < 0) {
                        n = -n;
                        s = -1;
                    }
                    if (n > dms->AThresh)
                        ev->ie_X = s * (short)((n - dms->AThresh - 1) * dms->Acc + dms->AThresh + 1);
                    n = ev->ie_Y;
                    s = 1;
                    if (n < 0) {
                        n = -n;
                        s = -1;
                    }
                    if (n > dms->AThresh)
                        ev->ie_Y = s * (short)((n - dms->AThresh - 1) * dms->Acc + dms->AThresh + 1);
                }
            }
            /*
             *  Auto Activate and LMB (win/scrn front/bak)
             */
            if (dms->AAEnable | dms->LMBEnable) {
                register LAYER *layer;
                Forbid();
                layer = WhichMouseLayer();
                if (dms->LMBEnable && ev->ie_Code == IECODE_RBUTTON && (ev->ie_Qualifier & dms->RQual)) {
                    register WIN *win;
                    if (layer && (win = (WIN *)layer->Window) && !(win->Flags & BACKDROP) && (win->NextWindow || win->WScreen->FirstWindow != win))
                        WindowToBack(layer->Window);
                    else if (IBASE->FirstScreen)
                        ScreenToBack(IBASE->FirstScreen);
                    ev->ie_Class = IECLASS_NULL;    /*  remove event    */
                }
                if (layer && layer->Window) {
                    if (dms->LMBEnable && ev->ie_Code == IECODE_LBUTTON && layer->ClipRect && layer->ClipRect->Next) {
                        /*
                         *  Note: Case where it is the 'first' click in a series, where dms->CTime is
                         *        garbage, works properly no matter what DoubleClick returns.
                         */
                        if ((APTR)dms->CWin == layer->Window && DoubleClick(dms->CTime.tv_secs, dms->CTime.tv_micro, ev->ie_TimeStamp.tv_secs, ev->ie_TimeStamp.tv_micro))
                            --dms->CLeft;
                        else
                            dms->CLeft = dms->Clicks - 1;
                        dms->CTime = ev->ie_TimeStamp;
                        dms->CWin = (WIN *)layer->Window;
                        if (dms->CLeft == 0) {
                            dms->CLeft = dms->Clicks;
                            WindowToFront(layer->Window);
                        }
                    }
                    if (dms->AAEnable && ev->ie_Code == IECODE_NOBUTTON && !(ev->ie_Qualifier & 0x7000) && layer->Window != IBASE->ActiveWindow)
                        ActivateWindow(layer->Window);
                }
                Permit();
            }
            break;
        case IECLASS_RAWKEY:
            /*
             *  Keyboard events will kill the screen timeout but not
             *  the mouse timeout.
             */
            {
                register LAYER *layer;
                Forbid();
                layer = WhichMouseLayer();
                if (layer && layer->Window) {
                    if (dms->AAEnable && layer->Window != IBASE->ActiveWindow)
                        ActivateWindow(layer->Window);
                }
                Permit();
            }
            STime = ev->ie_TimeStamp.tv_secs + dms->STo;
            if (STimedout)
                sendrequest(REQ_SCREENON);
            STimedout = 0;
            if (ev->ie_Code == dms->Code && ev->ie_Qualifier == dms->Qual) {
                sendrequest(REQ_DOCMD);
                ev->ie_Class = IECLASS_NULL;    /*  remove event    */
            }
            break;
        case IECLASS_TIMER:
            /*
             *  On a timer event, if timeout has occured execute the operation
             *  and reset the timeout.  Note that this will cause continuous
             *  timeouts every STo and MTo seconds... required because at any
             *  time Intuition might turn the mouse back on or open a screen or
             *  something and I want the blanker's to work in the long run.
             */
            {
                register long old;
                if (dms->STo && (old = STime - ev->ie_TimeStamp.tv_secs) < 0) {
                    STime = ev->ie_TimeStamp.tv_secs + dms->STo + 10;
                    STimedout = 1;
                    if (old > -10)
                        sendrequest(REQ_SCREENOFF);
                }
                if (dms->MTo && (old = MTime - ev->ie_TimeStamp.tv_secs) < 0) {
                    MTime = ev->ie_TimeStamp.tv_secs + dms->MTo + 1;
                    MTimedout = 1;
                    if (old > -10)
                        sendrequest(REQ_MOUSEOFF);
                }
            }
            break;
        }
    }
    return(Ev);
}
sendrequest(req)
long req;
{
    register MSG *msg = AllocMem(sizeof(MSG), MEMF_PUBLIC);
    if (msg) {
        msg->mn_Node.ln_Name = (char *)req;
        msg->mn_ReplyPort = NULL;
        msg->mn_Length = sizeof(MSG);
        PutMsg(&Dms->Port, msg);
    }
}
LAYER *
WhichMouseLayer()
{
    register struct IntuitionBase *ib = IBASE;
    register LAYER *layer = NULL;
    register SCR *scr = ib->FirstScreen;
    for (scr = ib->FirstScreen; scr; scr = scr->NextScreen) {
        register short mousey = ib->MouseY;
        if (!(scr->ViewPort.Modes & LACE))
            mousey >>= 1;
        if (layer = WhichLayer(&scr->LayerInfo, ib->MouseX, mousey - scr->ViewPort.DyOffset))
            break;
        if (mousey >= scr->ViewPort.DyOffset)
            break;
    }
    return(layer);
}
