;QMouse.s
;Written by Dan Babcock

;Note: This source was assembled with Macro68. If you don't have Macro68
;(shame) it may be easily modified for other assemblers.

	exeobj
	objfile	'c:QMouse'
	multipass

	IFND	_LVOSystemTagList
_LVOSystemTagList	equ	-$25E
	ENDC

SCREENLISTSIZE	equ	32	;maximum number of screens to keep track of
POINTERLISTSIZE	equ	32
HANDLERPRI	equ	60	;priority of input.device handler
MaxCopperSize	equ	500

SysBackground	equ	$e	;Offset into gb_copinit copper list -- 2.04 only!
DefColor	equ	$fff	;white

	move.l	4,a6
	cmp.w	#37,LIB_VERSION(a6)
	bhs.s	GoodVersion

;Print an error message - the user doesn't have 2.0
	lea	DosName(pc),a1
	SYS	OldOpenLibrary
	tst.l	d0
	beq.s	ErrEnd
	move.l	d0,a6
	SYS	Output
	move.l	d0,d1
	moveq	#EndSorry-Sorry,d3
	lea	Sorry(pc),a0
	move.l	a0,d2
	SYS	Write
	move.l	a6,a1
	move.l	4,a6
	SYS	CloseLibrary
ErrEnd:	moveq	#100,d0
	rts

	dc.b	'$VER: QMouse 2.10 (14.6.92)',$a,0
Sorry:	dc.b	'Sorry, this program requires Kickstart 2.0!',$a,0
EndSorry:	even

GoodVersion:

;Allocate global data structure
	move.l	#Data_Sizeof,d0
	move.l	#MEMF_CLEAR,d1
	SYS	AllocMem
	tst.l	d0
	beq	ErrEnd
	move.l	d0,a5
	lea	GlobalPtr(pc),a0
	move.l	a5,(a0)

;Allocate chip RAM data.
	move.l	#CData_Sizeof,d0
	move.l	#MEMF_CLEAR+MEMF_CHIP,d1
	SYS	AllocMem
	move.l	d0,ChipData(a5)
	beq	CleanUp

;Initialize static data
	move.b	#HANDLERPRI,InputInterrupt+LN_PRI(a5)
	move.l	a5,InputInterrupt+IS_DATA(a5)
	moveq	#1,d0
	move.l	d0,ClickCount(a1)

;Open libraries
	lea	LibTable(pc),a2
	move.l	a2,a3
	moveq	#NumLibs-1,d2
.OLibLoop:
	move.w	(a2)+,a1
	add.l	a3,a1
	SYS	OldOpenLibrary
	move.w	(a2)+,a0
	add.l	a5,a0
	move.l	d0,(a0)
	beq	CleanUp
	dbra	d2,.OLibLoop

;Parse command line
	lea	Template(pc),a0
	move.l	a0,d1
	lea	Options(a5),a0
	move.l	a0,d2
	moveq	#0,d3	;no custom rdarg structure
	move.l	DosBase(a5),a6
	SYS	ReadArgs
	move.l	d0,ArgPtr(a5)
	beq	CleanUp
	move.l	4,a6

;Process QUIT option, if selected.
	tst.l	QuitOption(a5)
	beq.s	.SkipQuit
	lea	ProcName(pc),a1
	SYS	FindTask
	tst.l	d0
	beq	CleanUp
	move.l	d0,a1
	move.l	TC_Userdata(a1),a0
	move.l	QuitSig(a0),d0
	SYS	Signal	;tell task to quit
	bsr	CleanUp
	bra	.Exit
.SkipQuit:

;If already installed, kill the existing process.
	lea	ProcName(pc),a1
	SYS	FindTask
	tst.l	d0
	beq.s	.NotInstalled
	move.l	d0,a1
	move.l	TC_Userdata(a1),a0
	move.l	QuitSig(a0),d0
	SYS	Signal	;tell task to quit

	moveq	#26,d2
.Kill:	subq.l	#1,d2
	beq	.Exit
	move.l	DosBase(a5),a6
	moveq	#2,d1
	SYS	Delay
	lea	ProcName(pc),a1
	move.l	4,a6
	SYS	FindTask
	tst.l	d0
	bne	.Kill
.NotInstalled:

;Get stack for command.
	move.l	DosBase(a5),a6
	SYS	Cli
	move.l	d0,a0
	move.l	cli_DefaultStack(a0),d0
	lsl.l	#2,d0
	lea	CmdStackSize(pc),a0
	move.l	d0,(a0)
	move.l	4,a6

;Copy resident part of code into allocated memory
	lea	StartAllocCode(pc),a0	;source
	lea	ResidentCode(a5),a1	;destination
	move.l	#ResidentCodeSize,d0	;size
	SYS	CopyMem
	SYS	CacheClearU	;for 020/030/040

;Start a process
	move.l	DosBase(a5),a6
	lea	ResidentCode(a5),a0
	lea	EntryPoint(pc),a1
	move.l	a0,(a1)
	lea	ProcName-StartAllocCode+ResidentCode(a5),a0
	lea	ProcNamePtr(pc),a1
	move.l	a0,(a1)
	lea	NewProcTags(pc),a0
	move.l	a0,d1
	SYS	CreateNewProc

;Exit back to CLI
.Exit:	moveq	#0,d0
	rts

***************************************************************************
* The following code is copied into allocated memory.                     *
***************************************************************************

StartAllocCode:
	move.l	4,a6
	move.l	GlobalPtr(pc),a5
	lea	IntRoutine(pc),a0
	move.l	a0,InputInterrupt+IS_CODE(a5)

;Initialize damping constant.
	move.l	AccelOption(a5),d0
	beq.s	.NoDamp
	move.l	d0,a0
	move.l	(a0),d0
	subq.l	#1,d0
	move.l	ThreshOption(a5),d1
	beq.s	.DefThresh
	move.l	d1,a0
	move.l	(a0),d1
.DefThresh:
	addq.l	#1,d1
	mulu.w	d0,d1
	move.w	d1,DampingConstant(a5)
.NoDamp:

;Allocate signals
	moveq	#NumSigs-1,d2
	lea	Sigs(a5),a2
.SigLoop:	moveq	#-1,d0
	SYS	AllocSignal
	tst.l	d0
	bmi	CleanUp
	moveq	#0,d1
	bset	d0,d1
	move.l	d1,(a2)+
	dbra	d2,.SigLoop

;Initialize semaphore(s)
	lea	MySemaphore(a5),a0
	SYS	InitSemaphore

	move.l	ThisTask(a6),a0
	move.l	a0,Task(a5)
	move.l	a5,TC_Userdata(a0)

;Install no-flicker option if selected.
	tst.l	FlickerOption(a5)
	beq.s	.NoFlicker
	move.l	GraphBase(a5),a0
	move.l	gb_CopInit(a0),a0
	move.l	a0,OriginalCopInit(a5)
	move.l	ChipData(a5),a1
	lea	Copper(a1),a1
	move.l	a1,a2
	move.w	#MaxCopperSize/4-1,d0
.CLoop:	move.l	(a0)+,d1
	cmp.l	#$008A0000,d1
	beq.s	.CopEnd
	move.l	d1,(a1)+
	dbra	d0,.CLoop
	clr.l	FlickerOption(a5)
	bra.s	.NoFlicker
.CopEnd:	move.l	#$01800000,(a1)+
	move.l	#$1607FFFE,(a1)+
	move.w	#$0180,(a1)+
	move.l	#$008A0000,2(a1)
	move.l	a1,RealBackground(a5)
	move.l	a2,cop1lc+_custom
	move.l	GraphBase(a5),a0
	move.l	a2,gb_CopInit(a0)
	cmp.w	#color,SysBackground-2(a2)
	bne.s	.DefColor
	move.w	SysBackground(a2),(a1)
	bra.s	.NoFlicker
.DefColor:
	move.w	#DefColor,(a1)
.NoFlicker:

;Patch MANY system routines.
	lea	PatchTable(pc),a2
	move.l	a2,a3
.Patch:	move.w	(a2)+,d0	;get base
	beq.s	.EndPatch
	move.l	(a5,d0.w),a1	;library base to patch
	move.w	(a2)+,d0	;get offset to new routine
	lea	(a3,d0.w),a0
	move.l	a0,d0	;new routine address
	move.w	(a2)+,a0	;_LVO offset
	SYS	SetFunction
	move.w	(a2)+,d1
	move.l	d0,(a5,d1.w)
	bra	.Patch
.EndPatch:

;Create an IORequest structure
	SYS	CreateMsgPort
	move.l	d0,InputMsgPort(a5)
	beq	CleanUp
	move.l	d0,a0
	moveq	#IOSTD_SIZE,d0
	SYS	CreateIORequest
	move.l	d0,InputIORequest(a5)
	beq	CleanUp

;Perform NoClick, if desired.
	move.l	NoClickOption(a5),d2
	beq.s	.Click
	move.l	d2,a0
	move.l	(a0),d2
.Click:	moveq	#0,d3
	moveq	#3,d4
.ClickLoop:
	lea	TrackName(pc),a0	;device name
	move.l	d3,d0	;unit number
	move.l	InputIORequest(a5),a1	;IO request
	moveq	#0,d1	;flags
	SYS	OpenDevice
	tst.l	d0
	bne.s	.ECLoop
	move.l	InputIORequest(a5),a1	;IO request
	move.l	IO_UNIT(a1),a0
	btst	d3,d2
	beq.s	.SkipNoClick
	bchg	#TDPB_NOCLICK,TDU_PUBFLAGS(a0)
.SkipNoClick:
	tst.l	VerifyOption(a5)
	beq.s	.SkipVerify
	bchg	#1,TDU_PUBFLAGS(a0)
.SkipVerify:
	SYS	CloseDevice
.ECLoop:	addq.l	#1,d3
	dbra	d4,.ClickLoop

;Handle 'STAR' option.
	tst.l	StarOption(a5)
	beq.s	.NoStar
	move.l	DosBase(a5),a0
	move.l	dl_Root(a0),a0
	bchg	#0,rn_Flags(a0)
.NoStar:

;Open the input.device
	lea	InputName(pc),a0	;device name
	moveq	#0,d0	;unit number
	move.l	InputIORequest(a5),a1	;IO request
	moveq	#0,d1	;flags
	SYS	OpenDevice
	tst.l	d0
	bne	CleanUp

;Install input handler
	move.l	InputIORequest(a5),a1
	move.w	#IND_ADDHANDLER,IO_COMMAND(a1)
	lea	InputInterrupt(a5),a0
	move.l	a0,IO_DATA(a1)
	SYS	DoIO
	tst.l	d0
	bne	CleanUp
	st	HandlerInstalled(a5)

;Wait for signal(s)
Wait:
	lea	Sigs(a5),a0
	move.l	(a0)+,d0
	moveq	#NumSigs-2,d1
..	add.l	(a0)+,d0
	dbra	d1,..
	move.l	4,a6
	SYS	Wait
	move.l	d0,d1
	and.l	QuitSig(a5),d1
	bne.s	SafeCleanUp
	move.l	d0,d1
	and.l	CLISig(a5),d1
	bne.s	.Execute
	move.l	d0,d1
	and.l	WBenchToFrontSig(a5),d1
	bne.s	.WBenchToFront

;Perform ScreenToBack
	move.l	IntBase(a5),a6
	move.l	ScreenPtr(a5),d0
	beq	Wait
	clr.l	ScreenPtr(a5)
	move.l	d0,a0
	SYS	ScreenToBack
	bra	Wait
.WBenchToFront:
	move.l	IntBase(a5),a6
	SYS	WBenchToFront
	bra	Wait
.Execute:
;Execute the desired command
	move.l	DosBase(a5),a6

	move.l	CmdOption(a5),d1
	beq	Wait
	lea	.Tags(pc),a0
	move.l	a0,d2	;tags
	SYS	SystemTagList
	bra	Wait
.Tags:	dc.l	SYS_USERSHELL,0
	dc.l	NP_StackSize
CmdStackSize:
	dc.l	0
	dc.l	0	;end of tag list

SafeCleanUp:
;Make sure the SetFunction'ed vectors are still intact.
	lea	PatchTable(pc),a0
	move.l	a0,a2
.Safe:	move.w	(a0)+,d0
	beq.s	CleanUp
	move.l	(a5,d0.w),a1	;library base
	move.w	(a0)+,d0
	lea	(a2,d0.w),a3	;address of new routine
	move.w	(a0)+,d0
	cmp.l	2(a1,d0.w),a3
	bne	Wait
	addq.l	#2,a0
	bra	.Safe

LibTable:	dc.w	DosName-LibTable
	dc.w	DosBase
	dc.w	IntName-LibTable
	dc.w	IntBase
	dc.w	LayersName-LibTable
	dc.w	LayersBase
	dc.w	GraphName-LibTable
	dc.w	GraphBase
EndLibTable:

NumLibs	equ	(EndLibTable-LibTable)/4

CleanUp:

;Enter with global ptr in A5

	move.l	4,a6

;Remove input handler, if installed.
	tst.b	HandlerInstalled(a5)
	beq.s	.SkipHand
	move.l	InputIORequest(a5),a1
	move.w	#IND_REMHANDLER,IO_COMMAND(a1)
	lea	InputInterrupt(a5),a0
	move.l	a0,IO_DATA(a1)
	SYS	DoIO
.SkipHand:

	tst.l	OldCloseScreen(a5)
	beq.s	.SkipRestore

	bsr	RestoreMouse
	bsr	RestoreScreen

;Restore original vectors.

	lea	PatchTable(pc),a2
.RestoreVecs:
	move.w	(a2),d0
	beq.s	.SkipRestore
	addq.l	#4,a2	;skip new routine info
	move.l	(a5,d0.w),a1	;get library base
	move.w	(a2)+,a0	;get _LVO
	move.w	(a2)+,d0
	move.l	(a5,d0.w),d0	;old routine
	SYS	SetFunction
	bra	.RestoreVecs
.SkipRestore:

;Delay for 1/10 second to ensure that no-one is using the patch code.
	move.l	DosBase(a5),d0
	beq.s	.SkipFreeArg
	move.l	d0,a6
	moveq	#5,d1
	SYS	Delay

;Free argument structure
	move.l	ArgPtr(a5),d1
	beq.s	.SkipFreeArg
	SYS	FreeArgs
.SkipFreeArg:

	move.l	4,a6

;Free signals
	lea	Sigs(a5),a2
	moveq	#NumSigs-1,d2
.SigLoop:	move.l	(a2)+,d1
	beq.s	.NextSig
	moveq	#-1,d0
.BitLoop:	addq.l	#1,d0
	btst	d0,d1
	beq	.BitLoop
	SYS	FreeSignal
.NextSig:	dbra	d2,.SigLoop

;Remove flicker option, if selected.
	tst.l	FlickerOption(a5)
	beq.s	.SkipFlicker
	move.l	OriginalCopInit(a5),a0
	move.l	GraphBase(a5),a2
	move.l	gb_CopInit(a2),a1
.CLoop:	move.l	(a1)+,(a0)+
	cmp.l	#$008A0000,(a0)
	bne	.CLoop
	move.l	OriginalCopInit(a5),gb_CopInit(a2)
	move.l	OriginalCopInit(a5),cop1lc+_custom
.SkipFlicker:

;Close libraries
	lea	LibTable+2(pc),a2
	moveq	#NumLibs-1,d2
.CLibLoop:
	move.w	(a2),a0
	add.l	a5,a0
	tst.l	(a0)
	beq.s	.SkipClose
	move.l	(a0),a1
	SYS	CloseLibrary
.SkipClose:
	addq.l	#4,a2
	dbra	d2,.CLibLoop

;Close input.device
	move.l	InputIORequest(a5),d0
	beq.s	.SkipCloseDev
	move.l	d0,a1
	tst.b	IO_ERROR(a1)	;was there an error?
	bne.s	.SkipCloseDev	;yes, don't close
	SYS	CloseDevice
.SkipCloseDev:

;Delete IORequest structure
	move.l	InputIORequest(a5),a0
	SYS	DeleteIORequest
	move.l	InputMsgPort(a5),a0
	SYS	DeleteMsgPort

;Free chip data.
	move.l	ChipData(a5),d0
	beq.s	.SkipChipData
	move.l	d0,a1
	move.l	#CData_Sizeof,d0
	SYS	FreeMem
.SkipChipData:

;Deallocate global structure. Note that this code is (sometimes) executed
;within the memory that we are freeing, so we are careful to terminate with
;a JMP.
	move.l	a5,a1
	move.l	#Data_Sizeof,d0
	jmp	_LVOFreeMem(a6)

;Semaphore routines

GetSemaphore:
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	MySemaphore(a0),a0
	move.l	4,a6
	SYS	ObtainSemaphore
	movem.l	(sp)+,a0/a6
	rts
FreeSemaphore:
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	MySemaphore(a0),a0
	move.l	4,a6
	SYS	ReleaseSemaphore
	movem.l	(sp)+,a0/a6
	rts

;****************** SetFunction'ed routines ***************

patch	macro
	dc.w	\1
	dc.w	New\2-PatchTable
	dc.w	_LVO\2
	dc.w	Old\2
	endm

PatchTable:
	patch	IntBase,CloseScreen
	patch	IntBase,CloseWindow
	patch	IntBase,OpenScreen
	patch	IntBase,ScreenToFront
	patch	IntBase,ScreenToBack
	patch	IntBase,OpenScreenTagList
	patch	IntBase,WBenchToFront
	patch	IntBase,WBenchToBack
	patch	IntBase,SetPointer
	patch	IntBase,ClearPointer
	patch	IntBase,DisplayBeep
	patch	GraphBase,LoadView
	dc.w	0

NewDisplayBeep:
	move.l	GlobalPtr(pc),a1
	tst.l	BeepOption(a1)
	bne.s	.SkipBeep
	move.l	OldDisplayBeep(a1),-(sp)
.SkipBeep:
	rts

NewSetPointer:
	bsr	GetSemaphore
	movem.l	d4-d5/a2-a3/a5,-(sp)
	move.l	GlobalPtr(pc),a5
	lea	PointerList(a5),a2

;Find if window is already in table and find empty spot.
	moveq	#POINTERLISTSIZE-1,d4
	sub.l	a3,a3
.WinSearch:
	move.l	pt_Window(a2),d5
	bne.s	.SkipSave
	move.l	a2,a3
.SkipSave:
	cmp.l	d5,a0
	beq.s	.Found
	lea	pt_Sizeof(a2),a2
	dbra	d4,.WinSearch
	move.l	a3,a2
	move.l	a2,d4
	beq.s	.End	;no room in table, so forget it
.Found:	movem.l	d0-d3/a0-a1,(a2)
.End:	cmp.l	MBlankWindow(a5),a0
	beq.s	.End1
.SkipMBlank:
	movem.l	(sp)+,d4-d5/a2-a3/a5
	subq.l	#8,sp
	move.l	a0,(sp)
	move.l	GlobalPtr(pc),a0
	move.l	OldSetPointer(a0),4(sp)
	move.l	(sp)+,a0
	bsr	FreeSemaphore
	rts
.End1:	movem.l	(sp)+,d4-d5/a2-a3/a5
	bsr	FreeSemaphore
	rts

NewClearPointer:
	bsr	GetSemaphore
	move.l	GlobalPtr(pc),a1
	lea	PointerList(a1),a1
	moveq	#POINTERLISTSIZE-1,d0
.ClearLoop:
	cmp.l	pt_Window(a1),a0
	beq.s	.Found
	lea	pt_Sizeof(a1),a1
	dbra	d0,.ClearLoop
	bra.s	.End
.Found:	clr.l	pt_Window(a1)
.End:	move.l	GlobalPtr(pc),a1
	cmp.l	MBlankWindow(a1),a0
	beq.s	.End1
.SkipMBlank:
	move.l	OldClearPointer(a1),-(sp)
.End1:	bsr	FreeSemaphore
	rts

NewLoadView:
	pea	.NewLoad(pc)
	move.l	GlobalPtr(pc),a0
	move.l	OldLoadView(a0),-(sp)
	rts
.NewLoad:
	move.l	GlobalPtr(pc),a0
	tst.l	FlickerOption(a0)
	beq.s	.End
	move.l	RealBackground(a0),a1
	move.l	GraphBase(a0),a0
	move.l	gb_copinit(a0),a0
	move.w	SysBackground(a0),(a1)
.End:	rts

NewCloseScreen:
	bsr	GetSemaphore
	move.l	GlobalPtr(pc),a1
	lea	ScreenList(a1),a1
	moveq	#SCREENLISTSIZE-1,d0
.Loop:	cmp.l	(a1),a0
	beq.s	.Match
	addq.l	#8,a1
	dbra	d0,.Loop
.End:	move.l	GlobalPtr(pc),a1
	pea	RestoreWindow(pc)
	move.l	OldCloseScreen(a1),-(sp)
	bsr	FreeSemaphore
	rts
.Match:	clr.l	(a1)
	bra	.End

NewCloseWindow:
	bsr	GetSemaphore
	push	a0
	SYS	ClearPointer
	pop	a0
	move.l	GlobalPtr(pc),a1
	cmp.l	MBlankWindow(a1),a0
	bne.s	.SkipClear
	clr.l	MBlankWindow(a1)
.SkipClear:
	lea	ScreenList+4(a1),a1
	moveq	#SCREENLISTSIZE-1,d0
.Loop:	cmp.l	(a1),a0
	beq.s	.Match
	addq.l	#8,a1
	dbra	d0,.Loop
.End:	move.l	GlobalPtr(pc),a1
	move.l	OldCloseWindow(a1),-(sp)
	bsr	FreeSemaphore
	rts
.Match:	clr.l	-4(a1)
	bra	.End

MyRemember:
	bsr	GetSemaphore
	bsr.s	RememberWindow
	move.l	a0,-(sp)
	move.l	GlobalPtr(pc),a0
	move.l	(a0,d0.w),d0
	move.l	(sp)+,a0
	move.l	d0,-(sp)
	bsr	FreeSemaphore
	rts

NewOpenScreen:
	move.w	#OldOpenScreen,d0
	bra	MyRemember
NewOpenScreenTagList:
	move.w	#OldOpenScreenTagList,d0
	bra	MyRemember
NewWBenchToFront:
	move.w	#OldWBenchToFront,d0
	bsr	MyRemember
	bra.s	RestoreWindow
NewWBenchToBack:
	move.w	#OldWBenchToBack,d0
	bsr	MyRemember
	bra.s	RestoreWindow
NewScreenToFront:
	move.w	#OldScreenToFront,d0
	bsr	MyRemember
	bra.s	RestoreWindow
NewScreenToBack:
	move.w	#OldScreenToBack,d0
	bsr	MyRemember
	bra.s	RestoreWindow

;****************** End of SetFunction'ed routines ***************

RememberWindow:

;This routine remembers the currently activated window on the current
;screen.

	movem.l	d0-d1/a0-a2/a4-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	IntBase(a5),a4
	move.l	4,a6
	SYS	Forbid
	move.l	ib_ActiveScreen(a4),a0
	move.l	ib_ActiveWindow(a4),a2
	SYS	Permit

;Sanity checks
	move.l	a0,d0
	beq.s	.End
	move.l	a2,d0
	beq.s	.End

	lea	ScreenList(a5),a1
	moveq	#SCREENLISTSIZE-1,d0
.ScLoop:	cmp.l	(a1),a0
	beq.s	.Match
	addq.l	#8,a1
	dbra	d0,.ScLoop

;Screen not found. Add to screen list.
	lea	ScreenList(a5),a1
	moveq	#SCREENLISTSIZE-1,d0
.AddLoop:	tst.l	(a1)
	beq.s	.Match
	addq.l	#8,a1
	dbra	d0,.AddLoop
	bra.s	.End	;table is full
.Match:
	move.l	a0,(a1)+	;screen
	move.l	a2,(a1)	;currently activated window
.End:	movem.l	(sp)+,d0-d1/a0-a2/a4-a6
	rts

RestoreWindow:

;This routine restores (re-activates) the previously activated window for
;this screen.

	bsr	GetSemaphore
	movem.l	d0-d1/a0-a1/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	ScreenList(a0),a1
	move.l	IntBase(a0),a6
	move.l	ib_FirstScreen(a6),a0
	move.l	a0,d0
	beq.s	.End	;sanity check
	moveq	#SCREENLISTSIZE-1,d0
.Loop:	cmp.l	(a1),a0
	beq.s	.Match
	addq.l	#8,a1
	dbra	d0,.Loop
	move.l	sc_FirstWindow(a0),a0	;default window
	move.l	a0,d0
	beq.s	.End	;sanity check
	bra.s	.SkipMatch
.Match:
	move.l	4(a1),a0
.SkipMatch:
	SYS	ActivateWindow
.End:	movem.l	(sp)+,d0-d1/a0-a1/a6
	bsr	FreeSemaphore
	rts

;***************************************************************************
;Start of input handler code
;***************************************************************************

;Get window associated with current mouse position.
;Returns Window in D0 and screen in D1.

GetWindow:
	movem.l	d2-d4/a0-a1/a4-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	LayersBase(a5),a6
	move.l	IntBase(a5),a4
	move.l	ib_FirstScreen(a4),d4
.ScLoop:	tst.l	d4
	beq.s	.ErrEnd
	move.l	d4,a0
	move.l	sc_NextScreen(a0),d4
	move.w	ib_MouseX(a4),d0
	move.w	ib_MouseY(a4),d1
	move.w	sc_ViewPort+vp_Modes(a0),d2
	move.w	d2,d3
	and.w	#V_LACE,d2	;interlace?
	bne.s	.Lace	;yes
	lsr.w	#1,d1
.Lace:	and.w	#V_HIRES,d3	;hires?
	bne.s	.Hires	;yes
	lsr.w	#1,d0
.Hires:	sub.w	sc_ViewPort+vp_DxOffset(a0),d0
	bmi.s	.ScLoop
	sub.w	sc_ViewPort+vp_DyOffset(a0),d1
	bmi.s	.ScLoop
	push	a0
	lea	sc_LayerInfo(a0),a0
	SYS	WhichLayer
	pop	d1
	tst.l	d0
	beq.s	.End
	move.l	d0,a0
	move.l	lr_Window(a0),d0
.End:	movem.l	(sp)+,d2-d4/a0-a1/a4-a6
	rts
.ErrEnd:	moveq	#0,d0
	moveq	#0,d1
	bra	.End

DoWindowToFront:

;Perform a WindowToFront on the window in D0.
;Enter with global ptr in A1.

	tst.l	d0
	beq.s	.End1
	movem.l	d0-d1/a0-a1/a6,-(sp)
	move.l	IntBase(a1),a6
	move.l	d0,a0
	move.l	wd_WLayer(a0),a1
	move.l	a1,d0
	beq.s	.End
	move.l	lr_ClipRect(a1),a1
	move.l	a1,d0
	beq.s	.End
	tst.l	(a1)
	beq.s	.End
	move.l	wd_Flags(a0),d0
	and.l	#WFLG_BACKDROP,d0
	bne.s	.End
	SYS	WindowToFront
.End:	movem.l	(sp)+,d0-d1/a0-a1/a6
.End1:	rts

BlankMouse:
	movem.l	d0-d3/a0-a1/a5-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	IntBase(a5),a6
	move.l	ib_ActiveWindow(a6),d0
	beq.s	.End
	cmp.l	MBlankWindow(a5),d0
	beq.s	.End
	move.l	d0,MBlankWindow(a5)
	move.l	d0,a0
	move.l	ChipData(a5),a1
;	lea	ZeroMouse(a1),a1	;not needed
	moveq	#1,d0	;height
	moveq	#16,d1	;width
	moveq	#0,d2	;xoffset
	moveq	#0,d3	;yoffset
	pea	.End(pc)
	move.l	OldSetPointer(a5),-(sp)
	rts
.End:	movem.l	(sp)+,d0-d3/a0-a1/a5-a6
	rts

RestoreMouse:
	movem.l	d0-d1/a0-a2/a5-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	IntBase(a5),a6
	move.l	MBlankWindow(a5),d0
	beq.s	.End

;Look for window in pointer table. If present, do a SetPointer to restore
;the custom pointer. Otherwise call ClearPointer.

	move.l	d0,a0
	clr.l	MBlankWindow(a5)
	lea	PointerList(a5),a1
	moveq	#POINTERLISTSIZE-1,d0
.WinLoop:	cmp.l	pt_Window(a1),a0
	beq.s	.Found
	lea	pt_Sizeof(a1),a1
	dbra	d0,.WinLoop
	pea	.End(pc)
	move.l	OldClearPointer(a5),-(sp)
	rts
.End:	movem.l	(sp)+,d0-d1/a0-a2/a5-a6
	rts
.Found:	move.l	a1,a2
	movem.l	(a2),d0-d3/a0-a1
	pea	.End(pc)
	move.l	OldSetPointer(a5),-(sp)
	rts

RestoreScreen:
	push	a5
	move.l	GlobalPtr(pc),a5
	bclr	#STB_SBlanked,Status(a5)
	beq.s	.End
	move.w	#DMAF_SETCLR+DMAF_COPPER+DMAF_RASTER,dmacon+_custom
.End:	pop	a5
	rts

SigTask:
;Enter with GlobalPtr in A1 and signal in D0.
	movem.l	d0-d1/a0-a1/a6,-(sp)
	move.l	Task(a1),a1
	move.l	4,a6
	SYS	Signal
	movem.l	(sp)+,d0-d1/a0-a1/a6
	rts

IntRoutine:
	movem.l	a2/a6,-(sp)
	move.l	IntBase(a1),a6
	bsr	GetSemaphore
	move.l	a0,-(sp)
	tst.l	FlickerOption(a1)
	beq.s	.SkipFlick
	move.l	GraphBase(a1),a0
	move.l	gb_copinit(a0),a0
	cmp.w	#color,Sysbackground-2(a0)
	bne.s	.DefColor
	move.w	SysBackground(a0),d0
	move.l	RealBackground(a1),a0
	move.w	d0,(a0)
	bra.s	.SkipColor
.DefColor:
	move.l	RealBackground(a1),a0
	move.w	#DefColor,(a0)
.SkipColor:
	move.l	(sp),a0
.SkipFlick:

;Perform SunMouse
	tst.l	SunMouseOption(a1)
	beq.s	.SkipSunMouse1
	cmp.b	#IECLASS_RAWMOUSE,ie_Class(a0)
	beq.s	.SkipSunMouse1
	bclr	#STB_SunMouse,Status(a1)
	beq.s	.SkipSunMouse1
	bsr	GetWindow
	movem.l	a0-a1,-(sp)
	move.l	d0,a0
	cmp.l	ib_ActiveWindow(a6),a0
	beq.s	.EndSun
	SYS	ActivateWindow
.EndSun:	movem.l	(sp)+,a0-a1
.SkipSunMouse1:

	move.l	MBlankWindow(a1),d0
	beq.s	.Loop
	cmp.l	ib_ActiveWindow(a6),d0
	beq.s	.EndMBW
	bsr	RestoreMouse
	bsr	BlankMouse
.EndMBW:

.Loop:	cmp.b	#IECLASS_TIMER,ie_Class(a0)
	beq	.NotKey

;Reset screen blank
	tst.l	SBlankOption(a1)
	beq.s	.SkipSRestore
	move.l	ie_TimeStamp(a0),ScreenTime(a1)
	bsr	RestoreScreen
.SkipSRestore:

	cmp.b	#IECLASS_RAWKEY,ie_Class(a0)
	bne	.NotKey
	tst.b	ie_Code+1(a0)
	bmi	.Next	;ignore up key codes

;Do Northgate keyboard mapping.
	tst.l	NorthGateOption(a1)
	beq.s	.SkipNorthgate
	lea	NorthgateTable(pc),a2
	move.w	ie_Code(a0),d0
	bclr	#7,d0
.NorthgateLoop:
	move.b	(a2),d1
	beq.s	.EndNorthgate
	addq.l	#3,a2
	cmp.b	d0,d1
	bne.s	.NorthgateLoop
	and.w	#$80,ie_Code(a0)
	move.b	-2(a2),d0
	or.b	d0,ie_Code+1(a0)
	move.b	-1(a2),d0
	bmi.s	.EndNorthgate
	bset	d0,ie_Qualifier+1(a0)
.EndNorthgate:
.SkipNorthgate:

	btst	#IEQUALIFIERB_LCOMMAND,ie_Qualifier+1(a0)
	bne	.DoLAmiga

;Blank mouse pointer
	tst.l	MBlankOption(a1)
	beq.s	.NoMBlank
	bsr	BlankMouse
.NoMBlank:
	bra	.Next

.DoLAmiga:
	cmp.w	#$37,ie_Code(a0)	;'m'?
	bne.s	.NotM

;We found an Amiga-M. Defuse and perform action.
	clr.b	ie_Class(a0)
	move.l	ib_FirstScreen(a6),a0
	move.l	a0,d0
	beq	.EndM	;sanity check
	move.l	a0,ScreenPtr(a1)
	move.l	ScreenToBackSig(a1),d0
	bsr	SigTask
.EndM:	bra	.End
.NotM:
	cmp.w	#$36,ie_Code(a0)	;'n'?
	bne.s	.NotN

;We found an Amiga-N. Defuse and perform action.
	clr.b	ie_Class(a0)
	move.l	WBenchToFrontSig(a1),d0
	bsr	SigTask
	bra	.End
.NotN:
	tst.l	CmdOption(a1)
	beq	.Next
	cmp.w	#$45,ie_Code(a0)	;ESC?
	bne	.Next
	clr.b	ie_Class(a0)
	move.l	CLISig(a1),d0
	bsr	SigTask
	bra	.End

.NotKey:
	move.w	ib_MouseX(a6),d0
	move.w	ib_MouseY(a6),d1
	cmp.b	#IECLASS_RAWMOUSE,ie_Class(a0)
	beq	.DoMouse
	cmp.w	CurrentX(a1),d0
	bne	.DoMouse
	cmp.w	CurrentY(a1),d1
	beq	.Next

.DoMouse:
	move.w	d0,CurrentX(a1)
	move.w	d1,CurrentY(a1)
	tst.l	MBlankOption(a1)
	beq.s	.NoRestore
	move.l	ie_TimeStamp(a0),MouseTime(a1)
	bsr	RestoreMouse
.NoRestore:

;Perform SunMouse
	tst.l	SunMouseOption(a1)
	beq.s	.SkipSunMouse
	move.w	ie_Qualifier(a0),d0
	and.w	#IEQUALIFIER_LEFTBUTTON+IEQUALIFIER_RBUTTON+IEQUALIFIER_MIDBUTTON,d0
	bne.s	.SkipSunMouse
	cmp.w	#IECODE_LBUTTON+IECODE_UP_PREFIX,ie_Code(a0)
	beq.s	.SkipSunMouse
	bset	#STB_SunMouse,Status(a1)
.SkipSunMouse:

;Perform click-to-back.
	tst.l	CTBOption(a1)
	beq.s	.NoCTB
	cmp.w	#IECODE_RBUTTON,ie_Code(a0)
	bne.s	.NoCTB
	move.w	ie_Qualifier(a0),d0
	btst	#IEQUALIFIERB_LEFTBUTTON,d0
	beq.s	.NoCTB

	movem.l	d2/a0-a1,-(sp)
	bsr	GetWindow
	tst.l	d0
	beq.s	.FlipScreen
	move.l	d0,a1
	move.l	wd_Flags(a1),d2
	and.l	#WFLG_BACKDROP,d2
	bne.s	.FlipScreen

;Is this the only window on this screen, except for backdrop windows?
	move.l	wd_WScreen(a1),a1
	move.l	sc_FirstWindow(a1),a1
.BackLoop:
	cmp.l	a1,d0
	beq.s	.EBackLoop
	move.l	wd_Flags(a1),d2
	and.l	#WFLG_BACKDROP,d2
	beq.s	.FlipWindow
.EBackLoop:
	move.l	(a1),a1
	move.l	a1,d2
	bne	.BackLoop
.FlipScreen:
	tst.l	d1
	beq.s	.ECTB
	move.l	GlobalPtr(pc),a1
	move.l	d1,ScreenPtr(a1)
	move.l	ScreenToBackSig(a1),d0
	bsr	SigTask
	bra.s	.ECTB
.FlipWindow:
	move.l	d0,a0
	SYS	WindowToBack

.ECTB:	movem.l	(sp)+,d2/a0-a1
	clr.b	ie_Class(a0)
	bra	.Next
.NoCTB:

;Perform click-to-front.
	tst.l	CTFOption(a1)
	beq	.NoCTF
	cmp.w	#IECODE_LBUTTON,ie_Code(a0)
	bne	.NoCTF
	bsr	GetWindow
	tst.l	d0
	beq	.NoCTF
	move.l	CTFOption(a1),a2
	move.l	(a2),d1
	cmp.l	#1,d1
	bhi.s	.MoreThanOne
	bsr	DoWindowToFront
	bra.s	.NoCTF
.MoreThanOne:
	cmp.l	ClickWindow(a1),d0
	beq.s	.SameWindow
	move.l	d0,ClickWindow(a1)
	moveq	#1,d0
	move.l	d0,ClickCount(a1)
.Time:	move.l	ie_TimeStamp+TV_SECS(a0),ClickTime+TV_SECS(a1)
	move.l	ie_TimeStamp+TV_MICRO(a0),ClickTime+TV_MICRO(a1)
	bra.s	.NoCTF
.SameWindow:

;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	d0
	movem.l	d2-d3/a0-a1,-(sp)
	move.l	ClickTime+TV_SECS(a1),d0
	move.l	ClickTime+TV_MICRO(a1),d1
	move.l	ie_TimeStamp+TV_SECS(a0),d2
	move.l	ie_TimeStamp+TV_MICRO(a0),d3
	SYS	DoubleClick
	movem.l	(sp)+,d2-d3/a0-a1
	tst.l	d0
	beq.s	.TimeExceeded
	addq.l	#1,ClickCount(a1)
	push	a0
	move.l	CTFOption(a1),a0
	move.l	(a0),d0
	pop	a0
	cmp.l	ClickCount(a1),d0
	bhi.s	.NeedMoreClicks
	clr.l	ClickCount(a1)
	pop	d0
	bsr	DoWindowToFront
	bra.s	.NoCTF
.TimeExceeded:
	moveq	#1,d0
	move.l	d0,ClickCount(a1)
.NeedMoreClicks:
	pop	d0
	bra	.Time
.NoCTF:

;Perform acceleration.
	tst.l	AccelOption(a1)
	beq.s	.Next

	moveq	#0,d0
	tst.l	ThreshOption(a1)
	beq.s	.T1
	move.l	ThreshOption(a1),a2
	move.l	(a2),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	AccelOption(a1),a2
	move.l	(a2),d1
	muls.w	ie_X(a0),d1
	bpl.s	.SubDampX
	add.w	DampingConstant(a1),d1
	bra.s	.DampX
.SubDampX:
	sub.w	DampingConstant(a1),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	AccelOption(a1),a2
	move.l	(a2),d1
	muls.w	ie_Y(a0),d1
	bpl.s	.SubDampY
	add.w	DampingConstant(a1),d1
	bra.s	.DampY
.SubDampY:
	sub.w	DampingConstant(a1),d1

.DampY:	move.w	d1,ie_Y(a0)
.EndAccel:

.Next:	move.l	a0,d1
	move.l	(a0),d0
	move.l	d0,a0
	bne	.Loop

;Check for time-outs (mouse and screen blanking)
	move.l	d1,a0
	tst.l	MBlankOption(a1)
	beq.s	.NoMTime
	move.l	MBlankOption(a1),a0
	move.l	(a0),d0	;get value in seconds
	move.l	d1,a0
	move.l	ie_TimeStamp(a0),d1
	tst.l	MouseTime(a1)
	beq.s	.InitMBlank
	sub.l	MouseTime(a1),d1
	cmp.l	d0,d1	;TimePassed ? UserSetting
	blo.s	.NoMTime
	bsr	BlankMouse
.NoMTime:
	tst.l	SBlankOption(a1)
	beq.s	.End
	btst	#STB_SBlanked,Status(a1)
	bne.s	.End
	push	a0
	move.l	SBlankOption(a1),a0
	move.l	(a0),d0	;get value in seconds
	pop	a0
	move.l	ie_TimeStamp(a0),d1
	tst.l	ScreenTime(a1)
	beq.s	.InitSBlank
	sub.l	ScreenTime(a1),d1
	cmp.l	d0,d1	;TimePassed ? UserSetting
	blo.s	.End
	move.w	#DMAF_COPPER+DMAF_RASTER,dmacon+_custom
	clr.w	color+_custom
	bset	#STB_SBlanked,Status(a1)

.End:	move.l	(sp)+,d0
	bsr	FreeSemaphore
	movem.l	(sp)+,a2/a6
	rts
.InitMBlank:
	move.l	ie_TimeStamp(a0),MouseTime(a1)
	bra	.NoMTime
.InitSBlank:
	move.l	ie_TimeStamp(a0),ScreenTime(a1)
	bra	.End

GlobalPtr:
	dc.l	0

NorthgateTable:
	dc.b	$6b,$4f,IEQUALIFIERB_LSHIFT
	dc.b	$6c,$4e,IEQUALIFIERB_LSHIFT
	dc.b	$6d,$4c,IEQUALIFIERB_LSHIFT
	dc.b	$6e,$4d,IEQUALIFIERB_LSHIFT
	dc.b	0

ProcName:	dc.b	'QMouse process',0
InputName:
	dc.b	'input.device',0
TrackName:
	dc.b	'trackdisk.device',0
	even

EndAllocCode:	;end of code copied into allocated memory
;***********************************************************************

NewProcTags:
	dc.l	NP_Entry
EntryPoint:
	dc.l	0
	dc.l	NP_Name
ProcNamePtr:
	dc.l	0
	dc.l	NP_Cli,-1
	dc.l	0	;end of tags

Template:	dc.b	'M=MBLANK/K/N,'
	dc.b	'S=SBLANK/K/N,'
	dc.b	'N=NOFLICKER/S,'
	dc.b	'CMD/K,'
	dc.b	'A=ACCELERATION/K/N,'
	dc.b	'T=THRESHOLD/K/N,'
	dc.b	'CTB=CLICKTOBACK/S,'
	dc.b	'CTF=CLICKTOFRONT/K/N,'
	dc.b	'SUNMOUSE/S,'
	dc.b	'NORTHGATE/S,'
	dc.b	'NOBEEP/S,'
	dc.b	'NOCLICK/K/N,'
	dc.b	'VERIFY/S,'
	dc.b	'STAR/S,'
	dc.b	'QUIT/S',0

IntName:	dc.b	'intuition.library',0
GraphName:
	dc.b	'graphics.library',0
DosName:	dc.b	'dos.library',0
LayersName:
	dc.b	'layers.library',0

;The ordering here is special: data registers first, then address
;registers, so that MOVEM may be used to save/restore the data.

	STRUCTURE	pt,0
	LONG	pt_Height
	LONG	pt_Width
	LONG	pt_XOffset
	LONG	pt_YOffset
	LONG	pt_Window
	LONG	pt_Pointer
	LABEL	pt_Sizeof

ResidentCodeSize	equ	EndAllocCode-StartAllocCode

	STRUCTURE	Data,0
	WORD	Nothing	;to make zero special
	LONG	Task
	LONG	InputMsgPort
	LONG	InputIORequest
	STRUCT	InputInterrupt,IS_SIZE
	LONG	IntBase
	LONG	GraphBase
	LONG	LayersBase
	STRUCT	ScreenList,SCREENLISTSIZE*8
	STRUCT	PointerList,POINTERLISTSIZE*pt_Sizeof
	LONG	OldCloseScreen
	LONG	OldCloseWindow
	LONG	OldScreenToFront
	LONG	OldScreenToBack
	LONG	OldOpenScreen
	LONG	OldOpenScreenTagList
	LONG	OldWBenchToFront
	LONG	OldWBenchToBack
	LONG	OldLoadView
	LONG	OldSetPointer
	LONG	OldClearPointer
	LONG	OldDisplayBeep
	LONG	DosBase
	LONG	ArgPtr
	STRUCT	ResidentCode,ResidentCodeSize
	LONG	MouseTime	;timeout for mouse blanking
	LONG	ScreenTime	;for screen blanking
	LONG	MBlankWindow
	WORD	DampingConstant
	STRUCT	MySemaphore,SS_SIZE
	LONG	ClickWindow
	STRUCT	ClickTime,TV_SIZE
	LONG	ClickCount
	LONG	Path
	LONG	ChipData
	LONG	RealBackground
	LONG	OriginalCopInit
	LONG	ScreenPtr
	WORD	CurrentX
	WORD	CurrentY

	LABEL	Sigs
	LONG	CLISig
	LONG	QuitSig
	LONG	ScreenToBackSig
	LONG	WBenchToFrontSig
	LABEL	EndSigs

	LABEL	Options
	LONG	MBlankOption
	LONG	SBlankOption
	LONG	FlickerOption
	LONG	CmdOption
	LONG	AccelOption
	LONG	ThreshOption
	LONG	CTBOption
	LONG	CTFOption
	LONG	SunMouseOption
	LONG	NorthGateOption
	LONG	BeepOption
	LONG	NoClickOption
	LONG	VerifyOption
	LONG	StarOption
	LONG	QuitOption

	BYTE	HandlerInstalled
	BYTE	Status	;various status bits (defined below)
	LABEL	Data_Sizeof

;Bit definitions for Status
	BITDEF	ST,SBlanked,0
	BITDEF	ST,SunMouse,1

NumSigs	equ	(EndSigs-Sigs)/4

	STRUCTURE	CData,0
	STRUCT	ZeroMouse,12
	STRUCT	Copper,MaxCopperSize+4*4
	LABEL	CData_Sizeof

	end
