*	PWkeys
*	1991 by Preben Nielsen
*
*
*HISTORY
*          Made with Hisoft V2.12
*
*  V1.0   20-Apr-91: This is first release. I made it over a year ago
*  V1.1   06-May-91: Corrected a bug that appeared when trying to activate
*                    next/previous window when none was active
*         07-May-91: Added 'Refresh Window', 'Minimize Window' and
*                    'Maximize Window'.
*         08-May-91: Added 'Sub Bitplane' and 'Add BitPlane' and
*                    'Toggle Input-Lock'.
*         09-May-91: Added screen-blanking to 'Toggle Input-Lock'.
*         10-May-91: Added mouse-acceleration and sunmouse.
*         14-May-91: Added automatic and persistent screen-blanking.
*         15-May-91: Added automatic and persistent mouse-blanking.
*  V2.0   18-May-91: Now I test for key-releases (saves time).
*         19-May-91: Help, I just found out that my "TellInputDevice"
*                    routine trashes memory-address 0 because it didn't
*                    do a "NewList" on its Message-port. It didn't cause
*                    any problems most of the time, but it has now been
*                    cured.

	OPT O+
	OPT O1+			; Tells when a branch could be optimised to short
;	OPT i+			; Tells when '#' is probably missing.

		incdir		"AsmInc:"
		include		"exec/exec_lib.i"
		include		"exec/io.i"
		include		"exec/memory.i"
		include		"exec/interrupts.i"
		include		"devices/input.i"
		include		"devices/inputevent.i"
		include		"libraries/dos.i"
		include		"libraries/dosextens.i"
		include		"libraries/dos_lib.i"
		include		"graphics/gfxbase.i"
		include		"graphics/clip.i"
		include		"graphics/layers_lib.i"
		include		"graphics/graphics_lib.i"
		include		"intuition/intuitionbase.i"
		include		"intuition/intuition_lib.i"
		include		"Workbench/startup.i"
		include		"Workbench/workbench.i"
		include		"Workbench/icon_lib.i"
		include		"hardware/custom.i"
		include		"hardware/dmabits.i"
		include		"PWkeys.i"

V_EHB		=$80

Prepare		MACRO
		IFC		'\1','Exec_Call'
		movea.l		4.W,A6
		ENDC
		IFC		'\1','Icon_Call'
		movea.l		IconBase(DB),A6
		ENDC
		IFC		'\1','Intuition_Call'
		movea.l		IntBase(DB),A6
		ENDC
		IFC		'\1','Gfx_Call'
		movea.l		GFXBase(DB),A6
		ENDC
		IFC		'\1','Dos_Call'
		movea.l		DosBase(DB),A6
		ENDC
		ENDM
CallLib		MACRO
		jsr		_LVO\1(A6)
		ENDM
Call		MACRO
		bsr		\1
		ENDM
CallS		MACRO
		bsr.S		\1
		ENDM
Push		MACRO
		movem.l		\1,-(SP)
		ENDM
Pop		MACRO
		movem.l		(SP)+,\1
		ENDM
PushS		MACRO
		move.l		\1,-(SP)
		ENDM
PopS		MACRO
		move.l		(SP)+,\1
		ENDM
rAPtr		MACRO		name
DefSiz		set		DefSiz+4
DefPtr		set		DefPtr-4
\1		=		DefPtr
		ENDM
rLong		MACRO		name
DefSiz		set		DefSiz+4
DefPtr		set		DefPtr-4
\1		=		DefPtr
		ENDM
rWord		MACRO		name
DefSiz		set		DefSiz+2
DefPtr		set		DefPtr-2
\1		=		DefPtr
		ENDM
rByte		MACRO		name
DefSiz		set		DefSiz+1
DefPtr		set		DefPtr-1
\1		=		DefPtr
		ENDM
rStorage	MACRO		name,size	; Define storage
DefSiz		set		DefSiz+\2
DefPtr		set		DefPtr-\2
\1		=		DefPtr
		ENDM
rEVEN		MACRO				; Word boundary
		IFNE		DefPtr&1
DefPtr		set		DefPtr-1
DefSiz		set		DefSiz+1
		ENDC
		ENDM
rStart		MACRO				; Define var section
DefPtr		set		0
DefSiz		set		0
		ENDM
rEnd		MACRO				; End var section
RelSize		=		DefSiz
		ENDM
rAlloc		MACRO				; Allocate vars
		link		DB,#-RelSize
		ENDM
rFree		MACRO				; Deallocate vars
		unlk		DB
		ENDM
rClear		MACRO				; Reset all vars
		movem.l		D0/DB,-(SP)
		move.w		#RelSize-1,D0
rClr.\@		clr.b		-(DB)
		dbf		D0,rClr.\@
		movem.l		(SP)+,D0/DB
		ENDM

ON_DISPLAY	MACRO
		move.w		#BITSET|DMAF_RASTER,$DFF000+dmacon
		ENDM

OFF_DISPLAY	MACRO
		move.w		#BITCLR|DMAF_RASTER,$DFF000+dmacon
		ENDM

TEST_DISPLAY	MACRO
		btst		#DMAB_RASTER-8,$DFF000+dmaconr
		ENDM

DB		EQUR		A4

		SECTION		PWkeys,CODE
InitProcess	rAlloc					; Allocate memory for variables
		rClear					; Clear the memory
		subq.l		#1,D0
		move.l		A0,CName(DB)
		move.l		D0,CLen(DB)
		Prepare		Exec_Call
		CallLib		Forbid
		lea		IHS+ihs_PortName(PC),A1
		CallLib		FindPort
		move.l		D0,HPort(DB)
		CallLib		Permit
		suba.l		A1,A1
		CallLib		FindTask		; Find us
		move.l		D0,PProcess(DB)
		movea.l		D0,A2
		tst.l		pr_CLI(A2)
		bne.S		CLIStart
WBStart		lea		pr_MsgPort(A2),A0
		CallLib		WaitPort		; Wait for a message
		lea		pr_MsgPort(A2),A0
		CallLib		GetMsg			; then get it
		move.l		D0,WBMsg(DB)		; save it for later reply
		bra.S		GetLibs
CLIStart	move.l		CName(DB),A0
		move.l		CLen(DB),D0
		clr.b		0(A0,D0.W)
GetLibs		lea		DosName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,DosBase(DB)
		beq		Error

		tst.l		HPort(DB)		; Install or Remove ?
		bne.S		LetsBegin
		tst.l		WBMsg(DB)
		beq.S		LetsBegin		; CLI or WB ?

		clr.l		CLen(DB)		; Signal that we haven't found args yet
		move.l		WBMsg(DB),A2
		move.l		sm_NumArgs(A2),D0	; Any args ?
		move.l		sm_ArgList(A2),A2
		subq.l		#1,D0
		blt.S		LetsBegin
		beq.S		FoundArg
		addq.w		#wa_SIZEOF,A2		; PROJECT !
FoundArg	move.l		wa_Lock(A2),D1
		move.l		wa_Name(A2),A2		; Name of icon
		beq.S		NoLock
		Prepare		Dos_Call
		CallLib		CurrentDir
NoLock		Prepare		Exec_Call
		lea		IconName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,IconBase(DB)
		beq.S		LetsBegin
		Prepare		Icon_Call
		move.l		A2,A0
		CallLib		GetDiskObject		; Get the icon
		move.l		D0,DiskObj(DB)
		beq.S		LetsBegin
		move.l		D0,A2
		move.l		do_ToolTypes(A2),A0
		lea		FileTxt(PC),A1
		CallLib		FindToolType		; Find 'FILE=????'
		move.l		D0,CName(DB)
		move.l		D0,CLen(DB)		; Signal that we have or haven't found args
LetsBegin	tst.l		HPort(DB)
		beq.S		DoInstall
DoRemove	moveq		#REMOVED,D7
		lea		IHS(PC),A0
		lea		PSEndIHS1(PC),A1
		lea		PSEndIHS2(PC),A2
		Call		RemoveHandler
		beq.S		ShowMsg
		moveq		#CANTREMOVE,D7
		bra.S		ShowMsg
DoInstall	moveq		#INSTALLED,D7
		lea		IHS(PC),A0
		lea		PSPrepIHS1(PC),A1
		lea		PSPrepIHS2(PC),A2
		Call		InstallHandler
		beq.S		ShowMsg
		moveq		#CANTINSTALL,D7
ShowMsg		move.l		D7,D0
		Call		CONMsg

Error
Exit		Prepare		Exec_Call
FreeDos		move.l		DosBase(DB),D0
		beq.S		ReplyWB
		move.l		D0,A1
		CallLib		CloseLibrary
ReplyWB		move.l		WBMsg(DB),D0
		beq.S		AllDone
		move.l		DiskObj(DB),D0
		beq.S		FreeIcon
		move.l		D0,A0
		Prepare		Icon_Call
		CallLib		FreeDiskObject
FreeIcon	Prepare		Exec_Call
		move.l		IconBase(DB),D0
		beq.S		WBEnd
		move.l		D0,A1
		CallLib		CloseLibrary
WBEnd		CallLib		Forbid			; We were started from WB
		move.l		WBMsg(DB),A1
		CallLib		ReplyMsg		; Reply WBMessage
AllDone		rFree
		moveq		#0,D0
		rts


FHandle		EQUR		D5
* Call: D0 = Msg-number
CONMsg		Push		D0-D7/A0-A6
		Prepare		Dos_Call
		move.l		D0,D4
		moveq		#0,D6
		CallLib		Output
		move.l		D0,FHandle
		bne.S		1$
		moveq		#1,D6
		lea		CONName(PC),A0
		move.l		A0,D1
		move.l		#MODE_OLDFILE,D2
		CallLib		Open
		move.l		D0,FHandle
		beq.S		2$
1$		moveq		#INFOMSG,D0
		Call		SendMsg
		move.l		D4,D0
		Call		SendMsg
		tst.l		D6
		beq.S		2$
		moveq		#127,D1
		CallLib		Delay
		move.l		FHandle,D1
		CallLib		Close
2$		Pop		D0-D7/A0-A6
		rts

* Call: D0 = Msg-number
SendMsg		neg.l		D0
		lsl.l		#1,D0
		lea		MsgTable(PC),A0
		add.w		0(A0,D0),A0
		move.l		A0,D2
		moveq		#-1,D3
1$		addq.l		#1,D3
		tst.b		(A0)+
		bne.S		1$
		move.l		FHandle,D1
		Prepare		Dos_Call
		CallLib		Write
		rts

INFOMSG		=0
INSTALLED	=-1
REMOVED		=-2
CANTINSTALL	=-3
CANTREMOVE	=-4

MsgText		MACRO
		dc.w		\1-MsgTable
		ENDM
MsgTable	MsgText		Msg
		MsgText		Msg1
		MsgText		Msg2
		MsgText		Msg3
		MsgText		Msg4

CONName		dc.b		'CON:100/60/330/63/PWkeys',0

Msg		PWkeys_CON_Msg
Msg1		dc.b		'has just been installed...',10,0
Msg2		dc.b		'has just been removed...',10,0
Msg3		dc.b		'Error: Cannot install handler',10,0
Msg4		dc.b		'Error: Cannot remove handler',10,0
		EVEN

rtsValue	EQUR		D7
* This is general-purpose inputhandler removal-routine
* It only needs an ihs with a port-name to remove the handler
* Call:   A0 = ihs
*	  A1 = first ihs-installation-routine or NULL
*	  A2 = second ihs-installation-routine or NULL
* Return: D0 = 0 means succes
RemoveHandler	Push		D1/rtsValue/A0-A3/A6
		moveq		#-1,rtsValue
		move.l		A2,A3
		move.l		A0,A2
		move.l		A1,D1
		beq.S		1$
		jsr		(A1)		; A0 = ihs
		beq.S		2$
		move.l		D0,A2
1$		move.l		A2,A0
		Prepare		Exec_Call
		moveq		#IND_REMHANDLER,D0
		Call		TellInputDevice
		move.l		D0,rtsValue
		bne.S		2$
		lea		ihs_Port(A2),A1
		CallLib		RemPort
		moveq		#0,D0
		bra.S		3$
2$		moveq		#-1,D0
3$		move.l		A3,D1
		beq.S		4$
		move.l		A2,A0
		jsr		(A3)		; A0 = ihs, D0 = 0 means succes
4$		move.l		rtsValue,D0
		Pop		D1/rtsValue/A0-A3/A6
		rts

* This is general-purpose inputhandler installation-routine
* It only needs an ihs with a port-name to install the handler
* Call:   A0 = ihs
*	  A1 = first ihs-installation-routine or NULL
*	  A2 = second ihs-installation-routine or NULL
* Return: D0 = 0 means succes
InstallHandler	Push		D1/rtsValue/A0-A3/A6
		moveq		#-1,rtsValue
		move.l		A2,A3
		move.l		A0,A2
		move.l		A1,D1
		beq.S		1$
		jsr		(A1)		; A0 = ihs
		beq.S		2$
		move.l		D0,A2
1$		move.l		A2,A0
		moveq		#IND_ADDHANDLER,D0
		Call		TellInputDevice
		move.l		D0,rtsValue
		bne.S		2$
		lea		ihs_Port(A2),A1
		lea		ihs_PortName(A2),A0
		move.l		A0,MP+LN_NAME(A1)		;MsgPort->mp_Node.ln_Name=Name;
		clr.b		MP+LN_PRI(A1)			;MsgPort->mp_Node.ln_Pri =0;
		move.b		#NT_MSGPORT,MP+LN_TYPE(A1)	;MsgPort->mp_Node.ln_Type=NT_MSGPORT;
		move.b		#PA_IGNORE,MP_FLAGS(A1)		;MsgPort->mp_Flags	 =PA_IGNORE;
		Prepare		Exec_Call
		CallLib		AddPort
		moveq		#0,D0
		bra.S		3$
2$		moveq		#-1,D0
3$		move.l		A3,D1
		beq.S		4$
		move.l		A2,A0
		jsr		(A3)		; A0 = ihs, D0 = 0 means succes
4$		move.l		rtsValue,D0
		Pop		D1/rtsValue/A0-A3/A6
		rts

* Open the input device. Set up the I/O block to add or remove the
* input handler, and send the request to the input device. Finally,
* close the device
* Call:   A0 = ihs
*	  D0 = Function to perform (IND_ADDHANDLER/IND_REMHANDLER)
* Return: D0 = 0 means succes
TellInputDevice	Push		D1-D2/rtsValue/A0-A3/A6
		Prepare		Exec_Call
		moveq		#-1,rtsValue
		move.l		D0,D2
		move.l		A0,A2
		lea		IReq(DB),A0
		moveq		#IOSTD_SIZE,D0
		Call		MemClear
		lea		IPort(DB),A0
		moveq		#MP_SIZE,D0
		Call		MemClear
		move.l		A0,A3
		move.b		#NT_MSGPORT,MP+LN_TYPE(A3)	; mp_Node.ln_Type=NT_MSGPORT;
		move.b		#PA_SIGNAL,MP_FLAGS(A3)		; mp_Flags	=PA_SIGNAL;
		moveq		#-1,D0
		CallLib		AllocSignal
		move.b		D0,MP_SIGBIT(A3)		; mp_SigBit	=MPSigBit;
		bmi.S		2$
		suba.l		A1,A1
		CallLib		FindTask
		move.l		D0,MP_SIGTASK(A3)		; mp_SigTask 	=FindTask(0);
		lea		MP_MSGLIST(A3),A0
		NEWLIST		A0
		lea		IReq(DB),A1
		move.l		A3,IO+MN_REPLYPORT(A1)		; ExtReq->io_Message.mn_ReplyPort   =taskReplyPort;
		move.b		#NT_MESSAGE,IO+MN+LN_TYPE(A1)	; ExtReq->io_Message.mn_Node.ln_Type=NT_MESSAGE;
		lea		InputName(PC),A0		; input.device
		moveq		#0,D0				; unit#
		moveq		#0,D1				; flags
		CallLib		OpenDevice
		tst.w		D0				; flag: error if > 0
		bne.S		1$
		lea		IReq(DB),A1
		move.w		D2,IO_COMMAND(A1)
		lea		ihs_Interrupt(A2),A0
		move.l		A0,IO_DATA(A1)
		CallLib		DoIO
		move.l		D0,rtsValue
		lea		IReq(DB),A1
		CallLib		CloseDevice
1$		move.b		MP_SIGBIT(A3),D0
		CallLib		FreeSignal
2$		move.l		rtsValue,D0
		Pop		D1-D2/rtsValue/A0-A3/A6
		rts

* Call: A0    = Memory area
*	D0:16 = Count
MemClear	Push		D0-D1/A0
		moveq		#0,D1
		bra.S		2$
1$		move.b		D1,(A0)+
2$		dbf		D0,1$
		Pop		D0-D1/A0
		rts

* Call: A0   = Source
*	A1   = Destination
*	D0:16= Count
MemCopy		Push		D0/A0-A1
		bra.S		2$
1$		move.b		(A0)+,(A1)+
2$		dbf		D0,1$
		Pop		D0/A0-A1
		rts

* Each handler should have such a pair of installation-routine
* The first one is passed to InstallHandler in A1 and it
* is called immediately when entering InstallHandler
* The second one is passed to InstallHandler in A2 and it
* is called after attempt to install handler and message-port
* -----------------------------------------------------------------
* Call:   A0 = ihs
* Return: D0 has to point to ihs to be used when installation proceeds
*	  If D0 = 0 then installation is aborted
PSPrepIHS1	Push		D1-D2/A0-A3/A6
		moveq		#0,D2
		Call		GetKeys
		beq		2$
		Prepare		Exec_Call
		lea		Entry(PC),A0
		CallLib		AllocEntry
		move.l		D0,MEntry(DB)
		bmi.S		2$			; Is bit 31 set ?
		move.l		D0,A3
		move.l		ML_ME+0*ME_SIZE(A3),A2
		lea		IHS(PC),A0
		move.l		A2,A1
		move.w		#HandlerSize,D0
		Call		MemCopy			; Copy handler
		lea		ChipData(PC),A0
		move.l		ML_ME+1*ME_SIZE(A3),A1
		move.w		#ChipSize,D0
		Call		MemCopy			; Copy CHIP-data
		move.l		A1,NewCopper-IHS(A2)
		lea		MySprite-ChipData(A1),A1
		move.l		A1,NewSprite-IHS(A2)
		move.b		PWkPri(PC),ihs_Interrupt+LN_PRI(A2)	; HandlerBlock.HInterrupt.is_Node.ln_Pri = PRI;
		lea		HandlerCode-IHS(A2),A0
		move.l		A0,ihs_Interrupt+IS_CODE(A2)		; HandlerBlock.HInterrupt.is_Code = Handler
		lea		PWkVersionID-IHS(A2),A0
		move.l		A0,ihs_Interrupt+IS_DATA(A2)		; HandlerBlock.HInterrupt.is_Data = KeyDefines;
		move.w		#HStartFlags,ihs_Flags(A2)
		move.l		A3,ihs_MemEntry(A2)
		lea		LayerName(PC),A1	; Libraries won't get closed
		CallLib		OldOpenLibrary
		move.l		D0,LAYERbase-IHS(A2)
		beq.S		2$
		lea		GfxName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,GFXbase-IHS(A2)
		beq.S		2$
		lea		IntName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,INTUIbase-IHS(A2)
		beq.S		2$
		move.l		A2,D2
2$		move.l		D2,D0
		Pop		D1-D2/A0-A3/A6
		rts
* Call:   A0 = ihs
*         D0 = 0 means everything went perfect
*             -1 means something went wrong during installation
PSPrepIHS2	Push		D0-D1/A0-A1/A6
		tst.l		D0
		beq.S		1$
		move.l		MEntry(DB),D0
		bmi.S		1$		; Is bit 31 set ?
		move.l		D0,A0
		Prepare		Exec_Call
		CallLib		FreeEntry
1$		Pop		D0-D1/A0-A1/A6
		rts

* Each handler should have such a pair of ending-routine
* The first one is passed to RemoveHandler in A1 and it
* is called immediately when entering RemoveHandler
* The second one is passed to RemoveHandler in A2 and it
* is called after attempt to remove handler and message-port
* -----------------------------------------------------------------
* Call:   A0 = ihs
* Return: D0 has to point to ihs to be used when removal proceeds
*	  If D0 = 0 then removal is aborted
PSEndIHS1	Push		D1-D2/A0-A1/A6
		Prepare		Exec_Call
		CallLib		Forbid
		lea		ihs_PortName(A0),A1
		CallLib		FindPort
		move.l		D0,D2
		CallLib		Permit
		move.l		D2,D0			; Does Forbid/Permit destroy scratch-registers ?
		Pop		D1-D2/A0-A1/A6
		rts
* Call:   A0 = ihs
*         D0 = 0 means everything went perfect
*             -1 means something went wrong during removal
PSEndIHS2	Push		D0-D1/A0-A1/A6
		tst.l		D0
		bmi.S		1$
		move.l		ihs_MemEntry(A0),A0
		Prepare		Exec_Call
		CallLib		FreeEntry
1$		Pop		D0-D1/A0-A1/A6
		rts

* Tries to read hotkeys from file (if any is specified)
* Return: D0 = 0 means failure
GetKeys		Push		D1-D7/A0-A6
		moveq		#1,rtsValue
		tst.l		CLen(DB)
		beq.S		2$
		moveq		#0,rtsValue
		Prepare		Dos_Call
		move.l		CName(DB),D1
		move.l		#MODE_OLDFILE,D2
		CallLib		Open
		move.l		D0,D4
		beq.S		2$
		move.l		D0,D1
		lea		TBuf(DB),A0
		move.l		A0,D2
		move.l		#pw_SIZE,D3
		CallLib		Read
		cmp.l		D0,D3
		bne.S		1$
		lea		TBuf(DB),A0
		movem.l		pw_ID(A0),D0-D1
		movem.l		PWkVersionID(PC),D2-D3
		cmp.l		D0,D2
		bne.S		1$
		cmp.l		D1,D3
		bne.S		1$
		lea		PWkVersionID(PC),A1
		move.l		#pw_SIZE,D0
		Call		MemCopy
		moveq		#1,rtsValue
1$		move.l		D4,D1
		CallLib		Close
2$		move.l		rtsValue,D0
		Pop		D1-D7/A0-A6
		rts

* »»»»»»»»»»»»»»»»»»»»»» Data-definition start «««««««««««««««««««««««
 rStart
 rLong		CLen
 rAPtr		CName
 rAPtr		PProcess
 rAPtr		WBMsg
 rAPtr		DosBase
 rAPtr		IconBase
 rAPtr		DiskObj
 rAPtr		HPort
 rAPtr		MEntry
 rStorage	IReq,IOSTD_SIZE
 rStorage	IPort,MP_SIZE
 rStorage	TBuf,pw_SIZE
 rEnd

DosName		dc.b		'dos.library',0
LayerName	dc.b		'layers.library',0
GfxName		dc.b		'graphics.library',0
IntName		dc.b		'intuition.library',0
IconName	dc.b		'icon.library',0
InputName	dc.b		'input.device',0
FileTxt		dc.b		'FILE',0
		EVEN

Entry		ds.b		LN_SIZE
		dc.w		2
		dc.l		MEMF_CLEAR|MEMF_PUBLIC
		dc.l		HandlerSize
		dc.l		MEMF_CLEAR|MEMF_CHIP
		dc.l		ChipSize

* »»»»»»»»»»»»»»»»»»»»»» Data-definition end «««««««««««««««««««««««««

* »»»»»»»»»»»»»»»»»»»»»» Input-handler start «««««««««««««««««««««««««

HDisabled	=0
HNoExtRemoval	=1
HInputDisabled	=2
HAccelMouse	=3
HSunMouse	=4
HScreenBlanked	=5
HMouseBlanked	=6

HStartFlags	=0

* This is the handler-block
IHS		ihs_Start
* Local variables
Chain		dc.l		0
OldSprite	dc.l		0
NewSprite	dc.l		0
NewCopper	dc.l		0
INTUIbase	dc.l		0
GFXbase		dc.l		0
LAYERbase	dc.l		0

* This is the beginning of the part coming from a file
PWkVersionID	dc.l		'PWKF'
PWkVersionNum	dc.w		PWkeysVersion,PWkeysRevision
PWkPri		dc.b		PWkeysPri,0
PWkMouseAccel	dc.b		PWkeysMSpeed
PWkMouseThresh	dc.b		PWkeysMThresh
PWkMTimeout	dc.l		PWkeysMTimeout
PWkSTimeout	dc.l		PWkeysSTimeout
PWkMWaitTime	dc.l		0
PWkSWaitTime	dc.l		0
PWkHotKeys	dc.w		KeyFuncNumber
* These defines are the default when no file is specified
* Keep them sorted on 'Code|Qual'
KeyDefines	HotKey		KP1,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_LEFT_BOTTOM
		HotKey		KP2,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_BOTTOM
		HotKey		KP3,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_RIGHT_BOTTOM
		HotKey		KP4,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_LEFT
		HotKey		KP5,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_CENTER
		HotKey		KP6,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_RIGHT
		HotKey		KP7,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_LEFT_TOP
		HotKey		KP8,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_TOP
		HotKey		KP9,LSHIFT|LAMIGA,LSHIFT|LAMIGA,W_TO_RIGHT_TOP
		HotKey		BACKSPACE,LAMIGA|RAMIGA,LAMIGA|RAMIGA,TOGGLE_INPUTLOCK
		HotKey		TAB,LSHIFT|LAMIGA,LSHIFT|LAMIGA,REFRESH_W
		HotKey		DEL,LAMIGA|RAMIGA,LAMIGA|RAMIGA,TOGGLE_FASTMOUSE
		HotKey		UPARROW,LAMIGA,SHIFT|AMIGA,FRONT_W_TO_BACK
		HotKey		UPARROW,LSHIFT|LAMIGA,SHIFT|AMIGA,W_TO_BACK
		HotKey		UPARROW,RSHIFT|RAMIGA,SHIFT|AMIGA,FRONT_S_TO_BACK
		HotKey		DOWNARROW,LAMIGA,SHIFT|AMIGA,BACK_W_TO_FRONT
		HotKey		DOWNARROW,LSHIFT|LAMIGA,SHIFT|AMIGA,W_TO_FRONT
		HotKey		DOWNARROW,RSHIFT|RAMIGA,SHIFT|AMIGA,BACK_S_TO_FRONT
		HotKey		RIGHTARROW,LSHIFT|LAMIGA,LSHIFT|LAMIGA,ACTIVATE_NEXT_W
		HotKey		LEFTARROW,LSHIFT|LAMIGA,LSHIFT|LAMIGA,ACTIVATE_PREV_W
		HotKey		F1,LSHIFT|LAMIGA,LSHIFT|LAMIGA,MINIMIZE_W
		HotKey		F2,LSHIFT|LAMIGA,LSHIFT|LAMIGA,MAXIMIZE_W
		HotKey		F9,RSHIFT|RAMIGA,RSHIFT|RAMIGA,SUB_BITPLANE
		HotKey		F10,RSHIFT|RAMIGA,RSHIFT|RAMIGA,ADD_BITPLANE
		HotKey		HELP,LAMIGA|RAMIGA,LAMIGA|RAMIGA,TOGGLE_SUNMOUSE


* This is the end of the part coming from a file
* This table below defines the connection between the
* selected hotkey and the function to perform
JmpEntry_SIZE	=2
JmpEntry	MACRO
		dc.w		\1-JmpTable
		ENDM
JmpTable	JmpEntry	WinToTop
		JmpEntry	WinToBottom
		JmpEntry	WinToLeft
		JmpEntry	WinToRight
		JmpEntry	WinToTL
		JmpEntry	WinToBL
		JmpEntry	WinToTR
		JmpEntry	WinToBR
		JmpEntry	WinToCenter
		JmpEntry	WinToFront
		JmpEntry	WinToBack
		JmpEntry	BackWinToFront
		JmpEntry	FrontWinToBack
		JmpEntry	ActivatePrevWin
		JmpEntry	ActivateNextWin
		JmpEntry	MinimizeWin
		JmpEntry	MaximizeWin
		JmpEntry	RefreshWin
		JmpEntry	BackScrToFront
		JmpEntry	FrontScrToBack
		JmpEntry	SubBitplane
		JmpEntry	AddBitplane
		JmpEntry	ToggleInputLock
		JmpEntry	ToggleFastMouse
		JmpEntry	ToggleSunMouse

* Handler()
* This is the input handler.  For each event in the event list:
* If the event is a raw key event, then
*   make the KeyCode longword for that event's code and qualifier,
*   binary search the KeyDefines[] array for a matching entry (only consider
*     the qualifiers specified by the HotKey_Mask).  Since most keys pressed
*     will NOT match a hot-key, we want the search to be as fast as 
*     possible, so we use a binary search rather than a linear search.
* if the key was not a hot key,
*   go on to the next key
* otherwise,
*   perform the function for the specified hot key,
*  remove the hot key from the event list.
* When all the events have been checked, return the event list so that
* Intuition can do its thing.

* Subroutines that uses other registers than the ones below, has
* to save them first and restore them afterwards.
SD		EQUR	D0			; Scratch data register
Accel		EQUR	D1			; Acceleration
Thresh		EQUR	D2			; Threshold
Min		EQUR	D1			; For binary search
Max		EQUR	D2			; For binary search
Num		EQUR	D3			; For binary search
TheKey		EQUR	D4			; For binary search
SA		EQUR	A0			; Scratch address register
Hkey		EQUR	A1			; Hotkey
PEvent		EQUR	A2			; Previous Event
Event		EQUR	A3			; This Event

StatusBits	=IHS+ihs_Flags+1

Next		=ie_NextEvent
Class		=ie_Class
Code		=ie_Code
Qual		=ie_Qualifier
Seconds		=ie_TimeStamp+TV_SECS
Micros		=ie_TimeStamp+TV_MICRO

ButtonsQual	=IEQUALIFIER_MIDBUTTON|IEQUALIFIER_RBUTTON|IEQUALIFIER_LEFTBUTTON

* Call:  A0 = List of InputEvents, A1 = HandlerData
HandlerCode	Push		D1-D4/A0-A3
		lea		Chain(PC),PEvent
		move.l		A0,Next(PEvent)
		btst		#HDisabled,StatusBits(PC)	; Future feature
		bne		NoMoreEvents
ieLoop		move.l		Next(PEvent),Event
		move.l		Event,SD
		beq		NoMoreEvents

* Process TIMER events
CheckTimer	cmpi.b		#IECLASS_TIMER,Class(Event)
		bne.S		CheckMouse

CheckSBlanking	btst		#HScreenBlanked,StatusBits(PC)
		bne.S		ForceSBlanking
		move.l		PWkSWaitTime(PC),SD
		beq.S		CheckMBlanking
		cmp.l		Seconds(Event),SD
		bge.S		CheckMBlanking
ForceSBlanking	TEST_DISPLAY			; Screen should be blanked !
		beq		NeverRemove
		Call		BlankScreen	; But is isn't, so I blank it !
		bra		NeverRemove
CheckMBlanking	btst		#HMouseBlanked,StatusBits(PC)
		bne.S		ForceMBlanking
		move.l		PWkMWaitTime(PC),SD
		beq		NeverRemove
		cmp.l		Seconds(Event),SD
		bge		NeverRemove
ForceMBlanking	move.l		GFXbase(PC),SA	; Mouse should be blanked !
		move.l		gb_copinit(SA),SA
		move.w		copinit_sprstrtup+2(SA),SD
		swap		SD
		move.w		copinit_sprstrtup+6(SA),SD
		cmp.l		NewSprite(PC),SD
		beq		NeverRemove
		Call		BlankMouse	; But it isn't, so I blank it now !
		bra		NeverRemove

* Process RAWMOUSE events
CheckMouse	cmpi.b		#IECLASS_RAWMOUSE,Class(Event)
		bne.S		CheckKey
		btst		#HInputDisabled,StatusBits(PC)
		bne		Remove
		Call		BreakSBlanking
		Call		BreakMBlanking

CheckFastMouse	btst		#HAccelMouse,StatusBits(PC)
		beq.S		CheckSunMouse

		cmpi.w		#IECODE_NOBUTTON,Code(Event)
		bne		NeverRemove
		move.w		Qual(Event),SD
		andi.w		#ButtonsQual,SD
		bne		NeverRemove

		moveq		#0,Accel
		moveq		#0,Thresh
		move.b		PWkMouseAccel(PC),Accel
		move.b		PWkMouseThresh(PC),Thresh
		move.w		ie_X(Event),D0
		Call		CalcNewPos
		move.w		D0,ie_X(Event)
		move.w		ie_Y(Event),D0
		Call		CalcNewPos
		move.w		D0,ie_Y(Event)

CheckSunMouse	btst		#HSunMouse,StatusBits(PC)
		beq		NeverRemove
		Call		ActivateMWindow
		bra		NeverRemove

* Process RAWKey events
CheckKey	cmpi.b		#IECLASS_RAWKEY,Class(Event)
		bne		NeverRemove
		move.w		Code(Event),TheKey	; Construct a 'Code|Qual' long-word
		btst		#7,TheKey		; Is it a key-release ?
		bne		DontRemoveKey
		swap		TheKey			; It is a key-press !
		move.w		Qual(Event),TheKey
		move.w		PWkHotKeys(PC),Max	; Do Binary-search
		moveq		#-1,Min
SearchLoop	move.w		Max,Num
		add.w		Min,Num
		asr.w		#1,Num
		cmp.w		Num,Min
		beq.S		DontRemoveKey		; Didn't find a match
		move.w		Num,SD
		lsl.w		#3,SD			; HotKey_SIZE * Num
		lea		KeyDefines(PC),Hkey
		add.w		SD,Hkey
		move.l		TheKey,SD
		and.l		HotKey_Mask(Hkey),SD	; TheKey & KeyDefines[Num].HotKey_Mask
		cmp.l		HotKey_ID(Hkey),SD	; A match ?
		beq.S		Perform
		blt.S		LookLower
LookHigher	move.l		Num,Min			; (TheKey & KeyDefines[Num].HotKey_Mask) > KeyDefines[Num].HotKey_ID
		bra.S		SearchLoop
LookLower	move.l		Num,Max			; (TheKey & KeyDefines[Num].HotKey_Mask) < KeyDefines[Num].HotKey_ID
		bra.S		SearchLoop

* Found a match
* Hkey = Hotkey
Perform		Push		D0-D7/A0-A6		; (TheKey & KeyDefines[Num].HotKey_Mask) = KeyDefines[Num].HotKey_ID
		moveq		#0,D0
		move.b		HotKey_Func(Hkey),D0	; KeyDefines[Num].HotKey_Func
		lsl.w		#1,D0			; JmpEntry_SIZE * KeyDefines[Num].HotKey_Func
		lea		JmpTable(PC),A2
		add.w		0(A2,D0.W),A2
		btst		#HInputDisabled,StatusBits(PC)
		beq.S		1$
		lea		ToggleInputLock(PC),A0
		cmp.l		A2,A0
		bne.S		2$
1$		Prepare		Exec_Call
		CallLib		Forbid
		move.l		INTUIbase(PC),A6	; Intuitionbase stays loaded
		jsr		(A2)			; Jump to function
		Prepare		Exec_Call
		CallLib		Permit
2$		Pop		D0-D7/A0-A6
RemoveKey	btst		#HInputDisabled,StatusBits(PC)
		bne.S		Remove
		Call		BreakSBlanking
Remove		move.l		Next(Event),Next(PEvent); Remove event from chain and move on to next Event
		bra		ieLoop
DontRemoveKey	btst		#HInputDisabled,StatusBits(PC)
		bne.S		Remove
		Call		BreakSBlanking
NeverRemove	move.l		Event,PEvent		; Just move on to next Event
		bra		ieLoop
NoMoreEvents	Pop		D1-D4/A0-A3
		move.l		Chain(PC),D0		; Return (shortened ?) chain
		rts

* »»»»»»»»»»» Misc functions called by the hotkey-functions ««««««««««

* Call:   A0 = Window to check
* Return: Z-flag set if no active stringgadget or system-gadget
*	  in the window or in any active requester in the window
* Destroys no registers
CheckWindow	Push		D0/A1
		move.l		wd_FirstRequest(A0),D0
		beq.S		TestWin
ReqCLoop	tst.l		D0
		beq.S		WinCEnd
		move.l		D0,A1
		move.w		rq_Flags(A1),D0
		andi.w		#REQACTIVE,D0
		beq.S		NextReq
		move.l		rq_ReqGadget(A1),D0
		bra.S		GadCLoop	; Found an active requester
NextReq		move.l		rq_OlderRequest(A1),D0
		bra.S		ReqCLoop
TestWin		move.l		wd_FirstGadget(A0),D0
* D0 = Gadgetlist to check
GadCLoop	tst.l		D0
		beq.S		WinCEnd
		move.l		D0,A1
		move.w		gg_GadgetType(A1),D0
		andi.w		#SYSGADGET,D0
		bne.S		CheckSELECT
		move.w		gg_GadgetType(A1),D0
		andi.w		#STRGADGET,D0
		beq.S		NextGad
CheckSELECT	move.w		gg_Flags(A1),D0
		andi.w		#SELECTED,D0
		beq.S		NextGad
		moveq		#1,D0		; Window contains an active string-gadget
		bra.S		WinCEnd		; or an active system-gadget
NextGad		move.l		gg_NextGadget(A1),D0
		bra.S		GadCLoop
WinCEnd		Pop		D0/A1
		rts

* TopScreen()
* Find the top screen
* Return: D0 = Screen or NULL
* Destroys D0-D1/A0-A1
TopScreen	move.l		ib_FirstScreen(A6),D0
		rts

* BottomScreen()
* Find the bottom screen
* Return: D0 = Screen or NULL
* Destroys D0-D1/A0-A1
BottomScreen	move.l		ib_FirstScreen(A6),D0
		beq.S		2$
1$		move.l		D0,A0
		move.l		sc_NextScreen(A0),D0
		bne.S		1$
		move.l		A0,D0
2$		rts

* TopWindow()
* Find the top window of the specified screen. Start at the top
* layer of the screen and move backward as long as the layer exists
* and has no window connected to it. Return the window associated
* with the final layer, if any.
* Call  : A0 = Screen
* Return: D0 = Window or NULL
* Destroys D0-D1/A0-A1
TopWindow	move.l		sc_LayerInfo+li_top_layer(A0),A0
		moveq		#0,D0
1$		move.l		A0,D1
		beq.S		2$
		move.l		lr_Window(A0),D0
		bne.S		2$
		move.l		lr_back(A0),A0
		bra.S		1$
2$		tst.l		D0
		rts

* BottomWindow()
* Find the bottom window of the specified screen. Start at the top 
* layer and as long as the layer exists, go to the next layer back.
* If the layer has a window attached, consider that to be the bottom
* window until a lower one is found.
* Call  : A0 = Screen
* Return: D0 = Window or NULL
* Destroys D0-D1/A0-A1
BottomWindow	move.l		sc_LayerInfo+li_top_layer(A0),A0
		moveq		#0,D0
1$		move.l		A0,D1
		beq.S		3$
		move.l		lr_Window(A0),D1
		beq.S		2$
		move.l		D1,D0
2$		move.l		lr_back(A0),A0
		bra.S		1$
3$		tst.l		D0
		rts

* NextWindow()
* Find the next window below the specified window (wrap arround to
* the top if the window is the bottom one). Start with the window's
* layer and go back until a layer with a window is found, or no more
* layers exist. If a window was found, return it, otherwise, use the
* top window.
* Call  : A0 = Window
* Return: D0 = Next Window or NULL
* Destroys D0-D1/A0-A1
NextWindow	moveq		#0,D0
		move.l		wd_WLayer(A0),A1
1$		move.l		lr_back(A1),D1
		beq.S		2$
		move.l		D1,A1
		move.l		lr_Window(A1),D0
		beq.S		1$
		cmp.l		A0,D0
		beq.S		1$
2$		tst.l		D0
		rts

* PreviousWindow()
* Find the window that is on top of the specified window (or NULL if there
* are no windows above it). Start with the window's layer, and move to
* the layer in front until a layer with a (different) window is found, or
* until no more layers exist. If a window was found, return it, otherwise
* return NULL.
* Call  : A0 = Window
* Return: D0 = Previous Window or NULL
* Destroys D0-D1/A0-A1
PrevWindow	moveq		#0,D0
		move.l		wd_WLayer(A0),A1
1$		move.l		lr_front(A1),D1
		beq.S		2$
		move.l		D1,A1
		move.l		lr_Window(A1),D0
		beq.S		1$
		cmp.l		A0,D0
		beq.S		1$
2$		tst.l		D0
		rts

ScreenToBack	jmp		_LVOScreenToBack(A6)
ScreenToFront	jmp		_LVOScreenToFront(A6)
WindowToBack	Call		CheckWindow
		bne.S		WinErr
		jmp		_LVOWindowToBack(A6)
WindowToFront	Call		CheckWindow
		bne.S		WinErr
		jmp		_LVOWindowToFront(A6)
ActivateWindow	Call		CheckWindow
		bne.S		WinErr
		jmp		_LVOActivateWindow(A6)
MoveWindow	Call		CheckWindow
		bne.S		WinErr
		jmp		_LVOMoveWindow(A6)
SizeWindow	Call		CheckWindow
		bne.S		WinErr
		jmp		_LVOSizeWindow(A6)
WinErr		rts


* »»»»»»»»»»»»»»»»»»» Window manipulating functions ««««««««««««««««««

* Move the active window in x/y directions
* Destroys D0-D1/A0-A2
WinToTop	lea		TopIt(PC),A1		; Window To Top
		lea		DontMoveX(PC),A2
		bra.S		WindowMove
WinToBottom	lea		BottomIt(PC),A1		; Window To Bottom
		lea		DontMoveX(PC),A2
		bra.S		WindowMove
WinToLeft	lea		DontMoveY(PC),A1	; Window To Left
		lea		LeftIt(PC),A2
		bra.S		WindowMove
WinToRight	lea		DontMoveY(PC),A1	; Window To Right
		lea		RightIt(PC),A2
		bra.S		WindowMove
WinToTL		lea		TopIt(PC),A1		; Window To Upper Left Corner
		lea		LeftIt(PC),A2
		bra.S		WindowMove
WinToTR		lea		TopIt(PC),A1		; Window To Upper Right Corner
		lea		RightIt(PC),A2
		bra.S		WindowMove
WinToBL		lea		BottomIt(PC),A1		; Window To Lower Left Corner
		lea		LeftIt(PC),A2
		bra.S		WindowMove
WinToBR		lea		BottomIt(PC),A1		; Window To Lower Right Corner
		lea		RightIt(PC),A2
		bra.S		WindowMove
WinToCenter	lea		CenterHeight(PC),A1	; Window To Center
		lea		CenterWidth(PC),A2
* Call:	A1 = Function to adjust position in y-direction
*	A2 = Function to adjust position in x-direction
WindowMove	move.l		ib_ActiveWindow(A6),D0
		beq.S		2$
		move.l		D0,A0
		jsr		(A1)
		move.w		D0,D1
		jsr		(A2)
		bne.S		1$
		tst.w		D1
		beq.S		2$
1$		Call		MoveWindow
2$		rts

DontMoveX
DontMoveY	moveq		#0,D0
		rts
* Call:	A0 = Window
CenterHeight	move.l		wd_WScreen(A0),A1
		move.w		sc_Height(A1),D0
		sub.w		wd_Height(A0),D0
		asr.w		#1,D0
		sub.w		wd_TopEdge(A0),D0
		rts
* Call:	A0 = Window
CenterWidth	move.l		wd_WScreen(A0),A1
		move.w		sc_Width(A1),D0
		sub.w		wd_Width(A0),D0
		asr.w		#1,D0
		sub.w		wd_LeftEdge(A0),D0
		rts
* Call:	A0 = Window
BottomIt	move.l		wd_WScreen(A0),A1
		move.w		sc_Height(A1),D0
		sub.w		wd_TopEdge(A0),D0
		sub.w		wd_Height(A0),D0
		rts
* Call:	A0 = Window
RightIt		move.l		wd_WScreen(A0),A1
		move.w		sc_Width(A1),D0
		sub.w		wd_LeftEdge(A0),D0
		sub.w		wd_Width(A0),D0
		rts
* Call:	A0 = Window
TopIt		move.w		wd_TopEdge(A0),D0
		neg.w		D0
		rts
* Call:	A0 = Window
LeftIt		move.w		wd_LeftEdge(A0),D0
		neg.w		D0
		rts

* WinToFront()
* Send the active window to the top of the list.
* Destroys D0-D2/A0-A1
WinToFront	move.l		ib_ActiveScreen(A6),D0
		beq.S		1$
		move.l		ib_ActiveWindow(A6),D2
		beq.S		1$
		move.l		D0,A0
		Call		TopWindow
		cmp.l		D0,D2		; Same window ? (NOT NULL !)
		beq.S		1$
		move.l		D2,A0
		Call		WindowToFront
1$		rts

* WinToBack()
* Send the active window to the back of the list.
* Destroys D0-D2/A0-A1
WinToBack	move.l		ib_ActiveScreen(A6),D0
		beq.S		1$
		move.l		ib_ActiveWindow(A6),D2
		beq.S		1$
		move.l		D0,A0
		Call		BottomWindow
		cmp.l		D0,D2		; Same window ? (NOT NULL !)
		beq.S		1$
		move.l		D2,A0
 		Call		WindowToBack
1$		rts

* BackWindowToFront()
* Move the bottom window to the top and activate it.
* Get the bottom window, skipping over backdrop windows. If one
* is found, bring it to the front, and activate it.
* Destroys D0-D2/A0-A1
BackWinToFront	move.l		ib_ActiveScreen(A6),D0
		beq.S		3$
		move.l		D0,A0
		Call		BottomWindow
1$		move.l		D0,A0
		tst.l		D0
		beq.S		3$
		btst		#0,wd_Flags+2(A0)	; BackDrop ?
		beq.S		2$
		Call		PrevWindow
		bra.S		1$
2$		move.l		A0,D2
		Call		WindowToFront
		move.l		D2,A0
		Call		ActivateWindow
3$		rts

* FrontWindowToBack()
* Move the top window to the back, and activate the new top window.
* Get the top window, send it top to the back, and activate the top
* window.
* Destroys D0-D2/A0-A1
FrontWinToBack	move.l		ib_ActiveScreen(A6),D2
		beq.S		1$
		move.l		D2,A0
		Call		TopWindow
		beq.S		1$
		move.l		D0,A0
		Call		WindowToBack
		move.l		D2,A0
		Call		TopWindow
		beq.S		1$
		move.l		D0,A0
		Call		ActivateWindow
1$		rts

* ActivatePreviousWindow()
* Get the window previous to the active window (if none, then get the
* bottom window in the active screen), and activate that window.
* Destroys D0-D2/A0-A1
ActivatePrevWin	move.l		ib_ActiveScreen(A6),D2
		beq.S		3$
		move.l		ib_ActiveWindow(A6),D0
		beq.S		1$
		move.l		D0,A0
		Call		PrevWindow
		bne.S		2$
		move.l		D2,A0
		Call		BottomWindow
		bra.S		2$
1$		move.l		D2,A0
		move.l		sc_FirstWindow(A0),D0
2$		beq.S		3$
		move.l		D0,A0
		Call		ActivateWindow
3$		rts

* ActivateNextWindow()
* Get the window below the active window and activate it.
* Destroys D0-D2/A0-A1
ActivateNextWin	move.l		ib_ActiveScreen(A6),D2
		beq.S		3$
		move.l		ib_ActiveWindow(A6),D0
		beq.S		1$
		move.l		D0,A0
		Call		NextWindow
		bne.S		2$
		move.l		D2,A0
		Call		TopWindow
		bra.S		2$
1$		move.l		D2,A0
		move.l		sc_FirstWindow(A0),D0
2$		beq.S		3$
		move.l		D0,A0
		Call		ActivateWindow
3$		rts

* Size the active window in x/y directions
* Destroys D0-D1/A0-A2
MinimizeWin	lea		MinimizeY(PC),A1
		lea		MinimizeX(PC),A2
		bra.S		WindowSize
MaximizeWin	lea		MaximizeY(PC),A1
		lea		MaximizeX(PC),A2
* Call:	A1 = Function to adjust size in y-direction
* 	A2 = Function to adjust size in x-direction
WindowSize	move.l		ib_ActiveWindow(A6),D0
		beq.S		2$
		move.l		D0,A0
		move.l		wd_Flags(A0),D0
		andi.w		#WINDOWSIZING,D0
		beq.S		2$
		jsr		(A1)
		move.w		D0,D1
		jsr		(A2)
		bne.S		1$
		tst.w		D1
		beq.S		2$
1$		Call		SizeWindow
2$		rts

* Call:	A0 = Window
MinimizeY	move.w		wd_MinHeight(A0),D0
		sub.w		wd_Height(A0),D0
		rts
* Call:	A0 = Window
MinimizeX	move.w		wd_MinWidth(A0),D0
		sub.w		wd_Width(A0),D0
		rts
* Call:	A0 = Window
MaximizeY	move.l		wd_WScreen(A0),A1
		move.w		sc_Height(A1),D0
		sub.w		wd_TopEdge(A0),D0
		cmp.w		#$FFFF,wd_MaxHeight(A0)
		beq.S		1$
		cmp.w		wd_MaxHeight(A0),D0
		ble.S		1$
		move.w		wd_MaxHeight(A0),D0
1$		sub.w		wd_Height(A0),D0
		rts
* Call:	A0 = Window
MaximizeX	move.l		wd_WScreen(A0),A1
		move.w		sc_Width(A1),D0
		sub.w		wd_LeftEdge(A0),D0
		cmp.w		#$FFFF,wd_MaxWidth(A0)
		beq.S		1$
		cmp.w		wd_MaxWidth(A0),D0
		ble.S		1$
		move.w		wd_MaxWidth(A0),D0
1$		sub.w		wd_Width(A0),D0
		rts


* Refresh the active windows border and gadgets
* Destroys D0-D1/A0-A1
RefreshWin	move.l		ib_ActiveWindow(A6),D0
		beq.S		1$
		move.l		D0,A0
		CallLib		RefreshWindowFrame
1$		rts


* »»»»»»»»»»»»»»»»»»» Screen manipulating functions ««««««««««««««««««

* BackScrToFront()
* Bring the bottom-most screen to the top, and activate its top window.
* While there is a screen following the active one, move the the next screen.
* Bring that screen to the front and find its top window.  If one was found,
* activate the window.
* Destroys D0-D1/A0-A1
BackScrToFront	Call		BottomScreen
		beq.S		1$
		move.l		D0,A0
		PushS		A0
		Call		ScreenToFront
		PopS		A0
		Call		TopWindow
		beq.S		1$
		move.l		D0,A0
		Call		ActivateWindow
1$		rts

* FrontScreenToBack()
* Move the top screen to the back and activate the top window on the new
* top screen.
* Destroys D0-D1/A0-A1
FrontScrToBack	Call		TopScreen
		beq.S		1$
		move.l		D0,A0
		PushS		A0
		Call		ScreenToBack
		PopS		A0
		Call		TopWindow
		beq.S		1$
		move.l		D0,A0
		Call		ActivateWindow
1$		rts

Planes		EQUR		D2
Index		EQUR		D3
WScreen		EQUR		A2
WBitMap		EQUR		A3
WViewPort	EQUR		A4
LORESMAX	=6
HIRESMAX	=4

* Subtracts a bitplane from the active screen.
* HAM-screens won't be touched.
* EHB-screens won't be EHB-screens after this.
* Destroys D0-D3/A0-A4
SubBitplane	Call		ASCommon
		beq.S		2$
		move.w		vp_Modes(WViewPort),D0
		andi.w		#V_HAM,D0
		bne.S		2$			; Don't touch HAM
		subq.w		#1,Planes
		beq.S		2$
		move.b		Planes,bm_Depth(WBitMap)
		move.w		Planes,Index
		mulu		#4,Index
		move.l		bm_Planes(WBitMap,Index),A0
		clr.l		bm_Planes(WBitMap,Index)
		move.w		sc_Width(WScreen),D0
		move.w		sc_Height(WScreen),D1
		move.l		GFXbase(PC),A6
		CallLib		FreeRaster
		cmp.b		#LORESMAX,Planes
		bge.S		1$
		andi.w		#~V_EHB,vp_Modes(a1)	; Clear EHB flag
1$		move.l		INTUIbase(PC),A6
		CallLib		RemakeDisplay
2$		rts

* Adds a bitplane to the active screen (if enough CHIP-memory).
* HIRES-screens can get a maximum of 4 bitplanes.
* LORES-screens can get a maximum of 6 bitplanes (6 bitplanes means EHB).
* Destroys D0-D3/A0-A4
AddBitplane	Call		ASCommon
		beq.S		3$
		move.w		Planes,Index
		mulu		#4,Index
		addq.w		#1,Planes
		moveq		#HIRESMAX,D1
		move.w		vp_Modes(WViewPort),D0
		andi.w		#V_HIRES,D0		; HIRES ?
		bne.S		1$
		moveq		#LORESMAX,D1
1$		cmp.w		D1,Planes
		bgt.S		3$
		move.w		sc_Width(WScreen),D0
		move.w		sc_Height(WScreen),D1
		move.l		GFXbase(PC),A6
		CallLib		AllocRaster
		move.l		D0,bm_Planes(WBitMap,Index)
		beq.S		3$
		move.l		D0,A1
		move.w		bm_Rows(WBitMap),D0
		swap		D0
		move.w		bm_BytesPerRow(WBitMap),D0
		moveq		#2,D1
		CallLib		BltClear
		move.b		Planes,bm_Depth(WBitMap)
		cmp.w		#LORESMAX,Planes
		blt.S		2$
		ori.w		#V_EHB,vp_Modes(WViewPort)	; Depth >= 6 => set EHB flag !
2$		move.l		INTUIbase(PC),A6
		CallLib		RemakeDisplay
3$		rts

ASCommon	move.l		ib_ActiveScreen(A6),D0
		beq.S		1$
		move.l		D0,WScreen
		lea		sc_BitMap(WScreen),WBitMap
		lea		sc_ViewPort(WScreen),WViewPort
		moveq		#0,Planes
		move.b		bm_Depth(WBitMap),Planes
1$		rts

* »»»»»»»»»»»»»»»»»»» Misc manipulating functions ««««««««««««««««««««

* Enables/Disables RAWKEY/RAWMOUSE events
* Destroys D0-D1/A0-A1
ToggleInputLock	lea		StatusBits(PC),A0
		bchg		#HInputDisabled,(A0)
		beq.S		BlankScreen
		bra.S		BreakSBlanking

* Enables/Disables mouse-acceleration
* Destroys D0-D1/A0-A1
ToggleFastMouse	lea		StatusBits(PC),A0
		bchg		#HAccelMouse,(A0)
		rts

* Enables/Disables sun-mouse
* Destroys D0-D1/A0-A1
ToggleSunMouse	lea		StatusBits(PC),A0
		bchg		#HSunMouse,(A0)
		rts

* »»»»»»»»»»»»»»» Misc functions called by handler «««««««««««««««««««

* Blanks the screen by installing a temporary copper-list, which
* ensures a black display, and then turning the raster-DMA off.
BlankScreen	Push		D1-D7/A0-A2/A6
		Prepare		Exec_Call
		CallLib		Forbid
		lea		StatusBits(PC),A0
		bset		#HScreenBlanked,(A0)
		move.l		GFXbase(PC),A6
		move.l		gb_ActiView(A6),A1
		move.l		v_LOFCprList(A1),A2
		movem.w		(A2),D2-D7	; Save CprList (10 bytes)
		clr.l		crl_Next(A2)
		move.l		NewCopper(PC),crl_start(A2)
		CallLib		LoadView	; Install a one-color copperlist
		OFF_DISPLAY
		movem.w		D2-D7,(A2) 	; Restore CprList (10 bytes)
		Prepare		Exec_Call
		CallLib		Permit
		Pop		D1-D7/A0-A2/A6
		rts

* Sets new screen-blanking timeout and turns the raster-DMA 
* back on, if it is off.
BreakSBlanking	move.l		PWkSTimeout(PC),D0
		beq.S		1$
		add.l		Seconds(Event),D0
1$		lea		PWkSWaitTime(PC),A0
		move.l		D0,(A0)
		lea		StatusBits(PC),A0
		bclr		#HScreenBlanked,(A0)
		beq.S		2$
		PushS		A6
		move.l		GFXbase(PC),A6
		move.l		gb_ActiView(A6),A1
		CallLib		LoadView
		ON_DISPLAY
		PopS		A6
2$		rts

* Blanks the mouse by installing an invisible image for sprite 0
* in the copper-list
BlankMouse	PushS		A6
		Prepare		Exec_Call
		CallLib		Forbid
		move.l		GFXbase(PC),A1
		move.l		gb_copinit(A1),A1
		lea		copinit_sprstrtup(A1),A1
		cmp.w		#$0120,(A1)
		bne.S		2$
		cmp.w		#$0122,4(A1)
		bne.S		2$
		lea		StatusBits(PC),A0
		bset		#HMouseBlanked,(A0)
		bne.S		1$
		lea		OldSprite(PC),A0
		move.w		2(A1),(A0)+
		move.w		6(A1),(A0)
1$		lea		NewSprite(PC),A0
		move.w		(A0)+,2(A1)
		move.w		(A0),6(A1)
2$		Prepare		Exec_Call
		CallLib		Permit
		PopS		A6
		rts

* Sets new mouse-blanking timeout and restores the old
* sprite 0 image, if the mouse is off.
BreakMBlanking	PushS		A6
		move.l		PWkMTimeout(PC),D0
		beq.S		1$
		add.l		Seconds(Event),D0
1$		lea		PWkMWaitTime(PC),A0
		move.l		D0,(A0)
		lea		StatusBits(PC),A0
		bclr		#HMouseBlanked,(A0)
		beq.S		2$
		Prepare		Exec_Call
		CallLib		Forbid
		move.l		GFXbase(PC),A1
		move.l		gb_copinit(A1),A1
		move.w		OldSprite+0(PC),copinit_sprstrtup+2(A1)
		move.w		OldSprite+2(PC),copinit_sprstrtup+6(A1)
		Prepare		Exec_Call
		CallLib		Permit
2$		PopS		A6
		rts

* Activate the window the mouse is over (if any, and if it is not
* already active).
ib_MouseY	=ib_FirstScreen+8
ib_MouseX	=ib_MouseY+2
ActivateMWindow	Push		D1-D2/A0-A2/A6
		Prepare		Exec_Call
		CallLib		Forbid
		move.l		INTUIbase(PC),A2
		move.l		ib_FirstScreen(A2),A1
ScrSLoop	move.l		A1,D0
		beq.S		DoneAMW			; Out of screens ?
		move.w		ib_MouseX(A2),D0
		move.w		ib_MouseY(A2),D1
		move.w		sc_ViewPort+vp_Modes(A1),D2
		btst		#15,D2
		bne.S		WasHIRES
		asr.w		#1,D0
WasHIRES	btst		#2,D2
		bne.S		WasLACE
		asr.w		#1,D1
WasLACE		sub.w		sc_ViewPort+vp_DyOffset(A1),D1
		bge.S		FoundScr
		move.l		sc_NextScreen(A1),A1
		bra.S		ScrSLoop
FoundScr	lea		sc_LayerInfo(A1),A0	; Mouse is on this screen
		move.l		LAYERbase(PC),A6
		CallLib		WhichLayer
		tst.l		D0
		beq.S		DoneAMW			; No layer ?
		move.l		D0,A0
		move.l		lr_Window(A0),D0
		beq.S		DoneAMW			; No window ?
		move.l		INTUIbase(PC),A6
		cmp.l		ib_ActiveWindow(A6),D0
		beq.S		DoneAMW
		move.l		D0,A0
		Call		ActivateWindow
DoneAMW		Prepare		Exec_Call
		CallLib		Permit
		Pop		D1-D2/A0-A2/A6
		rts

* Call:   D0:16 = ie_X/ie_Y
* Return: D0:16 = modified ie_X/ie_Y
* Calculates new mouse position using acceleration and threshold.
CalcNewPos	tst.w		D0
		bge.S		2$
		add.w		Thresh,D0
		bge.S		1$
		mulu		Accel,D0
1$		sub.w		Thresh,D0
		rts
2$		sub.w		Thresh,D0
		ble.S		3$
		mulu		Accel,D0
3$		add.w		Thresh,D0
		rts

HandlerSize	=*-IHS

* The data below goes into CHIP memory
ChipData
MyCopperList	dc.w		$0180,$0000	; Copperlist that ensures
		dc.w		$FFFF,$FFFE	; a one-color display (black)

MySprite	dc.w		$FE00,$FF00	; Invisible sprite
		dc.w		$0000,$0000
		dc.w		$0000,$0000
ChipSize	=*-ChipData

* »»»»»»»»»»»»»»»»»»»»»» Input-handler end «««««««««««««««««««««««««««
		END
* »»»»»»»»»»»»»»»»»»»»»»»»»» The End «««««««««««««««««««««««««««««««««

