* --------------------------------------------------------------------- *
* bag.a -- SetFunction ActivateGadget for more reliable performance.
* Public Domain Freeware source by Justin V. McCormick. 89-09-26
* --------------------------------------------------------------------- *
	 IFD	CAPE
	EXEOBJ
	OPTIMON
	SMALLOBJ
	 ENDC

	include "exec/types.i"
	include "exec/memory.i"
	include	"libraries/dos.i"
	include "libraries/dosextens.i"

* --------------------------------------------------------------------- *
* Equates
* --------------------------------------------------------------------- *
BWDELAY	EQU	100000	; The busy-wait delay constant,
			; can be tuned for your CPU speed.

* --------------------------------------------------------------------- *
* Macros
* --------------------------------------------------------------------- *
SYS	MACRO		; Call a Library function, load A6 conditionally
	 IFGT	NARG-2
	FAIL	!!!
	 ENDC
	 IFEQ	NARG-2
	MOVE.L	\2,a6
	 ENDC
	JSR	_LVO\1(a6)
	ENDM

XLVO	MACRO		; Declare a LVO offset from amiga.lib
	XREF	_LVO\1
	ENDM

* --------------------------------------------------------------------- *
* Library offsets
* --------------------------------------------------------------------- *
	 IFD	NOTEXECOBJ
	XLVO	ActivateGadget
	XLVO	AllocMem
	XLVO	CloseLibrary
	XLVO	CopyMem
	XLVO	FindTask
	XLVO	Forbid
	XLVO	FreeMem
	XLVO	GetMsg
	XLVO	OpenLibrary
	XLVO	Output
	XLVO	ReplyMsg
	XLVO	SetFunction
	XLVO	WaitPort
	XLVO	Write
	 ELSE
_LVOActivateGadget	EQU	-$01ce
_LVOAllocMem		EQU	-$00c6
_LVOCloseLibrary	EQU	-$019e
_LVOCopyMem		EQU	-$0270
_LVOFindTask		EQU	-$0126
_LVOForbid		EQU	-$0084
_LVOFreeMem		EQU	-$00d2
_LVOGetMsg		EQU	-$0174
_LVOOpenLibrary		EQU	-$0198
_LVOOutput		EQU	-$003c
_LVOReplyMsg		EQU	-$017a
_LVOSetFunction		EQU	-$01a4
_LVOWaitPort		EQU	-$0180
_LVOWrite		EQU	-$0030
	 ENDC

	SECTION	CODE
* --------------------------------------------------------------------- *
* EXECUTION STARTS HERE
* --------------------------------------------------------------------- *
launch:
	lea	_DT(pc),a4
	movea.l	4,a6
	move.l	a6,_SysBase-_DT(a4)
	suba.l	a1,a1			;Find our own task pointer
	SYS	FindTask		;d0 = FindTask (0L)
	movea.l	d0,a2			;Save it for later

* Check whether launched from CLI or WBench, handle appropriately
	moveq	#0,d7			;Clear out CLI flag/WBench msg pointer
	tst.l	pr_CLI(a2)		;Is there a CLI out there?
	 bne.s	cliargs			;Yep, no message to wait for...

* Must be from WBench, wait for startup message, keep msg as key for removal
	lea	pr_MsgPort(a2),a0
	SYS	WaitPort		;Wait for startup message
	lea	pr_MsgPort(a2),a0
	SYS	GetMsg			;Grab Message, pointer in D0
	move.l	d0,d7			;Save message pointer for ReplyMsg later

cliargs:
	lea	_DOSName(pc),a1		;dos.library string pointer
	moveq	#0,d0			;Take any version of DOS
	SYS	OpenLibrary		;OpenLibrary("dos.library", 0L)
	move.l	d0,_DOSBase-_DT(a4)	;Save DOSBase

	lea	_INTUIName(pc),a1	;intuition.library string pointer
	moveq	#33,d0			;Take any version of Intuition after 1.2
	SYS	OpenLibrary		;OpenLibrary("intuition.library", 33L)
	move.l	d0,_IntuitionBase-_DT(a4) ;Save IntuitionBase
	 beq	cleanup2		;Oops, 1.0 or 1.1 user out there still?

* Print credits
	lea	_creditmsg(pc),a0
	move.l	a0,d2
	move.l	#_creditmsgend-_creditmsg,d3
	jsr	writemsg

* See if our function is already installed
	movea.l	_IntuitionBase-_DT(a4),a0	;Grab IntuitionBase
	movea.l	_LVOActivateGadget+2(a0),a0	;Grab vector to current ActivateGadget()
	lea	MyActivateGadget(pc),a1		;Grab address of our function
	moveq	#OldFunc-MyActivateGadget,d1	;Length of our function to compare
1$	cmpm.b	(a0)+,(a1)+			;Same code?
	dbne	d1,1$				;Yep, compare next byte

* Install or Remove the vector?
	bne.s	installbag		;Not our function, install ours

removebag:
	movea.l	_IntuitionBase-_DT(a4),a1	;Grab IBase
	move.w	#_LVOActivateGadget,a0		;The Vector
	movea.l	_LVOActivateGadget+2(a1),a2	;Grab vector to current ActivateGadget()
	move.l	OldFunc-MyActivateGadget(a2),d0	;The original vector to restore
	SYS	SetFunction,_SysBase-_DT(a4)	;SetFunction(IntuitionBase, LVOActivateGadget, oldfunc)
	movea.l	d0,a1				;Move old function pointer to A1
	moveq	#MyActivateGadgetEnd-MyActivateGadget,d0 ;Size of function
	SYS	FreeMem				;FreeMem(oldfunc, sizeof(oldfunc))

	lea	_removedmsg(pc),a0
	move.l	a0,d2				;d2 = removed message
	move.l	#_removedmsgend-_removedmsg,d3	;d3 = strlen(msg)
	jsr	writemsg
	bra.s	cleanup

installbag:
	moveq	#0,d1
	moveq	#MyActivateGadgetEnd-MyActivateGadget,d0
	SYS	AllocMem,_SysBase-_DT(a4)	;AllocMem(sizeof(newfunc), 0L)
	tst.l	d0
	 beq.s	cleanup				;Oops, no memory?

* Copy the replacement function to the newly allocated memory
	movea.l	d0,a2				;Copy mem pointer
	lea	MyActivateGadget(pc),a0
	movea.l	a2,a1
	move.l	#MyActivateGadgetEnd-MyActivateGadget,d0
	SYS	CopyMem

* Revector the library, save old function address in replacement block
	movea.l	_IntuitionBase-_DT(a4),a1
	move.w	#_LVOActivateGadget,a0
	move.l	a2,d0
	SYS	SetFunction			;SetFunction(IntuitionBase, LVOActivateGadget, newfunc)
	move.l	d0,OldFunc-MyActivateGadget(a2) ;Save old ActivateGadget vector

	lea	_installedmsg(pc),a0
	move.l	a0,d2					;d2 = installed message
	move.l	#_installedmsgend-_installedmsg,d3	;d3 = strlen(msg)
	jsr	writemsg

* Cleanup and exit parent segment
cleanup:
	movea.l	_IntuitionBase-_DT(a4),a1
	SYS	CloseLibrary,_SysBase-_DT(a4) ;CloseLibrary(IntuitionBase)

cleanup2:
	movea.l	_DOSBase-_DT(a4),a1
	SYS	CloseLibrary		;CloseLibrary(DOSBase)

	tst.l	d7			;Were we called from WBench?
	 beq.s	exit			;from CLI - don't signal WBench
	SYS	Forbid			;Prevent WBench from UnloadSeg'ing
	movea.l	d7,a1			;Grab saved message pointer
	SYS	ReplyMsg		;Let WBench task know we're done

exit:
	rts

* --------------------------------------------------------------------- *
writemsg:
	SYS	Output,_DOSBase-_DT(a4)	;Get stdout handle
	move.l	d0,d1			;Is there a console to write to?
	 beq.s	exit			;Nope, blow it off
	jmp	_LVOWrite(a6)		;Write(stdout, msg, strlen(msg))

* --------------------------------------------------------------------- *
* Calls ActivateGadget() up to five times before giving up.
* Adjust BWDELAY for time delay between attempts.
* --------------------------------------------------------------------- *
	XDEF	MyActivateGadget
MyActivateGadget:
	movem.l	d2-d3/a2-a3,-(sp)	;Save affected regs

	movea.l	OldFunc(pc),a3		;Grab original vector
	movem.l	a0-a2,-(sp)		;Push parameters onto stack

	moveq	#4,d2			;Try 5 times
1$	movem.l	(sp),a0-a2
	jsr	(a3)			;Call ActivateGadget(gad, win, req)
					;		     a0   a1   a2
	tst.w	d0			;Success?
	 bne.s	3$			;Yep, done here!
	move.l	#BWDELAY,d3		;Kill some time
2$	subq.l	#1,d3
	 bgt	2$
	dbf	d2,1$			;Try again

3$	lea	12(sp),sp		;Pop stack parms a0,a1,a2

	movem.l	(sp)+,d2-d3/a2-a3	;Restore affected regs
	rts
OldFunc:
	dc.l	0			;Pointer to original function
MyActivateGadgetEnd:

* --------------------------------------------------------------------- *
* PC Relative data
* --------------------------------------------------------------------- *
_DT:
_SysBase:	dc.l	0
_DOSBase:	dc.l	0
_IntuitionBase:	dc.l	0
_DOSName:	DOSNAME
_INTUIName:	dc.b	'intuition.library',$00

_creditmsg:	dc.b	'BAG 1.0 by Justin McCormick - '
		dc.b	'BetterActivateGadget '
_creditmsgend:
_installedmsg:	dc.b	'Installed',$0a
_installedmsgend:
_removedmsg:	dc.b	'Removed',$0a
_removedmsgend:
* ------------------------------------------------------------------------- *
	END
