;ClickToFront V1.1 Copyright 1987 Bryce Nesbitt.  Unlimited, revokable, free,
;non-exclusive licence hereby granted to any sentient being to use or abuse
;this code in any way whatsoever provided that this and any other copyright
;notices remain fully attached and are reproduced in any simultaneously
;distributed printed matter and with the exception that, without prior
;written permission, it not be utilized by any entity that has been commonly
;referred to as Robert W. Skyles, Skyles Electric Works, Jim Drew, Regie
;Warren or any organization founded by, controlled, employing or profiting
;any such entity, it's offspring or spouses.  FISH use ok.  Author
;correspondence, bug or stupidity reports may be directed to:
;	1712 Marin Ave.
;	Berkeley, Ca 94707-2902
;	bryce@cogsci.berkeley.EDU -or- ucbvax!cogsci!bryce
;"(almost) as seen in Transactor magazine".
;
;USAGE
;	CLI - ClickToFront
;	WB  - Brings up requester, choose Install or Cancel
;
;	A click into any part of any window works as normal.
;	A double click brings it to the front.	How handy!
;
;	!!! In order to prevent false activation, lower the !!!
;	!!! double-click sensitvity from preferences.	    !!!
;
;BUGS
;   No provisions for un-install.  The only way I can think to do this would
;be to add a handler at priority 52, then search the chain downward for a
;handler with name ClickToFront.  This seems like a kludge.
;   This program is NOT Intuition.  This makes certain things more difficult.
;A test should be made that both clicks are over the same window.  If some,
;kind Intuition() hacker has a way...
;   No longer brings screens to the front.
;   Proper size when assembled with Metacomco and Blink'ed with NODEBUG
;   is 956 bytes.
;
**************
	NOLIST
	INCLUDE 'exec/types.i'
	INCLUDE 'exec/memory.i'
	INCLUDE 'exec/interrupts.i'
	INCLUDE 'exec/io.i'
;	INCLUDE 'exec/tasks.i'
ThisTask	EQU $114
	INCLUDE 'libraries/dosextens.i'
	INCLUDE 'devices/inputevent.i'
	INCLUDE 'devices/input.i'
;	INCLUDE 'intuition/intuitionbase.i'
ib_ActiveWindow EQU $34
ib_ActiveScreen EQU $38
ib_FirstScreen	EQU $3C
;
;What follows is a non-standard extension that eliminates the need to link
;with the HUGE amiga.lib file.
;
	;INCLUDE 'lib/exec_lib.i'
	;INCLUDE 'lib/intuition_lib.i'
	LIST
jsrlib	macro
 xref _LVO\1
 jsr  _LVO\1(a6)
 endm
jmplib	macro
 xref _LVO\1
 jmp  _LVO\1(a6)
 endm
********************
		CODE
startup:	move.l	4,a6
		move.l	ThisTask(a6),a5
		moveq	#0,d0		;Set zero for later
		move.l	pr_CLI(a5),d1	;Pointer to CLI only structure
		bne.s	fromCLI 	;If not zero, then save a zero...
;
; If called from Workbench a message will be sent.  This waits for it,
;  and saves it's pointer to be returned to Workbench later.
;
		lea.l	pr_MsgPort(a5),a0
		jsrlib	WaitPort
		lea.l	pr_MsgPort(a5),a0
		jsrlib	GetMsg		;Message pointer in D0
fromCLI 	move.l	d0,-(a7)	;Save message for later...
******************************** [A6=ExecBase][a5=this task]
failcode	equr	d7
Ibase		equr	d6
portsave	equr	a5
IOReqsave	equr	a4

		moveq	#20,failcode	;Default code
;---Prepare StdIO---
		bsr	_CreatePortE	;[d0=Port]
		beq	ExitToDOS
		move.l	d0,portsave
		bsr	_CreateStdIOE	;[a1=IoRequest]
		beq	e_StdIO
		move.l	a1,IOReqsave
;---Open input.device---
		moveq	#0,d0		;Unit
		move.l	d0,d1		;Flags
		lea.l	devname(pc),a0
		;[a1-IoRequest]
		jsrlib	OpenDevice	;[d0=zero if ok]
		tst.l	d0
		bne	e_Open
;---Open Intuition---
;Intuition will be opened, and the pointer given to the interrupt code
; Since the interrupt will still be installed after this task exits,
; intuition will be left open.
;
		lea.l	IntuiName(pc),a1
		jsrlib	OldOpenLibrary	;V1.0 Compatible :-)
		move.l	d0,Ibase	;(d)
		beq	e_Intui
		tst.l	(a7)		;check CLI flag on stack
		beq.s	DoIt		;always do it from CLI

		move.l	Ibase,a6
		suba.l	a0,a0
		lea.l	IText1(pc),a1
		lea.l	IText2(pc),a2
		lea.l	IText3(pc),a3
		moveq	#0,d0
		moveq	#0,d1
		move.l	#560,d2
		moveq	#68,d3
		jsrlib	AutoRequest
		move.l	4,a6
		tst.l	d0		;grumble, gritch, grrr...
		bne.s	DoIt
;---User said no, so pack our bags and go home--
		move.l	Ibase,a1
		jsrlib	CloseLibrary
		bra.s	Cancel
;---Copy Handler---
DoIt		move.l	#DownEnd-Start,d0
		move.l	#MEMF_PUBLIC+MEMF_CLEAR,d1
		jsrlib	AllocMem
		move.l	d0,a2		;location of block
		move.l	d0,a1
		tst.l	d0
		beq.s	e_nomem

		lea.l	Start(pc),a0
		moveq	#((DownEnd-Start)/4)-1,d1 ;Length in LONGS-1
copylp		move.l	(a0)+,(a1)+ ;Copy handler to Public Memory
		dbra	d1,copylp   ;68010 Loop mode!

;---Prepare IOReq with proper addresses---
		lea.l	Handlercode-Start(a2),a0 ;offset+memblock
		move.l	a0,IS_DATA(a2)		 ;unused
		move.l	a0,IS_CODE(a2)		 ;where code is
		lea.l	PopName-Start(a2),a0	 ;set ascii name
		move.l	a0,LN_NAME(a2)
		move.l	Ibase,IBASE1+2-Start(a2) ;modify code
		move.l	Ibase,IBASE2+2-Start(a2) ;modify code

;---Send ADDHANDLER---
		move.l	IOReqsave,a1
		move.w	#IND_ADDHANDLER,IO_COMMAND(a1)
		move.l	a2,IO_DATA(a1)
		;(IOReq in a1)
		jsrlib	DoIO		;d0=(IOReq-a0)
		tst.l	d0
		bne.s	e_DoIO	;bad<>0.  Forget the memory...

Cancel		moveq	#0,d7
e_nomem
e_DoIO
e_Intui 	move.l	IOReqsave,a1
		jsrlib	CloseDevice
e_Open		move.l	IOReqsave,a1
		bsr	_DeleteStdIOE
e_StdIO 	move.l	portsave,a1
		bsr	_DeletePortE
		tst.l	failcode
		beq.s	ExitToDOS

;-- There can be only one reason why we failed (knock on formica) --
;-- and that is lack of memory.  Unless you have a better idea... --

		move.l	ThisTask(a6),a1
		moveq	#ERROR_NO_FREE_STORE,d0
		move.l	d0,pr_Result2(a1)

******************************* (a7)+=message d7=return code a6=exec
ExitToDOS:	move.l	(a7)+,d2
		beq.s	NotWB	;If saved pointer is zero, exit to CLI...
;
; Return the startup message to the parent Workbench tool. The forbid
; is needed so workbench can't UnLoadSeg() the code too early.
;
		jsrlib	Forbid
		move.l	d2,a1		;message pointer
		jsrlib	ReplyMsg
NotWB		move.l	failcode,d0	;Set "failat" code
		rts

;**** exec_support/CreatePort ***
;
; port=_CreatePort()
;  d0
;
;FUNCTION:  Create a nameless message port with a 0 priority.
; (exec/ports.i)
;RESULT: The port pointer or Z=1 if an error occured.
;REGISTERS: a6 is destroyed unless _CreatePortE is called,
; in which case a6 must contain ExecBase.
;EXAMPLE:
;		moveq	#3,d0	;Set Priority
;		jsr	_CreatePort
;		beq.s	noport	;Not enough memory or signals
;
		;xref	_CreatePort
		;xref	_CreatePortE
;_CreatePort	move.l	4,a6
_CreatePortE:	move.l	a2,-(a7)
		move.l	#MEMF_PUBLIC+MEMF_CLEAR,d1
		moveq	#MP_SIZE,d0
		jsrlib	AllocMem
		move.l	d0,a2
		tst.l	d0
		beq.s	cp_nomemory
		moveq	#-1,d0
		jsrlib	AllocSignal	;d0=return
		moveq	#-1,d1
		cmp.l	d0,d1	;-1 indicates bad signal
		bne.s	cp_sigok
		move.l	a2,a1
		moveq	#MP_SIZE,d0
		jsrlib	FreeMem
cp_nomemory	moveq	#0,d0	;set z=1
		bra.s	cp_xit

cp_sigok	move.b	d0,MP_SIGBIT(a2)
		move.b	#PA_SIGNAL,MP_FLAGS(a2)
		move.b	#NT_MSGPORT,LN_TYPE(a2)
		clr.b	LN_PRI(a2)
		move.l	ThisTask(a6),MP_SIGTASK(a2)
		lea.l	MP_MSGLIST(a2),a0  ;Point to list header
		NEWLIST a0		;Init new list macro
		move.l	a2,d0
cp_xit		move.l	(a7)+,a2	;cc's NOT affected
		rts

;**** exec_support/DeletePort ***
;
;_DeletePort(port)
;	     a1
;
;FUNCTION:  Deletes the port by first setting some
; fields to illegal values then calling FreeMem.
;RESULT: none
;REGISTERS: a6 is destroyed unless _DeleteStdIOE is called,
; in which case a6 must contain ExecBase.
;
		;xref	_DeletePort
		;xref	_DeletePortE
;_DeletePort	move.l	4,a6
_DeletePortE:	move.l	a1,-(a7)
		moveq	#-1,d0
		move.b	d0,LN_TYPE(a1)
		move.l	d0,MP_MSGLIST+LH_HEAD(a1)
		moveq	#0,d0	;Clear upper 3/4 of d0
		move.b	MP_SIGBIT(a1),d0
		jsrlib	FreeSignal
		move.l	(a7)+,a1
		moveq	#MP_SIZE,d0
		jmplib	FreeMem
;
;ioStdReq=CreateStdIO(ioReplyPort)
; a1			d0
;
;Function: Allocate a IoRequest block of standard size.
;Example:	move.l	MyPort,d0
;		jsr	_CreateStdIO
;		beq.s	badnews
;
		;xref	_CreateStdIOE
		;xref	_CreateStdIO
;_CreateStdIO	move.l	4,a6
_CreateStdIOE:	move.l	d0,-(a7)
		moveq	#IOSTD_SIZE,d0
		move.l	#MEMF_PUBLIC+MEMF_CLEAR,d1
		jsrlib	AllocMem
		move.l	d0,a1
		tst.l	d0
		beq.s	nomem
		move.b	#NT_MESSAGE,LN_TYPE(a1)
		move.l	(a7)+,MN_REPLYPORT(a1)
nomem		rts

;*** exec_support/DeleteStdIO ***
;
;_DeleteStdIO(ioStdReq)
;		a1
;
;FUNCTION:  Deletes an ioStdReq by setting some
; fields to illegal values then calling FreeMem.
;RESULT: none
;REGISTERS: a6 is destroyed unless _DeleteStdIOE is called,
; in which case a6 must contain ExecBase.
;
		;xref	_DeleteStdIO
		;xref	_DeleteStdIOE
;_DeleteStdIO	move.l	4,a6	;Get ExecBase
_DeleteStdIOE:	moveq	#-1,d0	;Set fields to illegal value
		move.b	d0,LN_TYPE(a1)
		move.l	d0,IO_DEVICE(a1)
		move.l	d0,IO_UNIT(a1)
		moveq	#IOSTD_SIZE,d0
		jmplib	FreeMem

IText1: dc.b	0,1,1,0 	;pens, drawmode and filler
	dc.w	20,6		;XY
	dc.l	0		;NULL for default font
	dc.l	ITextText1	;pointer to text
	dc.l	IText1b 	;next IntuitText structure
IText1b dc.b	0,1,1,0
	dc.w	20,17
	dc.l	0
	dc.l	ITextText1b
	dc.l	0

IText2: dc.b	0,1,1,0
	dc.w	6,3
	dc.l	0
	dc.l	ITextText2
	dc.l	0
IText3: dc.b	0,1,1,0
	dc.w	6,3
	dc.l	0
	dc.l	ITextText3
	dc.l	0

ITextText1:	dc.b 'ClickToFront installer v1.1.   '
		dc.b $a9,'1987 Bryce Nesbitt',0
ITextText1b	dc.b 'Double-clicking will bring '
ITextText1c	dc.b 'any window to the front',0
ITextText2:	dc.b 'Install',0
ITextText3:	dc.b 'Cancel',0
IntuiName:	dc.b 'intuition.library',0
devname:	dc.b 'input.device',0

;-------------------------------
		cnop	0,4	;LONG word align
Start:
ints:	;This is an interrupt structure (exec/interrupts.i)
	;since it will be referenced by an interrupt it MUST be
	;copied into PUBLIC memory.  (All memory that will be used
	;by interrupts or other tasks must be PUBLIC.)
	dc.l 0		;succ
	dc.l 0		;pred
	dc.b 0		;type
	dc.b 51 	;Priority (One step higher than Intuition)
	dc.l 0		;Name
	dc.l 0		;Data
	dc.l 0		;Code
inte

;When the handler is entered a0 will point to a linked list of input
; events of the type defined in the incude file (devices/inputevent.i)
; When done mangling the input stream it returns a new pointer in d0
;Note: All references to (a0) below refer to ie_NextEvent(a0).	The
; offeset equates to zero and was eliminated for effeciency.

Handlercode:	move.l	a0,d0		;Save pointer to start of chain
		cmpi.w	#$0200,ie_Class(a0)

;Compare class AND subclass. $0200 is a combination of IECLASS_RAWMOUSE for
;ie_Class and IECLASS_NULL for ie_SubClass (it checks two BYTEs at once).

		beq.s	hurdle1
		move.l	(a0),d1 	;Get {ie_NextEvent} pointer
		bne.s	MoreEvents	;If any...
		rts			;exit (with no changes made)

hurdle1 	move.l	ie_Code(a0),d1	;Code & Qualifier.
		andi.w	#%0011000000000000,d1
;
;Mask off the bits of the qualifier field that we do not care about.  In this
; case that's all but the RIGHT and MIDDLE buttons, which may not be down.

		cmpi.l	#$00680000,d1
;
;The upper word of this comparison ($0068) is the RAWMOUSE code for LBUTTON
; the lower is the match for the qualifier.

		beq.s	CheckClick
KeepLooking	move.l	(a0),d1 	;See above...
		bne.s	MoreEvents	;See above...
		rts			;exit (no changes)
;
;MoreEvents handles the case where several events may be linked and
; can extract one of them from the middle.

MoreEvents	;move.l  a0,a1		 ;Temporary for unlinking
		move.l	d1,a0		;Look here next...
		cmpi.w	#$0200,ie_Class(a0)
		bne.s	KeepLooking
		move.l	ie_Code(a0),d1
		andi.w	#%0011000000000000,d1
		cmpi.l	#$00680000,d1
		bne.s	KeepLooking
;
;Don't unlink the event, that would cause problems.
;
CheckClick	movem.l a6/a1/d3/d2/d0,-(a7)
		move.l	a0,-(a7)
IBASE2		move.l	#-1,a6		;Self-modifying-> Intuitionbase
		move.l	TimeSave+0(pc),d0
		move.l	TimeSave+4(pc),d1
		move.l	ie_TimeStamp+0(a0),d2
		move.l	ie_TimeStamp+4(a0),d3
		jsrlib	DoubleClick
		move.l	(a7)+,a0
		lea.l	TimeSave+0(pc),a1
		move.l	ie_TimeStamp+0(a0),(a1)+
		move.l	ie_TimeStamp+4(a0),(a1)+
		tst.l	d0
		movem.l (a7)+,d0/d2/d3/a1/a6	;cc's not
		beq.s	KeepLooking

		;[d0-short input]

;The pointer to the currently active Screen and Windows are available at a
; positive offset from the Intuition base pointer.  These will be brought
; to the front.  Note that it may not be proper under the Amiga system to
; do this in the actual handler.  It may be safer to have another task
; lying around in the background to do the actual work.  The description
; of the Intuition WindowToFront commands indicates that it will not take
; effect until the NEXT input event, which (of course) will not happen
; until this one exits.  Now, if Intuition was a device...
;
tofront:	movem.l a6/d0,-(a7)
IBASE1		move.l	#-1,a6		;Self-modifying-> Intuitionbase
		moveq	#0,d0
		jsrlib	LockIBase	;Lock before reading any private
		move.l	d0,-(a7)	;Intuition structures

	;move.l  ib_ActiveScreen(a6),d0      ;Uncomment to bring screen
	;beq.s	 iinoscreen		     ;to front also
	;move.l  d0,a0
	;jsrlib  ScreenToFront

		;move.l  ib_ActiveScreen(a6),d0     ;Garbage, forget it
		;beq.s	 inoscreen
		;move.l  d0,a0
		;lea.l	 MouseYX(pc),a1
		;move.l  sc_MouseX(a0),d1
		;cmp.l	 d1,(a1)
		;bne.s	 imousemoved	 ;The mouse moved!! Don't do it!!
		;move.l  d1,(a1)
		;jsrlib  ScreenToFront

iinoscreen	move.l	ib_ActiveWindow(a6),d0
		beq.s	inowindow
		move.l	d0,a0
		jsrlib	WindowToFront
inowindow	move.l	(a7)+,a0
		jsrlib	UnlockIBase
		movem.l (a7)+,d0/a6	;d0 contains output pointer
		rts

imousemoved	;move.l  d1,(a1)	 ;Set last position
inoscreen	;move.l  (a7)+,a0
		;jsrlib  UnlockIBase
		;movem.l (a7)+,d0/a6
		;rts	;don't bother with remaining chained events

PopName:	dc.b	'ClickToFront',0
		cnop	0,4	;Pad out to Longword boundary
TimeSave:	dc.l	0,0	;longs on longs retain long
;MouseYX	 dc.l	 0
DownEnd
		END
