;
; win.s
;
; Window event dispatching (TMA)
;
;  1994-1998 Straylight
;

;----- Licensing note -------------------------------------------------------
;
; This file is part of Straylight's Sapphire library.
;
; Sapphire is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2, or (at your option)
; any later version.
;
; Sapphire is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with Sapphire.  If not, write to the Free Software Foundation,
; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

;----- Standard header ------------------------------------------------------

		GET	libs:header
		GET	libs:swis

;----- External dependencies ------------------------------------------------

		GET	sapphire:sapphire
		GET	sapphire:suballoc
		GET	sapphire:event

;----- Main code ------------------------------------------------------------

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- win__addToList ---
;
; On entry:	R0 == window handle
;		R1 == pointer to routine to call
;		R2 == R10 value to call routine
;		R3 == R12 value to call routine with
;		R4 == pointer to list head to use
;
; On exit:	R0-R4 preserved
;
; Use:		Adds a rountine to the given list. Later added
;		routines are called first

win__addToList	ROUT

		STMFD	R13!,{R0-R4,R14}	;Stack some registers

		; --- Allocate a block ---

		MOV	R0,#list__size		;Size to allocate
		BL	sub_alloc		;Allocate the block
		BVS	%01			;Branch ahead if error

		; --- Fill the block in ---

		LDR	R1,[R4]			;Get the list head
		STR	R1,[R0,#list__next]	;Store in next field
		STR	R0,[R4]			;Store new block at head

		LDMIA	R13,{R1-R4}		;Get parameters
		STMIB	R0,{R1-R4}		;Store them in the block

		; --- And return to user ---

		LDMFD	R13!,{R0-R4,R14}	;Load back link
		BICS	PC,R14,#V_flag		;Return without error

		; --- Barf if an error occured ---

01		ADD	R13,R13,#4		;Skip over R0
		LDMFD	R13!,{R1-R4,R14}	;Branch if error
		ORRS	PC,R14,#V_flag		;Return with error

		LTORG

; --- win__removeFromList ---
;
; On entry:	R0 == window handle
;		R1 == pointer to routine to called
;		R2 == R10 value routine is called with
;		R3 == R12 value routine is called with
;		R4 == pointer to list head to use
;
; On exit:	All registers/flags preserved
;
; Use:		Removes a routine from the given list. All values are
;		compared.

win__removeFromList
		ROUT

		STMFD	R13!,{R0-R10,R12,R14}

		; --- Find the block ---

		MOV	R5,#0			;The previous pointer
		MOV	R12,R4			;Remember where the head is
		LDR	R4,[R4]			;Get the head of the list
01		TEQ	R4,#0			;Are we at the end?
		LDMEQFD	R13!,{R0-R10,R12,PC}^	;Yes -- return
		LDR	R9,[R4],#4		;Get the next pointer
		LDMIA	R4,{R6-R8,R10}		;Load data from the block
		CMP	R6,R0			;Are handles/R4 the same?
		CMPEQ	R7,R1			;Yes -- and routines to call?
		CMPEQ	R8,R2			;Yes -- R10 value?
		CMPEQ	R10,R3			;Yes -- R12 value?
		SUBNE	R5,R4,#4		;If no, remember previous
		MOVNE	R4,R9			;...get list pointer
		BNE	%01			;...and keep looking

		; --- So now the block has been found ---

		SUB	R0,R4,#4		;Put the block in R0
		MOV	R1,#list__size		;Get the size
		BL	sub_free		;Free the block
		CMP	R5,#0			;Was there a previous block
		STREQ	R9,[R12,#0]		;No -- store next blk in head
		STRNE	R9,[R5,#0]		;Yes -- store in prev next ^

		; --- And return to the user ---

		LDMFD	R13!,{R0-R10,R12,PC}^

		LTORG

; --- win_eventHandler ---
;
; On entry:	R0 == window handle
;		R1 == pointer to routine to call
;		R2 == R10 value to call routine with
;		R3 == R12 value to call routine with
;
; On exit:	May return an error
;
; Use:		Adds a routine to the event handler list. Later added
;		routines are called first. The event handing routine
;		must preserve all the registers, but may alter the carry
;		flag. If it returns with carry set, then no more event
;		handlers, OR post-filters, will be called.

		EXPORT	win_eventHandler
win_eventHandler
		ROUT


		STMFD	R13!,{R4,R9,R14}	;Save some registers

		; --- Be careful not to alter flags ---

		WSPACE	win__wSpace,R9		;Get my workspace pointer
		ADR	R4,win__eHandlers	;Get the event handlers
		BL	win__addToList		;Add the routine to the list
		LDMFD	R13!,{R4,R9,PC}		;Return cunningly

; --- win_removeEventHandler ---
;
; On entry:	R0 == window handle
;		R1 == pointer to routine called
;		R2 == R10 value routine is called with
;		R3 == R12 value routine is called with
;
; On exit:	--
;
; Use:		Removes a routine to the event handler list.

		EXPORT	win_removeEventHandler
win_removeEventHandler
		ROUT

		STMFD	R13!,{R4,R9,R14}	;Stack some registers
		WSPACE	win__wSpace,R9		;Get my workspace pointer
		ADR	R4,win__eHandlers	;Get the event handlers
		BL	win__removeFromList	;Remove routine from the list
		LDMFD	R13!,{R4,R9,PC}^	;Load registers

		LTORG

; --- win_swapWindow ---
;
; On entry:	R0 == old window handle
;		R1 == new window handle
;
; On exit:	--
;
; Use:		Searches for all the event handlers for window R0, and
;		changes the window handle for R1.  This is designed for
;		situations in wihich a window has been deleted and
;		recreated.

		EXPORT	win_swapWindow
win_swapWindow	ROUT

		STMFD	R13!,{R0-R3,R9,R14}	;Stack some registers
		WSPACE	win__wSpace,R9		;Get my workspace pointer
		LDR	R2,win__eHandlers	;Get my event handlers list
10		TEQ	R2,#0			;Are we at the end
		BEQ	%20win_swapWindow	;Yes -- jump ahead
		LDMIA	R2,{R3,R14}		;Load some information
		TEQ	R14,R0			;Are window handles the same
		STREQ	R1,[R2,#list__wHandle]	;Yes -- store new window hnd
		MOV	R2,R3			;Put next handler in R2
		B	%10win_swapWindow	;Try another handler
20		LDMFD	R13!,{R0-R3,R9,PC}^	;Return to caller

		LTORG

; --- win_windowDeleted ---
;
; On entry:	R0 == window handle
;
; On exit:	--
;
; Use:		Removes all the event handlers associated with the given
;		window handle.  It is intended to be used when a window
;		has been deleted.

		EXPORT	win_windowDeleted
win_windowDeleted ROUT

		STMFD	R13!,{R0-R5,R9,R14}	;Stack some registers
		WSPACE	win__wSpace,R9		;Get my workspace pointer

		; --- Find the block ---

		MOV	R5,#0			;The previous pointer
		LDR	R2,win__eHandlers	;Get the event handlers
01		TEQ	R2,#0			;Are we at the end?
		LDMEQFD	R13!,{R0-R5,R9,PC}^	;Yes -- return
		LDMIA	R2,{R3,R4}		;Get next/handle
		CMP	R4,R0			;Are handles the same?
		MOVNE	R5,R2			;If no, remember previous
		MOVNE	R2,R3			;...get list pointer
		BNE	%01win_windowDeleted	;...and keep looking

		; --- So now the block has been found ---

		MOV	R0,R2			;Put the block in R0
		MOV	R1,#list__size		;Get the size
		BL	sub_free		;Free the block
		CMP	R5,#0			;Was there a previous block
		ADREQ	R14,win__eHandlers	;No -- point to list head
		STREQ	R3,[R14,#0]		;No -- store next blk in head
		STRNE	R3,[R5,#0]		;Yes -- store in prev next ^

		; --- Now search for the next one ---

		MOV	R2,R3			;Put the next block in R2
		B	%01win_windowDeleted	;And keep on searching

		LTORG

; --- win_unknownHandler ---
;
; On entry:	R0 == pointer to routine to call
;		R1 == R4 value to call routine with
;		R2 == R10 value to call routine with
;		R3 == R12 value to call routine with
;
; On exit:	May return an error
;
; Use:		Adds a rountine to the event handler list. Later added
;		routines are called first. The event handing routine
;		must preserve all the registers, but may alter the carry
;		flag. If it returns with carry set, then no more event
;		handlers, OR post-filters, will be called.

		EXPORT	win_unknownHandler
win_unknownHandler
		ROUT


		STMFD	R13!,{R4,R9,R14}	;Save some registers

		; --- Be careful not to alter flags ---

		WSPACE	win__wSpace,R9		;Get my workspace pointer
		ADR	R4,win__uHandlers	;Get the unknowns list
		BL	win__addToList		;Add the routine to the list
		LDMFD	R13!,{R4,R9,PC}		;Return cunningly

; --- win_removeUnknownHandler ---
;
; On entry:	R0 == pointer to routine called
;		R1 == R4 value routine is called with
;		R2 == R10 value routine is called with
;		R3 == R12 value routine is called with
;
; On exit:	--
;
; Use:		Removes a routine to the unknown handler list.

		EXPORT	win_removeUnknownHandler
win_removeUnknownHandler
		ROUT

		STMFD	R13!,{R4,R9,R14}	;Stack some registers
		WSPACE	win__wSpace,R9		;Get my workspace pointer
		ADR	R4,win__uHandlers	;Get the event handlers
		BL	win__removeFromList	;Remove routine from the list
		LDMFD	R13!,{R4,R9,PC}^	;Load registers

		LTORG

; --- win__dispatchUnknown ---
;
; On entry:	R0  == reason code returned from Wimp_Poll
;		R1  == pointer to block
;		R12 == pointer to workspace
;
; On exit:	R0-R1 preserved
;		C Clear if the event is not claimed, Set otherwise

win__dispatchUnknown
		ROUT

		; --- Note ---
		;
		; At this point, we've already got R2-R5, R9, R10, R12
		; and R14 on the stack.

		; --- Go through the handlers list ---

02		ADDS	R0,R0,#0		;Clear carry flag
		LDR	R2,win__uHandlers	;Get my event handlers list
10		TEQ	R2,#0			;Are we at the end
		BEQ	%20                     ;Yes -- jump ahead
		LDMIA	R2,{R2-R4,R10,R12}	;Load parameters to pass
		MOV	R14,PC			;Set return address
		MOV	PC,R3			;Branch to handler
		BCC	%10			;Try next handler
20		LDMFD	R13!,{R2-R5,R9,R10,R14}	;Load registers
		ORRCSS	PC,R14,#C_flag		;... and return
		BICCCS	PC,R14,#C_flag

		LTORG

win__dispatchEvents
		ROUT

		STMFD	R13!,{R2-R5,R9,R10,R14}	;Stack some registers
		MOV	R9,R12			;Put my workspace in R9

		; --- Messages need special attention ---

		CMP	R0,#17			;Is this User_Message?
		CMPNE	R0,#18			;Or User_Message_Recorded?
		BEQ	%30			;Yes -- off we go then

		; --- If it's some weird kind of message ---

		CMP	R0,#13			;Highest event I know
		BGT	win__dispatchUnknown	;Too high -- unknown then

		; --- Find the table and get window handle offset ---

		ADR	R14,win__eventTbl	;Point to the table
		LDRB	R5,[R14,R0]		;Get offset for this event
		CMP	R5,#255			;Is this event `unknown'?
		BEQ	win__dispatchUnknown	;Yes -- call unknown list
		CMP	R5,#254			;Is it broadcastable?
		LDRNE	R5,[R1,R5]		;Get the window handle
		MOVEQ	R5,#&40000000		;Or set up for broadcast

		; --- Go through the handlers list ---

05		ADDS	R0,R0,#0		;Clear carry flag
		LDR	R2,win__eHandlers	;Get my event handlers list
10		TEQ	R2,#0			;Are we at the end
		BEQ	%20                     ;Yes -- jump ahead
		LDMIA	R2,{R2,R3,R4,R10,R12}	;Load parameters to pass
		TEQ	R5,R3			;Are window handles the same
		TEQNE	R5,#&40000000		;Or is this a broadcast?
		BNE	%10			;No -- try another handler
		MOV	R14,PC			;Set return address
		MOV	PC,R4			;Branch to handler
		BCC	%10			;Try next handler
20		LDMFD	R13!,{R2-R5,R9,R10,R14}	;Load the registers
		BICCCS	PC,R14,#C_flag		;Return with carry clear
		ORRCSS	PC,R14,#C_flag		;... or with carry set

		; --- Now try unknowns if I'm not claiming the message ---

30		LDR	R2,[R1,#16]		;Get message code
		ADR	R14,win__msgTbl		;Point to message table
00		LDMIA	R14!,{R3,R4}		;Load code and offset
		CMP	R2,R3			;Does the code match?
		LDREQ	R5,[R1,R4]		;Yes -- load window handle
		BEQ	%05			;And continue going
		CMP	R3,#-1			;Is this the end yet?
		BNE	%b00			;No -- keep going then

		; --- See if it's broadcastable ---

		ADR	R14,win__broadTbl	;Point to the table
00		LDR	R3,[R14],#4		;Load the message code
		CMP	R2,R3			;Do we have a match?
		MOVEQ	R5,#&40000000		;Yes -- signal broadcast
		BEQ	%05			;And dispatch appropriately
		CMP	R3,#-1			;Is this the end yet?
		BNE	%b00			;No -- keep on going

		B	win__dispatchUnknown	;Unknown event -- dispatch it

		DCB	0			;Hint_Received
win__eventTbl	DCB	255			;Null
		DCB	0			;Redraw
		DCB	0			;Open
		DCB	0			;Close
		DCB	0			;Leave window
		DCB	0			;Enter window
		DCB	12			;Mouse click
		DCB	255			;Drag event
		DCB	0			;Key press
		DCB	255			;Menu choice
		DCB	0			;Scroll request
		DCB	0			;Lose caret
		DCB	0			;Gain caret
		DCB	255			;Poll word nonzero
		ALIGN

win__msgTbl	DCD	&1,	20		;DataSave
		DCD	&3,	20		;DataLoad
		DCD	&502,	32		;HelpRequest
		DCD	&11,	20		;Dragging (drag'n'drop)
		DCD	-1,	-1

win__broadTbl	DCD	&9			;PaletteChange
		DCD	&400C1			;ModeChange
		DCD	&400CF			;FontChange
		DCD	-1

		LTORG

; --- win_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the win system.

		EXPORT	win_init
win_init	ROUT

		STMFD	R13!,{R0,R1,R9,R14}	;Stack some registers
		WSPACE	win__wSpace,R9		;Get my workspace

		; --- Are we already initialised? ---

		LDR	R0,win__flags		;Get my flags
		TST	R0,#win__INITED		;Are we initialised?
		LDMNEFD	R13!,{R0,R1,R9,PC}^	;Yes -- return

		ORR	R0,R0,#win__INITED	;Set initialised flag
		STR	R0,win__flags		;And store them back

		; --- Clear rest of workspace ---

		MOV	R0,#0			;Zero some registers
		STR	R0,win__eHandlers	;Clear event handlers list
		STR	R0,win__uHandlers	;Clear unknown list

		; --- Initialise event system ---

		BL	event_init		;Initialise event system

		; --- Set up a post filter for the win system ---

		ADR	R0,win__dispatchEvents	;Call this routine
		MOV	R1,R9			;Put my workspace in R12
		BL	event_postFilter	;Add it to post-filter list

		; --- That's it now ---

		LDMFD	R13!,{R0,R1,R9,PC}^	;Return

		LTORG

win__wSpace	DCD	0			;My workspace pointer

;----- Workspace ------------------------------------------------------------

		^	0,R9
win__wStart	#	0

win__flags	#	4			;Flags

win__INITED	EQU	(1<<0)			;I've been initialised

win__eHandlers	#	4			;Event handler list
win__uHandlers	#	4			;Unknown event handlers

win__wSize	EQU	{VAR}-win__wStart

; --- list structure ---

		^	0
list__next	#	4			;The next block
list__wHandle	#	4			;The window handle
list__proc	#	4			;Handler code
list__r10	#	4			;R12 to call with
list__r12	#	4			;R12 to call with

list__size	#	0

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	win__wSize		;Workspace size
		DCD	win__wSpace		;Workspace pointer
		DCD	0			;Scratchpad size
		DCD	win_init		;Initialisation code

;----- That's all, folks ----------------------------------------------------

		END
