*	PWKeys V1.0
*	By Preben Nielsen
*
*	  PWKeys is an input-handler that allows you to manipulate windows
*	and screens by pressing keys on the keyboard (a lot faster than
*	using the mouse).
*
*	  Once installed, the program only uses 1124 bytes of memory. To
*	remove PWKeys simply run the it again.
*
*	NOTE:	There's no need to 'RUN' or 'RUNBACK' this program from the
*		CLI. It terminates immediately.
*
*HISTORY
*          Made with Hisoft V2.12
*
*  V1.0   20-Apr-91: This is first release. I made it over a year ago

	OPT O+
	OPT O1+			; Tells when a branch could be optimised to short
	OPT i+			; Tells when '#' is probably missing
		include		"PWKeys.i"
		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/dos_lib.i"
		include		"libraries/dosextens.i"
		include		"graphics/layers.i"
		include		"intuition/intuition.i"
		include		"intuition/intuitionbase.i"
		include		"intuition/intuition_lib.i"
		include		"Workbench/startup.i"
		include		"Workbench/workbench.i"
		include		"Workbench/icon_lib.i"


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
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

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(PC),A1
		lea		ihs_PortName(A1),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
		lea		IntName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,IntBase(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		dc.b		10,$9B,'0;33m PWKeys V1.0',10
		dc.b		$9B,'0;31m 1991 by ',$9B,'0;33mPreben Nielsen',$9B,'0;31m',10,' ',0
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		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/A0-A1/A6
		Call		GetKeys
		beq.S		2$
		lea		IntuiBase(PC),A1
		move.l		IntBase(DB),(A1)
		Prepare		Exec_Call
		move.l		#HandlerSize,D0
		move.l		#MEMF_PUBLIC|MEMF_CLEAR,D1
		CallLib		AllocMem
		move.l		D0,HMem(DB)
		beq.S		2$
		move.l		D0,A1
		lea		IHS(PC),A0
		move.l		#HandlerSize,D0
		Call		MemCopy
		move.l		D0,ihs_Length(A1)		; This will enable removal by other programs
		lea		HandlerCode-IHS(A1),A0
		move.l		A0,ihs_Interrupt+IS_CODE(A1)	; HandlerBlock.HInterrupt.is_Code = Handler
		lea		PWKVersionID-IHS(A1),A0
		move.l		A0,ihs_Interrupt+IS_DATA(A1)	;HandlerBlock.HInterrupt.is_Data = KeyDefines;
		move.b		PWKPri(PC),ihs_Interrupt+LN_PRI(A1);HandlerBlock.HInterrupt.is_Node.ln_Pri = PRI;
		move.l		A1,D0
2$		Pop		D1/A0-A1/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		HMem(DB),D0
		beq.S		1$
		move.l		D0,A1
		move.l		#HandlerSize,D0
		Prepare		Exec_Call
		CallLib		FreeMem
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$
		Prepare		Exec_Call
		move.l		ihs_Length(A0),D0
		move.l		A0,A1
		CallLib		FreeMem
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		TempFile(PC),A0
		move.l		A0,D2
		move.l		#FilePart,D3
		CallLib		Read
		cmp.l		D0,D3
		bne.S		1$
		movem.l		TempFile(PC),D0-D1
		movem.l		PWKVersionID(PC),D2-D3
		cmp.l		D0,D2
		bne.S		1$
		cmp.l		D1,D3
		bne.S		1$
		lea		TempFile(PC),A0
		lea		PWKVersionID(PC),A1
		move.l		#FilePart,D0
		Call		MemCopy
		moveq		#1,rtsValue
1$		move.l		D4,D1
		CallLib		Close
2$		move.l		rtsValue,D0
		Pop		D1-D7/A0-A6
		rts

* This is buffer when trying to read file
TempFile	dc.l		'PWKF'
		dc.w		PWKeysVersion,PWKeysRevision
		dc.b		PWKeysPri,0
		dc.w		DefFuncNumber
TemPWKeys	dcb.b		FilePart-12

*====================== Input-handler start =========================
ihs_Port	=0
ihs_Interrupt	=MP_SIZE
ihs_ID		=MP_SIZE+IS_SIZE
ihs_Length	=MP_SIZE+IS_SIZE+4
ihs_Flags	=MP_SIZE+IS_SIZE+8
ihs_PortName	=MP_SIZE+IS_SIZE+10

ihs_Start	MACRO
		dcb.b		MP_SIZE		; Message-Port structure
		dcb.b		IS_SIZE		; Interrupt structure
		dc.l		'P_IH'		; ID (Handler made by me)
		dc.l		0		; Length of handler 
		dc.w		0		; Flags
		dc.b		\1,0
		EVEN
		ENDM

HDisabled	=0
HNoExtRemoval	=1				; Future

* This is the handler-block
IHS		ihs_Start	<'PWKeys V1.0 Port'>
* Local variables
Chain		dc.l		0
IntuiBase	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
PWKHotKeys	dc.w		DefFuncNumber
* These defines are the default when no file is specified
* Keep them sorted on 'Code|Qual'
KeyDefines	HotKey		KP1,LAMIGA,LAMIGA,AW_TO_LEFT_BOTTOM
		HotKey		KP2,LAMIGA,LAMIGA,AW_TO_BOTTOM
		HotKey		KP3,LAMIGA,LAMIGA,AW_TO_RIGHT_BOTTOM
		HotKey		KP4,LAMIGA,LAMIGA,AW_TO_LEFT
		HotKey		KP5,LAMIGA,LAMIGA,AW_TO_CENTER
		HotKey		KP6,LAMIGA,LAMIGA,AW_TO_RIGHT
		HotKey		KP7,LAMIGA,LAMIGA,AW_TO_LEFT_TOP
		HotKey		KP8,LAMIGA,LAMIGA,AW_TO_TOP
		HotKey		KP9,LAMIGA,LAMIGA,AW_TO_RIGHT_TOP
		HotKey		UPARROW,LAMIGA,LSHIFT|LAMIGA|RAMIGA,AW_TO_BACK
		HotKey		UPARROW,LSHIFT|LAMIGA,LSHIFT|LAMIGA|RAMIGA,FRONT_W_TO_BACK
		HotKey		UPARROW,RAMIGA,LSHIFT|LAMIGA|RAMIGA,FRONT_S_TO_BACK
		HotKey		DOWNARROW,LAMIGA,LSHIFT|LAMIGA|RAMIGA,AW_TO_FRONT
		HotKey		DOWNARROW,LSHIFT|LAMIGA,LSHIFT|LAMIGA|RAMIGA,BACK_W_TO_FRONT
		HotKey		DOWNARROW,RAMIGA,LSHIFT|LAMIGA|RAMIGA,BACK_S_TO_FRONT
		HotKey		RIGHTARROW,LAMIGA,LAMIGA,ACTIVATE_NEXT_W
		HotKey		LEFTARROW,LAMIGA,LAMIGA,ACTIVATE_PREV_W
		HotKey		U_CODE,U_QUAL,U_QUAL,U_FUNC
		HotKey		U_CODE,U_QUAL,U_QUAL,U_FUNC
		HotKey		U_CODE,U_QUAL,U_QUAL,U_FUNC
* This is the end of the part coming from a file

* This table below defines the connction between the
* selected hotkey and the function to perform
JmpEntry_SIZE	=2
JmpEntry	MACRO
		dc.w		\1-JmpTable
		ENDM
JmpTable	JmpEntry	WindowToTop
		JmpEntry	WindowToBottom
		JmpEntry	WindowToLeft
		JmpEntry	WindowToRight
		JmpEntry	WindowToTL
		JmpEntry	WindowToBL
		JmpEntry	WindowToTR
		JmpEntry	WindowToBR
		JmpEntry	WindowToCenter
		JmpEntry	CurrWinToFront
		JmpEntry	CurrWinToBack
		JmpEntry	ActivatePrevWin
		JmpEntry	ActivateNextWin
		JmpEntry	BackWinToFront
		JmpEntry	FrontWinToBack
		JmpEntry	BackScrToFront
		JmpEntry	FrontScrToBack
		JmpEntry	Dummy
		JmpEntry	Dummy
		JmpEntry	Dummy

* 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.

Max		EQUR	D4
Min		EQUR	D5
Num		EQUR	D6
TheKey		EQUR	D7
PEvent		EQUR	A4				; Previous Event
Event		EQUR	A5				; This Event

Next		=ie_NextEvent
Class		=ie_Class
Code		=ie_Code
Qual		=ie_Qualifier

* Call:  A0 = List of InputEvents, A1 = HandlerData
HandlerCode	Push		D1-D7/A0-A6
		move.w		IHS+ihs_Flags(PC),D0
		btst		#HDisabled,D0		; Future feature
		bne		NoMoreEvents
		lea		Chain(PC),PEvent
		move.l		A0,Next(PEvent)
ieLoop		move.l		Next(PEvent),Event
		move.l		Event,D0
		beq.S		NoMoreEvents
		cmpi.b		#IECLASS_RAWKEY,Class(Event)
		bne.S		DontRemove
DoKey		move.w		Code(Event),TheKey	; Construct a 'Code|Qual' long-word
		swap		TheKey
		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		DontRemove		; Didn't find a match
		move.w		Num,D0
		lsl.w		#3,D0			; HotKey_SIZE*Num
		lea		KeyDefines(PC),A2
		add.w		D0,A2
		move.l		TheKey,D0
		and.l		HotKey_Mask(A2),D0	; TheKey & KeyDefines[Num].HotKey_Mask
		cmp.l		HotKey_ID(A2),D0	; 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
* A2 = Hotkey
Perform		Prepare		Exec_Call
		CallLib		Forbid			; (TheKey & KeyDefines[Num].HotKey_Mask) = KeyDefines[Num].HotKey_ID
		moveq		#0,D0
		move.b		HotKey_Func(A2),D0	; KeyDefines[Num].HotKey_Func
		lsl.w		#1,D0			; JmpEntry_SIZE*KeyDefines[Num].HotKey_Func
		lea		JmpTable(PC),A0
		add.w		0(A0,D0.W),A0
		move.l		IntuiBase(PC),A6	; Intuitionbase stays loaded
		jsr		(A0)			; Jump to function
		Prepare		Exec_Call
		CallLib		Permit
Remove		move.l		Next(Event),Next(PEvent); Remove event from chain and move on to next Event
		bra.S		ieLoop
* Didn't find a match
DontRemove	move.l		Event,PEvent		; Just move on to next Event
		bra.S		ieLoop
* Lets return
NoMoreEvents	Pop		D1-D7/A0-A6
		move.l		Chain(PC),D0		; Return (shortened ?) chain
		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)
WinErr		rts

* 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
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
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

* 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
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
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
NextWindow	move.l		wd_WLayer(A0),A1
1$		move.l		lr_back(A1),A1
		move.l		A1,D1
		beq.S		2$
		move.l		lr_Window(A1),D0
		beq.S		1$
		bra.S		3$
2$		move.l		wd_WScreen(A0),A0
		CallS		TopWindow
3$		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
PrevWindow	moveq		#0,D0
		move.l		wd_WLayer(A0),A1
1$		move.l		lr_front(A1),A1
		move.l		A1,D1
		beq.S		2$
		move.l		lr_Window(A1),D0
		beq.S		1$
		cmp.l		A0,D0
		beq.S		1$
2$		tst.l		D0
		rts

* BackScrToFront()
* Bring the bottom-most screen to the top, and activate its top window.
* While there is a screen following the current one, move the the next screen.
* Bring that screen to the front and find its top window.  If one was found,
* activate the window.
BackScrToFront	move.l		A2,-(SP)
		move.l		ib_FirstScreen(A6),A2
1$		move.l		sc_NextScreen(A2),D0
		beq.S		2$
		move.l		D0,A2
		bra.S		1$
2$		move.l		A2,A0
		Call		ScreenToFront
		move.l		A2,A0
		move.l		(SP)+,A2
		Call		TopWindow
		bra.S		ActiCommon

* FrontScreenToBack()
* Move the top screen to the back and activate the top window on the new
* top screen.
FrontScrToBack	move.l		ib_FirstScreen(A6),A0
		Call		ScreenToBack
		move.l		ib_FirstScreen(A6),A0
		Call		TopWindow
		bra.S		ActiCommon

* ActivatePreviousWindow()
* Get the window previous to the current window (if none, then get the
* bottom window in the active screen), and activate that window.
ActivatePrevWin	move.l		ib_ActiveWindow(A6),A0
		CallS		PrevWindow
		bne.S		ActiCommon
		move.l		ib_ActiveScreen(A6),A0
		Call		BottomWindow
		bra.S		ActiCommon

* ActivateNextWindow()
* Get the window below the current window and activate it.
ActivateNextWin	move.l		ib_ActiveWindow(A6),A0
		Call		NextWindow
ActiCommon	beq.S		1$
		move.l		D0,A0
		Call		ActivateWindow
1$		rts

* CurrentWindowToBack()
* Send the current window to the back of the list.
CurrWinToBack	move.l		ib_ActiveWindow(A6),D0
		beq.S		1$
		move.l		D0,A0
 		Call		WindowToBack
1$		rts

* CurrentWindowToFront()
* Send the current window to the top of the list.
CurrWinToFront	move.l		ib_ActiveWindow(A6),D0
		beq.S		1$
		move.l		D0,A0
		Call		WindowToFront
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.
BackWinToFront	move.l		A2,-(SP)
		move.l		ib_ActiveScreen(A6),A0
		Call		BottomWindow
		beq.S		3$
1$		move.l		D0,A0
		move.l		A0,D0
		beq.S		3$
		btst		#0,wd_Flags+2(A0)	; BackDrop ?
		beq.S		2$
		Call		PrevWindow
		bra.S		1$
2$		move.l		A0,A2
		Call		WindowToFront
		move.l		A2,D0
		beq.S		3$
		move.l		D0,A0
		Call		ActivateWindow
3$		move.l		(SP)+,A2
		rts

* FrontWindowToBack()
* Move the top window to the back, and activate the new top window.
* Get the top window, and then the window following it.  Send the top window
* to the back, and activate the next window.
FrontWinToBack	Push		D2/A2
		move.l		ib_ActiveScreen(A6),A0
		Call		TopWindow
		beq.S		1$
		move.l		D0,A0
		move.l		A0,A2
		Call		NextWindow
		move.l		D0,D2
		move.l		A2,A0
		Call		WindowToBack
		move.l		D2,D0
		beq.S		1$
		move.l		D0,A0
		Call		ActivateWindow
1$		Pop		D2/A2
		rts

WindowToTop	lea		TopIt(PC),A1		; Window To Top
		lea		DontMoveX(PC),A2
		bra.S		WindowMove
WindowToBottom	lea		BottomIt(PC),A1		; Window To Bottom
		lea		DontMoveX(PC),A2
		bra.S		WindowMove
WindowToLeft	lea		DontMoveY(PC),A1	; Window To Left
		lea		LeftIt(PC),A2
		bra.S		WindowMove
WindowToRight	lea		DontMoveY(PC),A1	; Window To Right
		lea		RightIt(PC),A2
		bra.S		WindowMove
WindowToCenter	lea		CenterHeight(PC),A1	; Window To Center
		lea		CenterWidth(PC),A2
		bra.S		WindowMove
WindowToTL	lea		TopIt(PC),A1		; Window To Upper Left Corner
		lea		LeftIt(PC),A2
		bra.S		WindowMove
WindowToTR	lea		TopIt(PC),A1		; Window To Upper Right Corner
		lea		RightIt(PC),A2
		bra.S		WindowMove
WindowToBL	lea		BottomIt(PC),A1		; Window To Lower Left Corner
		lea		LeftIt(PC),A2
		bra.S		WindowMove
WindowToBR	lea		BottomIt(PC),A1		; Window To Lower Right Corner
		lea		RightIt(PC),A2

* A1 = Function to adjust window in y-direction
* A2 = Function to adjust window in x-direction
WindowMove	Push		A1
		move.l		ib_ActiveWindow(A6),A0
		jsr		(A1)
		move.w		D0,D1
		jsr		(A2)
		bne.S		1$
		tst.w		D1
		beq.S		2$
1$		Call		MoveWindow
2$		Pop		A1
		rts

DontMoveX
DontMoveY	moveq		#0,D0
Dummy		rts

* 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

* 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

* 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

* 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

* A0=window
TopIt		move.w		wd_TopEdge(A0),D0
		neg.w		D0
		rts

* A0=window
LeftIt		move.w		wd_LeftEdge(A0),D0
		neg.w		D0
		rts

HandlerSize	=		*-IHS
*====================== Input-handler end ===========================

*====================== Data-definition start =======================
 rStart
 rLong		CLen
 rAPtr		CName
 rAPtr		PProcess
 rAPtr		WBMsg
 rAPtr		DosBase
 rAPtr		IntBase
 rAPtr		IconBase
 rAPtr		DiskObj
 rAPtr		HPort
 rStorage	IReq,IOSTD_SIZE
 rStorage	IPort,MP_SIZE
 rAPtr		HMem
 rEnd

DosName		dc.b		'dos.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
		END

 --- InputEvent.ie_Class ---
IECLASS_NULL		0x00	A NOP input event
IECLASS_RAWKEY		0x01	A raw keycode from the keyboard device
IECLASS_RAWMOUSE	0x02	The raw mouse report from the game port device
IECLASS_EVENT		0x03	A private console event
IECLASS_POINTERPOS	0x04	A Pointer Position report
IECLASS_TIMER		0x06	A timer event
IECLASS_GADGETDOWN	0x07	select button pressed down over a Gadget (address in ie_EventAddress)
IECLASS_GADGETUP	0x08	select button released over the same Gadget (address in ie_EventAddress)
IECLASS_REQUESTER	0x09	some Requester activity has taken place.  See Codes REQCLEAR and REQSET
IECLASS_MENULIST	0x0A	this is a Menu Number transmission (Menu number is in ie_Code)
IECLASS_CLOSEWINDOW	0x0B	User has selected the active Window's Close Gadget
IECLASS_SIZEWINDOW	0x0C	this Window has a new size
IECLASS_REFRESHWINDOW	0x0D	the Window pointed to by ie_EventAddress needs to be refreshed
IECLASS_NEWPREFS	0x0E	new preferences are available
IECLASS_DISKREMOVED	0x0F	the disk has been removed
IECLASS_DISKINSERTED	0x10	the disk has been inserted
IECLASS_ACTIVEWINDOW	0x11	the window is about to be made active
IECLASS_INACTIVEWINDOW	0x12	the window is about to be made inactive
IECLASS_MAX		0x12	the last class

  --- InputEvent.ie_Code --- 
IECLASS_RAWKEY
IECODE_UP_PREFIX	0x80
IECODE_KEY_CODE_FIRST	0x00
IECODE_KEY_CODE_LAST	0x77
IECODE_COMM_CODE_FIRST	0x78
IECODE_COMM_CODE_LAST	0x7F
  
IECLASS_ANSI
IECODE_C0_FIRST		0x00
IECODE_C0_LAST		0x1F
IECODE_ASCII_FIRST	0x20
IECODE_ASCII_LAST	0x7E
IECODE_ASCII_DEL	0x7F
IECODE_C1_FIRST		0x80
IECODE_C1_LAST		0x9F
IECODE_LATIN1_FIRST	0xA0
IECODE_LATIN1_LAST	0xFF
  
IECLASS_RAWMOUSE
IECODE_LBUTTON		0x68	also uses IECODE_UP_PREFIX
IECODE_RBUTTON		0x69
IECODE_MBUTTON		0x6A
IECODE_NOBUTTON		0xFF
  
IECLASS_EVENT
IECODE_NEWACTIVE	0x01	active input window changed

IECLASS_REQUESTER Codes
REQSET is broadcast when the first Requester (not subsequent ones) opens
in the Window

IECODE_REQSET		0x01
REQCLEAR is broadcast when the last Requester clears out of the Window
IECODE_REQCLEAR		0x00

 --- InputEvent.ie_Qualifier ---
IEQUALIFIER_LSHIFT		0x0001
IEQUALIFIER_RSHIFT		0x0002
IEQUALIFIER_CAPSLOCK		0x0004
IEQUALIFIER_CONTROL		0x0008
IEQUALIFIER_LALT		0x0010
IEQUALIFIER_RALT		0x0020
IEQUALIFIER_LCOMMAND		0x0040
IEQUALIFIER_RCOMMAND		0x0080
IEQUALIFIER_NUMERICPAD		0x0100
IEQUALIFIER_REPEAT		0x0200
IEQUALIFIER_INTERRUPT		0x0400
IEQUALIFIER_MULTIBROADCAST	0x0800
IEQUALIFIER_MIDBUTTON		0x1000
IEQUALIFIER_RBUTTON		0x2000
IEQUALIFIER_LEFTBUTTON		0x4000
IEQUALIFIER_RELATIVEMOUSE	0x8000

IEQUALIFIERB_LSHIFT		0
IEQUALIFIERB_RSHIFT		1
IEQUALIFIERB_CAPSLOCK		2
IEQUALIFIERB_CONTROL		3
IEQUALIFIERB_LALT		4
IEQUALIFIERB_RALT		5
IEQUALIFIERB_LCOMMAND		6
IEQUALIFIERB_RCOMMAND		7
IEQUALIFIERB_NUMERICPAD		8
IEQUALIFIERB_REPEAT		9
IEQUALIFIERB_INTERRUPT		10
IEQUALIFIERB_MULTIBROADCAST	11
IEQUALIFIERB_MIDBUTTON		12
IEQUALIFIERB_RBUTTON		13
IEQUALIFIERB_LEFTBUTTON		14
IEQUALIFIERB_RELATIVEMOUSE	15
  
struct InputEvent
{
 struct  InputEvent *ie_NextEvent	;the chronologically next event
 UBYTE   ie_Class			;the input event class
 UBYTE   ie_SubClass			;optional subclass of the class
 UWORD   ie_Code			;the input event code
 UWORD   ie_Qualifier			;qualifiers in effect for the event
 union
  {
   struct
    {
     WORD    ie_x			;the pointer position for the event
     WORD    ie_y
    } ie_xy
   APTR    ie_addr
  } ie_position
  struct timeval ie_TimeStamp		;the system tick at the event
};

ie_X			=ie_position.ie_xy.ie_x
ie_Y			=ie_position.ie_xy.ie_y
ie_EventAddress		=ie_position.ie_addr

