; MyMouse
; Input handler bit (to cut down the size of the source file)
; Andrew Forrest
;****************************************************************************
; Routine can't get semaphore since this causes deadlock when we try to
; change the parameters. For this reason, we must use only atomic variables.
;***************************************************************************
;***************************************************************************

; Takes a0=^oldEventChain; a1=^GlobalPtr; Returns d0=^newEventChain;
; Scratches d1, a0, a1
; Uses a4=^first link; a5=^global structure; d1=TimeStamp of last event
IntRoutine:			;Start of input handler code
	pushm.l	a4-a5
	lea	(a0),a4	; First link in event chain
	lea	(a1),a5	; Global Ptr
.loop	  push.l	  a0
	  bsr.s	  Do_Event
	  pop.l	  a0
	  move.l	  ie_NextEvent(a0),d0
	beq.s	.end_loop
	  move.l	  d0,a0
	bra.s	.loop
.end_loop	move.l	ie_TimeStamp+TV_SECS(a0),d1	; time in seconds
	bsr	Time_Outs
	move.l	a4,d0
	popm.l	a4-a5
	rts

;****************************************************************************
Do_Event:
; Takes a0=^Event; a5=^GlobalPtr; 
; scratches d1, a0, a1
	pushm.l	a2/a6
	move.l	IntBase(a5),a6

	;*»»  Perform SunMouse (Activate window)
	tst.l	SunMouseOption(a5)
	  beq.s	  .SkipSunMouse1
	cmp.b	#IECLASS_TIMER,ie_Class(a0)
	  bne.s	  .SkipSunMouse1
	bclr	#STB_SunMouse,Status(a5)
	  beq.s	  .SkipSunMouse1	; Skip if already Sunned
	bsr	GetWindow
	push.l	a0
	move.l	d0,a0
	cmp.l	ib_ActiveWindow(a6),a0
	  beq.s	  .EndSun
	just	ActivateWindow
.EndSun:	pop.l	a0
.SkipSunMouse1:

	cmp.b	#IECLASS_TIMER,ie_Class(a0)
	  beq	  .not_key	; If it's a timer event, don't unblank

	;*»»  Reset screen blank
	move.l	ie_TimeStamp(a0),ScreenTime(a5)
	bclr	#STB_SBlanked,Status(a5)
	beq.s	.SkipSRestore
	  UnBkScreen
.SkipSRestore

	cmp.b	#IECLASS_RAWKEY,ie_Class(a0)
	  bne	  .not_key		; skip this bit if not a key


	;*»»  Do Northgate keyboard mapping.
	tst.l	NorthgateOption(a5)
	  beq.s	  .SkipNorthgate
	lea	NorthgateTable(pc),a2
	move.w	ie_Code(a0),d0
	bclr	#IECODEB_UP_PREFIX,d0
.NorthgateLoop:
	move.b	(a2),d1
	  beq.s	  .EndNorthgate
	addq.l	#3,a2
	cmp.b	d0,d1
	  bne.s	  .NorthgateLoop
	and.w	#IECODE_UP_PREFIX,ie_Code(a0)
	move.b	-2(a2),d0
	or.b	d0,ie_Code+1(a0)
	move.b	-1(a2),d0
	or.b	d0,ie_Qualifier+1(a0)
.EndNorthgate
.SkipNorthgate

	move.w	ie_Code(a0),d0
	tst.b	d0
	  bmi	  .end_event	;henceforth ignore key-ups
	btst	#IEQUALIFIERB_LCOMMAND,ie_Qualifier+1(a0)
	  bne.s	  .do_LAmiga

	;*»»  Blank mouse pointer if not a qualifier key pressed
	tst.l	MouseBlank(a5)
	bmi.s	.NoMBlank
	  and.b	  #%11111000,d0	; If raw key is 60 thru 67,
	  cmp.b	  #$60,d0		; ...we shouldn't blank mouse
	  beq.s	  .NoMBlank	; ...('cos it was shift or something).
	    bset	    #STB_DoMBlank,Status(a5)
	    bsr	    SignalMouseBlank	; Blank mouse on keypress
.NoMBlank	bra	.end_event
	
.do_LAmiga:
	;*»»  Check for Amiga-ESC
	tst.l	CmdOption(a5)
	  beq	  .end_event
	cmp.w	#KEYCODE_ESC,ie_Code(a0)
	  bne	  .end_event
	clr.b	ie_Class(a0)
	move.l	CLISig(a5),d0
	bsr	SignalTask
	  bra	  .end_event

.not_key:
	move.w	ib_MouseX(a6),d0
	move.w	ib_MouseY(a6),d1
	cmp.b	#IECLASS_RAWMOUSE,ie_Class(a0)
	  beq.s	  .do_mouse
	cmp.w	CurrentX(a5),d0
	  bne.s	  .do_mouse
	cmp.w	CurrentY(a5),d1
	  beq	  .end_event

.do_mouse:	move.w	d0,CurrentX(a5)
	move.w	d1,CurrentY(a5)
	;*»»  Restore mouse pointer if the mouse moves
	move.l	ie_TimeStamp(a0),MouseTime(a5)
	btst	#STB_MBlanked,Status(a5)
	  bne.s	  .restore	; Restore mouse if we _know_ it's blanked
	move.l	ib_ActiveWindow(a6),d0	; Examine active window
	  beq.s	  .no_restore	; Exit if no active window(!)
	move.l	d0,a1
	move.l	wd_Pointer(a1),d0
	cmp.l	MouseChipData(a5),d0
	bne.s	.no_restore	; Restore if it _turns out_ to be blanked
.restore	  bclr	  #STB_DoMBlank,Status(a5)
	  bsr	  SignalMouseBlank
.no_restore

	;*»»  Test Sunnyness of mouse
	tst.l	SunMouseOption(a5)
	  beq.s	  .noSunMouse
	move.w	ie_Qualifier(a0),d0
	and.w	#IEQUALIFIER_LEFTBUTTON+IEQUALIFIER_RBUTTON+IEQUALIFIER_MIDBUTTON,d0
	  bne.s	  .noSunMouse
	cmp.w	#IECODE_LBUTTON+IECODE_UP_PREFIX,ie_Code(a0)
	  beq.s	  .noSunMouse
	bset	#STB_SunMouse,Status(a5)
.noSunMouse

	;*»»  Perform click-to-back.
	tst.l	CTBOption(a5)
	  beq	  .no_CTB
	cmp.w	#IECODE_RBUTTON,ie_Code(a0)
	  bne	  .no_CTB
	move.w	ie_Qualifier(a0),d0
	btst	#IEQUALIFIERB_LEFTBUTTON,d0
	  beq	  .no_CTB
	pushm.l	d2/a0-a1
	bsr	GetWindow
	tst.l	d0
	  beq.s	  .end_CTB
	move.l	d0,a1
	move.l	wd_Flags(a1),d2
	and.l	#WFLG_BACKDROP,d2
	  bne.s	  .end_CTB
	;Is this the only window on this screen, except for backdrop windows?
	move.l	wd_WScreen(a1),a1
	move.l	sc_FirstWindow(a1),a1
.back_loop	  cmp.l	  a1,d0
	beq.s	.endback_lp
	  move.l	  wd_Flags(a1),d2
	  and.l	  #WFLG_BACKDROP,d2
	beq.s	.flipwindow
.endback_lp	  move.l	  wd_NextWindow(a1),a1
	  move.l	  a1,d2
	bne.s	.back_loop
	bra.s	.end_CTB
.flipwindow	move.l	d0,a0
	just	WindowToBack
.end_CTB	popm.l	d2/a0-a1
	clr.b	ie_Class(a0)
	bra	.end_event
.no_CTB

	;*»»  Perform click-to-front.
	tst.l	CTF(a5)
	  ble	  .no_CTF
	cmp.w	#IECODE_LBUTTON,ie_Code(a0)
	  bne	  .no_CTF
	btst	#IEQUALIFIERB_LCOMMAND,ie_Qualifier+1(a0)
	  bne	  .no_CTF	;to avoid interferring with Snap
	bsr	GetWindow
	tst.l	d0
	  beq	  .no_CTF
	push.l	d2
	move.l	CTFB(a5),d2
	bmi.s	.no_border
	  move.l	  d0,a1
	  btst	  #BBB_LEFT,d2
	  beq.s	  .test_right
.test_left	    move.w	    wd_MouseX(a1),d1
	    move.b	    wd_BorderLeft(a1),d0
	    ext.w	    d0
	    cmp.w	    d0,d1
	      blo.s	      .in_border
.test_right	  btst	  #BBB_RIGHT,d2
	  beq.s	  .test_top
	    move.b	    wd_BorderRight(a1),d0
	    ext.w	    d0
	    add.w	    d0,d1
	    cmp.w	    wd_Width(a1),d1
	      bhs.s	      .in_border
.test_top	  btst	  #BBB_TOP,d2
	  beq.s	  .test_botto
	    move.w	    wd_MouseY(a1),d1
	    move.b	    wd_BorderTop(a1),d0
	    ext.w	    d0
	    cmp.w	    d0,d1
	      blo.s	      .in_border
.test_botto	  btst	  #BBB_BOTTOM,d2
	  beq	  .end_CTF
	    move.b	    wd_BorderBottom(a1),d0
	    ext.w	    d0
	    add.w	    d0,d1
	    cmp.w	    wd_Height(a1),d1
	      blo	      .end_CTF
.in_border	  move.l	  a1,d0	  
.no_border	move.l	CTF(a5),d1
	cmp.l	#1,d1
	bhi.s	.more_thn_1
	bsr	DoWindowToFront
	  bra	  .end_CTF
.more_thn_1	cmp.l	ClickWindow(a5),d0
	  beq.s	  .same_win
	move.l	d0,ClickWindow(a5)
	moveq	#1,d0
	move.l	d0,ClickCount(a5)
	bra.s	.save_time
.same_win	; Check to see whether MaxClickDelay has been exceeded. If not
	; increment ClickCount and compare with (CTFOption). If equal, do
	; a WindowToFront. Window under pointer in D0.
	push.l	d0
	pushm.l	d2-d3/a0
	move.l	ClickTime+TV_SECS(a5),d0
	move.l	ClickTime+TV_MICRO(a5),d1
	move.l	ie_TimeStamp+TV_SECS(a0),d2
	move.l	ie_TimeStamp+TV_MICRO(a0),d3
	just	DoubleClick
	popm.l	d2-d3/a0
	tst.l	d0
	  beq.s	  .time_exceeded
	addq.l	#1,ClickCount(a5)
	move.l	CTF(a5),d0
	cmp.l	ClickCount(a5),d0
	  bhi.s	  .need_more_clicks
	clr.l	ClickCount(a5)
	pop.l	d0
	bsr	DoWindowToFront
	  bra.s	  .end_CTF
.time_exceeded
	moveq	#1,d0
	move.l	d0,ClickCount(a5)
.need_more_clicks
	pop.l	d0
.save_time	move.l	ie_TimeStamp+TV_SECS(a0),ClickTime+TV_SECS(a5)
	move.l	ie_TimeStamp+TV_MICRO(a0),ClickTime+TV_MICRO(a5)
.end_CTF	pop.l	d2
.no_CTF

	;*»»  Perform acceleration.
	tst.l	Acceleration(a5)
	  bmi	  .end_event
	move.l	Threshold(a5),d0
	bpl.s	.T1
	  moveq	  #0,d0
.T1	move.w	ie_X(a0),d1
	  bpl.s	  .PosX
	neg.w	d1
.PosX	cmp.w	d0,d1
	  bls.s	  .SkipX	;below threshold
	move.l	Acceleration(a5),d1
	muls.w	ie_X(a0),d1
	  bpl.s	  .SubDampX
	add.w	DampingConstant(a5),d1
	  bra.s	  .DampX
.SubDampX	sub.w	DampingConstant(a5),d1
.DampX	move.w	d1,ie_X(a0)
.SkipX	move.w	ie_Y(a0),d1
	  bpl.s	  .PosY
	neg.w	d1
.PosY	cmp.w	d0,d1
	  bls.s	  .EndAccel
	move.l	Acceleration(a5),d1
	muls.w	ie_Y(a0),d1
	  bpl.s	  .SubDampY
	add.w	DampingConstant(a5),d1
	  bra.s	  .DampY
.SubDampY	sub.w	DampingConstant(a5),d1
.DampY	move.w	d1,ie_Y(a0)
.EndAccel

.end_event	popm.l	a2/a6
	rts

;***************************************************************************
Time_Outs:	;*»»  Check for time-outs (mouse and screen blanking)
; Takes d1=TimeStamp seconds; a5=GlobalPtr
; scratches d0, d1, a0, a1
	push.l	a6
	move.l	IntBase(a5),a6

	;*»»  Check if window has changed with blanked pointer
	btst	#STB_MBlanked,Status(a5)
	  beq.s	  .EndMBW	; (Skip if not blanked)
	move.l	MBlankWindow(a5),d0
	cmp.l	ib_ActiveWindow(a6),d0
	  beq.s	  .EndMBW
	bset	#STB_DoMBlank,Status(a5)
	bsr	SignalMouseBlank	; Blank mouse for this new window
.EndMBW:

	;*»»  Check if mouse should be timed out
	tst.l	MouseBlank(a5)
	  ble.s	  .end_MBlank ; If user-mouse-blank-time=0, don't blank
	move.l	d1,d0
	sub.l	MouseBlank(a5),d0	; this_time - user_pause
	tst.l	MouseTime(a5)
	bne.s	.else_MBlnk	; If last-used mouse time is zero, then
	  move.l	  d1,MouseTime(a5)	; reset last-used mouse time
	bra.s	.end_MBlank	; else
.else_MBlnk	  sub.l	  MouseTime(a5),d0	; sub last mouse move time
	  blo.s	  .end_MBlank	; If this_time - last_move > user_pause
	    move.l	    d0,MouseTime(a5)
	    bset	    #STB_DoMBlank,Status(a5)
	    bsr	    SignalMouseBlank
.end_MBlank	;endif

	tst.l	ScreenBlank(a5)
	  ble.s	  .end_SBlank ; If screen-blank time-out=0, don't blank
	move.l	d1,d0
	sub.l	ScreenBlank(a5),d0	; this_time - user_pause
	tst.l	ScreenTime(a5)
	bne.s	.else_SBlnk	; If last-used screen time is zero then
	  move.l	  d1,ScreenTime(a5)	; reset last-used screen time
	bra.s	.end_SBlank	;else
.else_SBlnk	  sub.l	  ScreenTime(a5),d0
	  blo.s	  .end_SBlank
	    bset	    #STB_SBlanked,Status(a5)
	  bne.s	  .end_SBlank	; Don't blank if already blanked
	    BlankScreen
.end_SBlank	;endif

	pop.l	a6
	rts

;****************************************************************************

SignalMouseBlank:	; Takes a5=GlobalPtr; Returns, Trashes nil
	push.l	d0
	move.l	MouseBlankSig(a5),d0
	bsr.s	SignalTask
	pop.l	d0
	rts

SignalTask:		; Takes a5=GlobalPtr; d0=signal; Trashes nil
	pushm.l	d0-d1/a0-a1/a6
	move.l	Task(a5),a1
	move.l	Execbase,a6
	just	Signal
	popm.l	d0-d1/a0-a1/a6
	rts


;****************************************************************************