;                           QMouse 1.2  by Lyman Epp
;
;        Another "Mouse Accelerator???"  What for?  It seems that all the
;        other mouse accelerators have been written in "C".  This is okay,
;        but the programs are 10-30K.  This mouse accelerator is written
;        in assembler and contains most of the features of the others, but
;        the program size is less than 3K bytes!  Also, the source is
;        included so that you can add more features (please send me a copy
;        if you do.)  QMouse stands for "Quick Mouse."
;
;        This is being distributed as ShareWare.  If you think that QMouse
;        is useful, please send $10.  Your response will insure the release
;        of more quality programs for your Amiga!  Programs already in the
;        works include: a disk based print spooler, a window dump that
;        converts the window bitmap back to text, and more.  Be sure to
;        check out the latest shareware version of QView.
;
;        The latest version of QMouse can always be found on the Wind
;        Dragon Inn BBS, (402)-291-8053.
;
;                Lyman R. Epp
;                10072 Wirt Plaza #15
;                Omaha, Nebraska 68134


         SECTION  text,CODE

         INCLUDE  "exec/execbase.i"
         INCLUDE  "exec/memory.i"
         INCLUDE  "libraries/dosextens.i"
         INCLUDE  "devices/input.i"
         INCLUDE  "graphics/gfxbase.i"
         INCLUDE  "graphics/copper.i"
         INCLUDE  "hardware/custom.i"
         INCLUDE  "hardware/dmabits.i"
         INCLUDE  "intuition/intuition.i"
         INCLUDE  "intuition/intuitionbase.i"


RAW_M               EQU     $37
RAW_ESCAPE          EQU     $45

CLOCK_WIDTH         EQU     44

FLAGB_BLANK         EQU     0
FLAGB_POINTEROFF    EQU     1
FLAGB_NO_AMIGA_M    EQU     2
FLAGB_CLOCK         EQU     3
FLAGB_SUNMOUSE      EQU     4
FLAGB_CLICKBACK     EQU     5

POSTB_BLANK         EQU     0
POSTB_UNBLANK       EQU     1
POSTB_NEWCLI        EQU     2
POSTB_TIMER         EQU     3
POSTB_SHUFFLE       EQU     4
POSTB_FRONT         EQU     5
POSTB_BACK          EQU     6
POSTB_ACTIVATE      EQU     7
POSTB_QUIT          EQU     8


        STRUCTURE work,0

        APTR    dosBase
        APTR    gfxBase
        APTR    layersBase
        APTR    intuitionBase

        APTR    parmAddr

        APTR    qmouseTask
        ULONG   qmouseSignal
        UWORD   qmousePost
        UBYTE   qmouseFlag
        UBYTE   filler

        UWORD   mouseAccel
        UWORD   accelThresh
        UWORD   pointerSecs
        UWORD   screenSecs
        UWORD   numClicks

        APTR    front
        APTR    back
        APTR    activate

        APTR    clockWindow
        APTR    blankScreen
        APTR    blankSprite
        APTR    saveSprite
        ULONG   nullHandle
        ULONG   timerSec
        UWORD   blankTimer
        UWORD   pointerTimer
        UWORD   clicksLeft
        APTR    clickWindow
        STRUCT  clickTime,8

        STRUCT  execCommand,100

        STRUCT  inputReqBlk,IOSTD_SIZE
        STRUCT  handlerStuff,IS_SIZE

        LABEL   work_SIZEOF



        XREF    _AbsExecBase


CALLSYS MACRO
        XREF    _LVO\1
        CALLLIB _LVO\1
        ENDM

LINKSYS MACRO
        XREF    _LVO\1
        LINKLIB _LVO\1,\2
        ENDM

ON_DISPLAY  MACRO
        move.w  #BITSET!DMAF_RASTER,$dff000+dmacon
        ENDM

OFF_DISPLAY  MACRO
        move.w  #BITCLR!DMAF_RASTER,$dff000+dmacon
        ENDM



QMouse:
        move.l  _AbsExecBase,a6
        move.l  #workarea,a5

        IFD     MANX
        move.l  a5,a1
        move.l  #(work_SIZEOF-1)/4,d1   ; round to the number of longs - 1 !!
Clear_Mem:
        clr.l   (a1)+                   ; manx doesn't set the length
        dbf     d1,Clear_Mem            ; of the BSS hunk, so the loader
        ENDC                            ; doesn't clear it !!!

        move.l  ThisTask(a6),qmouseTask(a5)
        move.l  a0,parmAddr(a5)         ; save parm len/address

        lea     dosName(pc),a1          ; open "dos.library"
        moveq   #LIBRARY_VERSION,d0
        CALLSYS OpenLibrary
        move.l  d0,dosBase(a5)
        beq     abort

        lea     graphicsName(pc),a1     ; open "graphics.library"
        moveq   #LIBRARY_VERSION,d0
        CALLSYS OpenLibrary
        move.l  d0,gfxBase(a5)
        beq     abort

        lea     layersName(pc),a1       ; open "layers.library"
        moveq   #LIBRARY_VERSION,d0
        CALLSYS OpenLibrary
        move.l  d0,layersBase(a5)
        beq     abort

        lea     intuitionName(pc),a1    ; open "intuition.library"
        moveq   #LIBRARY_VERSION,d0
        CALLSYS OpenLibrary
        move.l  d0,intuitionBase(a5)
        beq     abort

        move.w  #3,mouseAccel(a5)       ; initialize the defaults
        move.w  #3,accelThresh(a5)
        move.w  #10,pointerSecs(a5)
        move.w  #600,screenSecs(a5)
        move.w  #2,numClicks(a5)
        movem.l defaultCommand(pc),d0-d1
        movem.l d0-d1,execCommand(a5)

        move.l  parmAddr(a5),a0         ; get parm len/address
        bsr     ParseInput

        lea     qmousePortName(pc),a1
        CALLSYS FindPort
        tst.l   d0
        bne     abort

        moveq   #12,d0                  ; minimum sprite size
        move.l  #MEMF_CHIP!MEMF_CLEAR,d1
        CALLSYS AllocMem                ; allocate working storage
        move.l  d0,blankSprite(a5)
        beq     abort
        move.l  d0,a0
        move.w  #$FF00,(a0)
        move.w  #$FF00,2(a0)

        move.w  screenSecs(a5),blankTimer(a5)
        move.w  pointerSecs(a5),pointerTimer(a5)

        lea     nullName(pc),a1
        move.l  a1,d1
        move.l  #MODE_NEWFILE,d2
        LINKSYS Open,dosBase(a5)
        move.l  d0,nullHandle(a5)
        beq     abort

        move.l  qmouseTask(a5),a1
        move.b  pr_MsgPort+MP_SIGBIT(a1),d1
        moveq   #0,d0
        bset    d1,d0
        move.l  d0,qmouseSignal(a5)

        btst    #FLAGB_CLOCK,qmouseFlag(a5)
        beq     Main_NoClock
        lea     clockNewWindow(pc),a0
        LINKSYS OpenWindow,intuitionBase(a5)
        move.l  d0,clockWindow(a5)
        beq     abort
        bsr     Clock
Main_NoClock:

        lea     qmousePortName(pc),a0
        moveq   #0,d0
        bsr     CreatePort
        tst.l   d0
        beq     abort

        lea     inputReqBlk(a5),a1
        move.b  #NT_MESSAGE,LN_TYPE(a1)
        move.w  #IOSTD_SIZE,MN_LENGTH(a1)
        move.l  d0,MN_REPLYPORT(a1)

        lea     inputName(pc),a0
        moveq   #0,d0
        moveq   #0,d1
        CALLSYS OpenDevice
        tst.l   d0
        bne     abort

        lea     MyHandler(pc),a1
        lea     handlerStuff(a5),a0
        move.b  #51,LN_PRI(a0)          ; before Intuition !!
        move.l  a1,IS_CODE(a0)

        move.l  qmouseTask(a5),a1
        moveq   #25,d0                  ; before input.device !!
        CALLSYS SetTaskPri

        lea     inputReqBlk(a5),a1
        move.l  a0,IO_DATA(a1)
        move.w  #IND_ADDHANDLER,IO_COMMAND(a1)
        CALLSYS DoIO

Main_Wait:
        move.l  qmouseSignal(a5),d0
        CALLSYS Wait
        CALLSYS Forbid

        move.w  qmousePost(a5),d7
        clr.w   qmousePost(a5)

        btst    #POSTB_BLANK,d7
        beq     Main_Blank
        bsr     BlankScreen
Main_Blank:

        btst    #POSTB_UNBLANK,d7
        beq     Main_Unblank
        bsr     UnblankScreen
Main_Unblank:

        btst    #POSTB_NEWCLI,d7
        beq     Main_Newcli
        bsr     NewCLI
Main_Newcli:

        btst    #POSTB_TIMER,d7
        beq     Main_Timer
        bsr     Clock
Main_Timer:

        btst    #POSTB_SHUFFLE,d7
        beq     Main_Shuffle
        move.l  intuitionBase(a5),a0
        move.l  ib_FirstScreen(a0),a0
        LINKSYS ScreenToBack,intuitionBase(a5)
Main_Shuffle:

        btst    #POSTB_FRONT,d7
        beq     Main_Front
        move.l  front(a5),a0
        LINKSYS WindowToFront,intuitionBase(a5)
Main_Front:

        btst    #POSTB_BACK,d7
        beq     Main_Back
        move.l  back(a5),a0
        LINKSYS WindowToBack,intuitionBase(a5)
Main_Back:

        btst    #POSTB_ACTIVATE,d7
        beq     Main_Active
        move.l  activate(a5),a0
        LINKSYS ActivateWindow,intuitionBase(a5)
Main_Active:

        CALLSYS Permit

        btst    #POSTB_QUIT,d7
        beq     Main_Wait


Main_Quit:
        lea     inputReqBlk(a5),a1
        move.w  #IND_REMHANDLER,IO_COMMAND(a1)
        CALLSYS DoIO

        lea     inputReqBlk(a5),a1
        CALLSYS CloseDevice

        lea     inputReqBlk(a5),a1
        move.l  MN_REPLYPORT(a1),d0
        bsr     DeletePort

        bsr     PointerOn


abort:
        move.l  clockWindow(a5),d0
        beq     Abort_NoWindow
        move.l  d0,a0
        LINKSYS CloseWindow,intuitionBase(a5)
Abort_NoWindow:

        move.l  blankSprite(a5),d0
        beq     Abort_NoBlankSprite
        move.l  d0,a1
        moveq   #12,d0
        CALLSYS FreeMem
Abort_NoBlankSprite:

        move.l  intuitionBase(a5),d0
        beq     Abort_NoIntuitionLib
        move.l  d0,a1
        CALLSYS CloseLibrary
Abort_NoIntuitionLib:

        move.l  layersBase(a5),d0
        beq     Abort_NoLayersLib
        move.l  d0,a1
        CALLSYS CloseLibrary
Abort_NoLayersLib:

        move.l  gfxBase(a5),d0
        beq     Abort_NoGraphicsLib
        move.l  d0,a1
        CALLSYS CloseLibrary
Abort_NoGraphicsLib:

        moveq   #0,d1                   ; initialize return code
        LINKSYS Exit,dosBase(a5)        ; the SAFE way to return!



CreatePort:       ; ( Name:a0, Pri:d0 )
        movem.l d2-d4/a2,-(sp)
        move.l  a0,d3
        move.b  d0,d4

        moveq   #-1,d0
        CALLSYS AllocSignal
        cmpi.b  #-1,d0
        beq     CreatePort_Err
        move.b  d0,d2

        moveq   #MP_SIZE,d0
        move.l  #MEMF_PUBLIC!MEMF_CLEAR,d1
        CALLSYS AllocMem
        tst.l   d0
        beq     CreatePort_Err
        move.l  d0,a2

        move.l  d3,LN_NAME(a2)
        move.b  d4,LN_PRI(a2)
        move.b  #NT_MSGPORT,LN_TYPE(a2)
        move.b  #PA_SIGNAL,MP_FLAGS(a2)
        move.b  d2,MP_SIGBIT(a2)
        move.l  ThisTask(a6),MP_SIGTASK(a2)

        tst.l   d3
        beq     CreatePortNoName
        move.l  a2,a1
        CALLSYS AddPort
        move.l  a2,d0
        bra     CreatePort_End

CreatePortNoName:
        lea     MP_MSGLIST(a2),a1
        NEWLIST a1
        move.l  a2,d0

CreatePort_End:
        movem.l (sp)+,d2-d4/a2
        rts

CreatePort_Err:
        moveq   #0,d0
        bra     CreatePort_End



DeletePort:       ; ( Port:d0 )
        move.l  a2,-(sp)
        move.l  d0,a2

        tst.l   LN_NAME(a2)
        beq     DeletePort_NoName
        move.l  a2,a1
        CALLSYS RemPort
DeletePort_NoName:

        move.b  MP_SIGBIT(a2),d0
        CALLSYS FreeSignal

        move.l  a2,a1
        moveq   #MP_SIZE,d0
        CALLSYS FreeMem

        move.l  (sp)+,a2
        rts



ParseInput:     ; ( CommandLine:a0, Length:d0 )
        moveq   #0,d1                    ; reset quoted flag
Parse_Loop:
        move.b  (a0)+,d2
        cmpi.b  #10,d2
        beq     Parse_Done
        cmpi.b  #'"',d2
        beq     Parse_QuoteChar
        cmpi.b  #'-',d2
        beq     Parse_Option
        cmpi.b  #' ',d2
        beq     Parse_Loop
        bra     ErrUsage

Parse_Done:
        btst    #31,d1
        bne     ErrUsage
        rts

Parse_QuoteChar:
        bset    #31,d1
        bra     Parse_Loop

Parse_Option:
        move.b  (a0)+,d2
        or.b    #$20,d2                  ; force to lowercase
        cmpi.b  #'a',d2                  ; Accel factor
        beq     Parse_Acc
        cmpi.b  #'b',d2                  ; click to back enabled
        beq     Parse_ClickBack
        cmpi.b  #'c',d2                  ; Clock enable
        beq     Parse_Clock
        cmpi.b  #'e',d2                  ; Execute command
        beq     Parse_Exec
        cmpi.b  #'k',d2                  ; number of clicks (to front)
        beq     Parse_Klick
        cmpi.b  #'m',d2                  ; Amiga-M disable
        beq     Parse_Amiga_M
        cmpi.b  #'p',d2                  ; Pointer Off seconds
        beq     Parse_Pointer
        cmpi.b  #'r',d2                  ; Sun Mouse enable?
        beq     Parse_SunMouse
        cmpi.b  #'s',d2                  ; Screen Blank seconds
        beq     Parse_Screen
        cmpi.b  #'t',d2                  ; Accel thresh-hold
        beq     Parse_Thresh
        cmpi.b  #'v',d2                  ; Stack Size value
        beq     Parse_Stack
        bra     ErrUsage

Parse_Next:
        bclr    #31,d1
        beq     Parse_Loop
        cmpi.b  #'"',(a0)+
        beq     Parse_Loop
        bra     ErrUsage

Parse_Acc:
        bsr     GetNumber
        move.w  d0,mouseAccel(a5)
        bra     Parse_Next

Parse_ClickBack:
        bset    #FLAGB_CLICKBACK,qmouseFlag(a5)
        bra     Parse_Next

Parse_Clock:
        bset    #FLAGB_CLOCK,qmouseFlag(a5)
        bsr     GetNumber
        tst.w   d0
        beq     Parse_Next
        move.l  intuitionBase(a5),a1
        move.l  ib_FirstScreen(a1),a1
        move.w  sc_Width(a1),d1
        sub.w   #CLOCK_WIDTH,d1
        cmp.w   d1,d0
        bgt     Parse_Next
        lea     clockNewWindow(pc),a1
        move.w  d0,nw_LeftEdge(a1)
        bra     Parse_Next

Parse_Exec:
        lea     execCommand(a5),a1
Parse_ExecLoop:
        move.b  (a0)+,d2
        cmpi.b  #10,d2
        beq     Parse_ExecDone
        btst    #31,d1
        bne     Parse_ExecQuoted
        cmpi.b  #' ',d2                  ; end of the field?
        bne     Parse_ExecMove
        bra     Parse_ExecDone
Parse_ExecQuoted:
        cmpi.b  #'"',d2                  ; end of the field?
        beq     Parse_ExecDone
Parse_ExecMove:
        move.b  d2,(a1)+
        bra     Parse_ExecLoop
Parse_ExecDone:
        move.b  -(a0),d2                 ; point at char that terminated
        clr.b   (a1)                     ; terminate the execute string
        bra     Parse_Next

Parse_Klick:
        bsr     GetNumber
        move.w  d0,numClicks(a5)
        bra     Parse_Next

Parse_Amiga_M:
        bset    #FLAGB_NO_AMIGA_M,qmouseFlag(a5)
        bra     Parse_Next

Parse_Pointer:
        bsr     GetNumber
        move.w  d0,pointerSecs(a5)
        bra     Parse_Next

Parse_SunMouse:
        bset    #FLAGB_SUNMOUSE,qmouseFlag(a5)
        bra     Parse_Next

Parse_Screen:
        bsr     GetNumber
        move.w  d0,screenSecs(a5)
        bra     Parse_Next

Parse_Thresh:
        bsr     GetNumber
        move.w  d0,accelThresh(a5)
        bra     Parse_Next

Parse_Stack:
        bsr     GetNumber
        cmpi.l  #1800,d0
        blt     ErrUsage
        move.l  qmouseTask(a5),a1
        move.l  pr_CLI(a1),a1
        add.l   a1,a1                     ; convert that silly BPTR to ADDR
        add.l   a1,a1
        lsr.l   #2,d0
        move.l  d0,cli_DefaultStack(a1)   ; and set the default stack size!
        bra     Parse_Next


GetNumber:
        moveq   #0,d0
        moveq   #0,d2
GetNumberLoop:
        move.b  (a0)+,d2
        subi.b  #'0',d2
        blt     GetNumber_Exit
        cmpi.b  #9,d2
        bgt     GetNumber_Exit
        mulu    #10,d0
        add.l   d2,d0
        bra     GetNumberLoop
GetNumber_Exit:
        move.b  -(a0),d2                 ; point at char that terminated
        rts


ERR_USAGE        dc.b    $9b,'0;33mQMouse 1.2',$9b,'0m  by Lyman Epp',10
                 dc.b    'Usage: QMouse -A# -B -C# -Ecommand -K# -M -P# -R -S# -T# -V#',10
ERR_USAGE_SIZE   EQU     95               ; make it work with manx

ERR_FATAL        dc.b    'QMouse: Fatal error',10
ERR_FATAL_SIZE   EQU     20               ; make it work with manx

        CNOP    0,2


ErrUsage:
        move.l  #ERR_USAGE,d2
        move.l  #ERR_USAGE_SIZE,d3
        bra     ErrDisplay

ErrFatal:
        move.l  #ERR_FATAL,d2
        move.l  #ERR_FATAL_SIZE,d3
        bra     ErrDisplay

ErrDisplay:
        LINKSYS Output,dosBase(a5)
        move.l  d0,d1
        LINKSYS Write,dosBase(a5)
        bra     abort




PointerOff:
        move.l  gfxBase(a5),a1
        move.l  gb_copinit(a1),a1
        bset    #FLAGB_POINTEROFF,qmouseFlag(a5)
        bne     PointerOffSkip
        move.w  copinit_sprstrtup+2(a1),saveSprite+0(a5)
        move.w  copinit_sprstrtup+6(a1),saveSprite+2(a5)
PointerOffSkip:
        move.w  blankSprite+0(a5),copinit_sprstrtup+2(a1)
        move.w  blankSprite+2(a5),copinit_sprstrtup+6(a1)

PointerOff_End:
        rts



PointerOn:
        bclr    #FLAGB_POINTEROFF,qmouseFlag(a5)
        beq     PointerOn_End

        move.l  gfxBase(a5),a1
        move.l  gb_copinit(a1),a1
        move.w  saveSprite+0(a5),copinit_sprstrtup+2(a1)
        move.w  saveSprite+2(a5),copinit_sprstrtup+6(a1)

PointerOn_End:
        rts



BlankScreen:
        lea     blankNewScreen(pc),a0
        LINKSYS OpenScreen,intuitionBase(a5)
        move.l  d0,blankScreen(a5)
        beq     Blank_End

        move.l  d0,a0
        lea     sc_ViewPort(a0),a0
        moveq   #0,d0
        moveq   #0,d1
        moveq   #0,d2
        moveq   #0,d3
        LINKSYS SetRGB4,gfxBase(a5)

        OFF_DISPLAY

        bset    #FLAGB_BLANK,qmouseFlag(a5)

Blank_End:
        rts



UnblankScreen:
        bclr    #FLAGB_BLANK,qmouseFlag(a5)
        beq     Unblank_End

        move.l  blankScreen(a5),a0
        LINKSYS CloseScreen,intuitionBase(a5)

        ON_DISPLAY

Unblank_End:
        rts



NewCLI:
        LINKSYS WBenchToFront,intuitionBase(a5)

        lea     execCommand(a5),a1
        move.l  a1,d1
        moveq   #0,d2
        move.l  nullHandle(a5),d3
        LINKSYS Execute,dosBase(a5)

NewCLI_End:
        rts



Clock:
        btst    #FLAGB_CLOCK,qmouseFlag(a5)
        beq     Clock_End

        move.l  dosBase(a5),a0
        move.l  dl_Root(a0),a0
        move.l  rn_Time+ds_Minute(a0),d0

        divu    #60,d0
        move.l  d0,d1                   ; save for calculating minutes

        cmpi.w  #12,d0
        ble     Clock_NotPM
        sub.w   #12,d0
Clock_NotPM:
        tst.w   d0
        bne     Clock_NotMidnite
        moveq   #12,d0
Clock_NotMidnite:
        ext.l   d0
        divu    #10,d0
        or.b    #'0',d0
        cmpi.b  #'0',d0
        bne     Clock_FirstCharOk
        move.b  #' ',d0
Clock_FirstCharOk:
        lea     clockBuffer(pc),a0
        move.b  d0,(a0)
        swap    d0
        or.b    #'0',d0
        move.b  d0,1(a0)

        swap    d1
        ext.l   d1
        divu    #10,d1
        or.b    #'0',d1
        move.b  d1,3(a0)
        swap    d1
        or.b    #'0',d1
        move.b  d1,4(a0)

        moveq   #':',d1
        cmp.b   2(a0),d1
        bne     Clock_FlipFlop
        moveq   #' ',d1
Clock_FlipFlop:
        move.b  d1,2(a0)

        move.l  clockWindow(a5),a0
        move.l  wd_RPort(a0),a0
        lea     clockText(pc),a1
        moveq   #0,d0
        moveq   #0,d1
        LINKSYS PrintIText,intuitionBase(a5)

        move.l  clockWindow(a5),a0
        move.l  wd_RPort(a0),a1
        move.l  rp_Layer(a1),a1
        move.l  lr_ClipRect(a1),d0        ; are there any ClipRect structs ??
        beq     Clock_End                 ; no, the clock isn't obscured!
        move.l  d0,a1
        tst.l   cr_Next(a1)               ; more than one ClipRect ??
        bne     Clock_ToFront             ; yes, clock is obscured!
        tst.l   cr_BitMap(a1)             ; was whole clock copied to BitMap ??
        beq     Clock_End                 ; no, the clock isn't obscured!
Clock_ToFront:
        LINKSYS WindowToFront,intuitionBase(a5)

Clock_End:
        rts



AccelMouse:
        move.w  accelThresh(a5),d1
        cmp.w   d1,d0
        bgt     Accel_Yes
        neg.w   d1
        cmp.w   d1,d0
        bge     Accel_No
Accel_Yes:
        sub.w   d1,d0
        mulu    mouseAccel(a5),d0
        add.w   d1,d0
Accel_No:
        rts



SunMouse:
        move.l  a0,-(sp)
        btst    #FLAGB_SUNMOUSE,qmouseFlag(a5)
        beq     SunMouse_End

        bsr     WhichMouseLayer
        tst.l   d0                        ; is pointer over a layer?
        beq     SunMouse_End              ; no, don't bother!
        move.l  d0,a1
        move.l  lr_Window(a1),d0          ; is pointer over a window?
        beq     SunMouse_End              ; no, don't bother!
        move.l  intuitionBase(a5),a1
        cmp.l   ib_ActiveWindow(a1),d0    ; is this window already active?
        beq     SunMouse_End              ; yes, don't bother!

        move.l  d0,activate(a5)           ; activate this window
        bset    #POSTB_ACTIVATE,d7

SunMouse_End:
        move.l  (sp)+,a0
        rts



CheckMouseClicks:
        move.l  a0,-(sp)

        tst.w   numClicks(a5)             ; click to front enabled?
        beq     Click_End

        bsr     WhichMouseLayer           ; check if pointer is over a layer
        tst.l   d0
        beq     Click_None
        move.l  d0,a1
        move.l  lr_Window(a1),d4          ; check if pointer is over a window
        beq     Click_None
        move.l  lr_ClipRect(a1),d0        ; is this window already up front?
        beq     Click_None                ; yes, don't waste more time!
        move.l  d0,a1
        tst.l   cr_Next(a1)               ; is this window already up front?
        beq     Click_None                ; yes, don't waste more time!
        move.l  d4,a1
        move.l  wd_Flags(a1),d0
        and.w   #BACKDROP,d0              ; is this a BACKDROP window?
        bne     Click_None                ; yes, don't waste more time!

        movem.l ie_TimeStamp(a0),d2-d3
        cmp.l   clickWindow(a5),d4        ; same window as last click?
        bne     Click_DiffWindow          ; no, reset clicks left
        movem.l clickTime(a5),d0-d1       ; let intuition check if they
        LINKSYS DoubleClick,intuitionBase(a5)  ; double clicked soon enough
        tst.l   d0
        bne     Click_CheckCount
Click_DiffWindow:
        move.w  numClicks(a5),clicksLeft(a5)
Click_CheckCount:
        movem.l d2-d3,clickTime(a5)
        move.l  d4,clickWindow(a5)
        sub.w   #1,clicksLeft(a5)
        bne     Click_End
        move.l  d4,front(a5)              ; bring this window to front
        bset    #POSTB_FRONT,d7
Click_None:
        move.w  numClicks(a5),clicksLeft(a5)

Click_End:
        move.l  (sp)+,a0
        rts



WindowBack:
        move.l  a0,-(sp)

        btst    #FLAGB_CLICKBACK,qmouseFlag(a5)
        beq     Back_End

        bsr     WhichMouseLayer           ; check if pointer is over a layer
        tst.l   d0
        beq     Back_End
        move.l  d0,a1
        move.l  lr_Window(a1),d0          ; check if pointer is over a window
        beq     Back_End
        move.l  d0,a0
        move.l  wd_Flags(a0),d0
        and.w   #BACKDROP,d0              ; is this a BACKDROP window?
        bne     Back_End                  ; yes, don't waste more time!

        move.l  a0,back(a5)               ; send this window to back
        bset    #POSTB_BACK,d7

Back_End:
        move.l  (sp)+,a0
        rts



WhichMouseLayer:
        movem.l d1-d3/a0-a3,-(sp)

        CALLSYS Forbid

        move.l  intuitionBase(a5),a3
        move.l  ib_FirstScreen(a3),d0
        beq     Layer_Exit
Layer_Loop:
        move.l  d0,a2
        move.w  ib_MouseX(a3),d2
        move.w  sc_ViewPort+vp_Modes(a2),d0
        and.w   #V_HIRES,d0
        bne     Layer_Hires
        lsr.w   #1,d2
Layer_Hires:
        move.w  ib_MouseY(a3),d3
        move.w  sc_ViewPort+vp_Modes(a2),d0
        and.w   #V_LACE,d0
        bne     Layer_Interlace
        lsr.w   #1,d3
Layer_Interlace:
        lea     sc_LayerInfo(a2),a0
        moveq   #0,d0
        move.w  d2,d0
        moveq   #0,d1
        move.w  d3,d1
        sub.w   sc_ViewPort+vp_DyOffset(a2),d1
        LINKSYS WhichLayer,layersBase(a5)
        tst.l   d0
        bne     Layer_Exit
        cmp.w   sc_ViewPort+vp_DyOffset(a2),d3
        bge     Layer_Exit
        move.l  sc_NextScreen(a2),d0
        bne     Layer_Loop

Layer_Exit:
        CALLSYS Permit

        movem.l (sp)+,d1-d3/a0-a3
        rts



MyHandler:
        movem.l d7/a0/a5,-(sp)
        move.l  #workarea,a5
        moveq   #0,d7

Handler_Loop:
        cmpi.b  #IECLASS_TIMER,ie_Class(a0)
        bne     Handler_NotTimer
        bsr     TimerEvent
        bra     Handler_NextEvent

Handler_NotTimer:
        bsr     NotTimerEvent
        cmpi.b  #IECLASS_RAWMOUSE,ie_Class(a0)
        bne     Handler_NotMouse
        bsr     MouseEvent
        bra     Handler_NextEvent

Handler_NotMouse:
        cmpi.b  #IECLASS_RAWKEY,ie_Class(a0)
        bne     Handler_NextEvent
        bsr     RawKeyEvent

Handler_NextEvent:
        move.l  ie_NextEvent(a0),d0
        move.l  d0,a0
        bne     Handler_Loop

        move.w  d7,qmousePost(a5)
        beq     Handler_End

        move.l  qmouseTask(a5),a1
        move.l  qmouseSignal(a5),d0
        LINKSYS Signal,_AbsExecBase

Handler_End:
        move.l  (sp)+,d7
        movem.l (sp)+,d0/a5
        rts



TimerEvent:
        btst    #FLAGB_POINTEROFF,qmouseFlag(a5)  ; Intuition will restore the
        beq     Timer_PointerOK                   ; pointer--make sure its off!
        bsr     PointerOff
Timer_PointerOK:

        move.l  ie_TimeStamp(a0),d0
        cmp.l   timerSec(a5),d0
        beq     Timer_End

        move.l  d0,timerSec(a5)
        bset    #POSTB_TIMER,d7

        tst.w   pointerSecs(a5)             ; pointer blank enabled?
        beq     Timer_NotPointer
        btst    #FLAGB_POINTEROFF,qmouseFlag(a5)
        bne     Timer_NotPointer
        subq.w  #1,pointerTimer(a5)
        bne     Timer_NotPointer
        bsr     PointerOff
Timer_NotPointer:

        tst.w   screenSecs(a5)              ; Screen blank enabled?
        beq     Timer_NotBlank
        btst    #FLAGB_BLANK,qmouseFlag(a5)
        bne     Timer_NotBlank
        subq.w  #1,blankTimer(a5)
        bne     Timer_NotBlank

        bset    #POSTB_BLANK,d7
Timer_NotBlank:

Timer_End:
        rts



NotTimerEvent:
        move.w  screenSecs(a5),blankTimer(a5)

        btst    #FLAGB_BLANK,qmouseFlag(a5)
        beq     NotTimer_End            ; screen not blank -- return
        bset    #POSTB_UNBLANK,d7

NotTimer_End:
        rts



MouseEvent:
        movem.l d1-d3/a0,-(sp)

        move.w  pointerSecs(a5),pointerTimer(a5)

        btst    #FLAGB_POINTEROFF,qmouseFlag(a5)
        beq     Mouse_NotPointer         ; pointer already on -- skip
        bsr     PointerOn
Mouse_NotPointer:

        cmpi.w  #1,mouseAccel(a5)
        ble     Mouse_NoAccel
        move.w  ie_Qualifier(a0),d0
        btst    #IEQUALIFIERB_RELATIVEMOUSE,d0
        beq     Mouse_NoAccel
        move.w  ie_X(a0),d0
        bsr     AccelMouse
        move.w  d0,ie_X(a0)
        move.w  ie_Y(a0),d0
        bsr     AccelMouse
        move.w  d0,ie_Y(a0)
Mouse_NoAccel:

        cmpi.w  #IECODE_LBUTTON,ie_Code(a0)
        bne     Mouse_NoClick
        bsr     CheckMouseClicks
Mouse_NoClick:

        cmpi.w  #IECODE_RBUTTON,ie_Code(a0)
        bne     Mouse_NoBack
        move.w  ie_Qualifier(a0),d0
        btst    #IEQUALIFIERB_LEFTBUTTON,d0
        beq     Mouse_NoBack
        bsr     WindowBack
        move.b  #IECLASS_NULL,ie_Class(a0)
Mouse_NoBack:

        cmpi.w  #IECODE_NOBUTTON,ie_Code(a0)
        bne     Mouse_NoSunMouse
        move.w  ie_Qualifier(a0),d0
        and.w   #IEQUALIFIER_LEFTBUTTON!IEQUALIFIER_MIDBUTTON!IEQUALIFIER_RBUTTON,d0
        bne     Mouse_NoSunMouse
        bsr     SunMouse
Mouse_NoSunMouse:

Mouse_End:
        movem.l (sp)+,d1-d3/a0
        rts



RawKeyEvent:
        bsr     SunMouse               ; make sure window is active

        move.w  ie_Qualifier(a0),d0
        btst    #IEQUALIFIERB_LCOMMAND,d0
        beq     RawKey_End

        btst    #FLAGB_NO_AMIGA_M,qmouseFlag(a5)
        bne     RawKey_NotShuffle
        cmpi.w  #RAW_M,ie_Code(a0)
        bne     RawKey_NotShuffle
        move.b  #IECLASS_NULL,ie_Class(a0)
        bset    #POSTB_SHUFFLE,d7
RawKey_NotShuffle:

        cmpi.w  #RAW_ESCAPE,ie_Code(a0)
        bne     RawKey_End
        move.b  #IECLASS_NULL,ie_Class(a0)
        btst    #IEQUALIFIERB_CONTROL,d0
        bne     RawKey_Quit
        bset    #POSTB_NEWCLI,d7
        bra     RawKey_End
RawKey_Quit:
        bset    #POSTB_QUIT,d7

RawKey_End:
        rts





dosName          dc.b    'dos.library',0
graphicsName     dc.b    'graphics.library',0
layersName       dc.b    'layers.library',0
intuitionName    dc.b    'intuition.library',0
inputName        dc.b    'input.device',0

qmousePortName   dc.b    'QMouse 1.2 Port',0
nullName         dc.b    'NIL:',0
defaultCommand   dc.b    'NewCLI',0



blankNewScreen   dc.w    0,0,320,30,1
                 dc.b    0,0
                 dc.w    0
                 dc.w    CUSTOMSCREEN
                 dc.l    0,0,0,0

clockNewWindow   ds.w    0
                 dc.w    280,0
                 dc.w    CLOCK_WIDTH,10
                 dc.b    -1,2
                 dc.l    0
                 dc.l    SMART_REFRESH!NOCAREREFRESH
                 dc.l    0,0,0,0,0
                 dc.w    0,0
                 dc.w    0,0
                 dc.w    WBENCHSCREEN

clockBuffer      dc.b    '00:00',0
                 cnop    0,2

clockText        ds.w    0
                 dc.b    3,2
                 dc.b    RP_JAM2,0
                 dc.w    2,1
                 dc.l    0
                 dc.l    clockBuffer
                 dc.l    0




        IFD     MANX

        BSS     workarea,work_SIZEOF

        ENDC

        IFND    MANX

        SECTION uninit,BSS

workarea:
        ds.b    work_SIZEOF

        ENDC


        END

