; ; repeater.s ; ; Handles things that should auto-repeat ; ; © 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 GET libs:stream ;----- External dependencies ------------------------------------------------ GET sapphire:divide GET sapphire:idle GET sapphire:sapphire GET sapphire:win ;----- Main code ------------------------------------------------------------ AREA |Sapphire$$Code|,CODE,READONLY ; --- repeater --- ; ; On entry: R0 == pointer to routine to call ; R1 == R10 value to pass to routine ; R2 == R12 value to pass to routine ; ; On exit: -- ; ; Use: Calls a routine (a) immediately, (b) after the configured ; keyboard delay rate and (c) repeatedly after the configured ; keyboard repeat rate. Calls stop when the user stops ; pressing the mouse button. ; ; The routine is called with R0 containing either the number ; of missed calls since the last one (normally this is 1) -- ; this is intended to be used to implement a kind of buffering ; of repeats if the operation being performed is a lengthy one ; -- and with 0 to indicate that the operation is now ; completed. EXPORT repeater repeater ROUT STMFD R13!,{R0-R4,R12,R14} ;Save some registers WSPACE rpt__wSpace ;Locate my workspace nicely ; --- Set up the workspace --- STMIA R12,{R0-R2} ;Save the call information MOV R0,#1 ;This is the first call BL rpt__call ;Call the user's routine ; --- Now we have to work out the repeat times --- MOV R0,#196 ;Read keybd repeat and delay MOV R1,#0 ;Set up registers to read... MOV R2,#255 ;... without corrupting SWI OS_Byte ;Read the autorepeat info STR R2,rpt__repeat ;Save repeat rate in wspace MOVS R4,R1 ;Look after delay time MOVEQ R0,#0 ;If no delay, end op now BLEQ rpt__call ;Tell client it's over BEQ %90repeater ;And exit nicely ; --- Set up the drag box --- ; ; We do this for several reasons: ; ; * It informs us when the mouse button is finally released. ; ; * It keeps the mouse pointer bounded for the duration. ; ; * It stops things going wrong if for some reason the mouse ; gets unbounded (e.g. by doing a Wimp_SetMode). SUB R13,R13,#56 ;Get a drag box block MOV R1,R13 ;Point to the block SWI Wimp_GetPointerInfo ;Get the current mouse state LDMIA R13,{R2,R3} ;Read mouse position MOV R1,#7 ;This drag is a user type STMIA R13,{R0,R1} ;Save them in the block ADD R14,R13,#24 ;Point to parent rectangle STMIA R14!,{R2,R3} ;Save the current mouse... STMIA R14!,{R2,R3} ;... posn to lock in place MOV R1,R13 ;Point to the block SWI Wimp_DragBox ;Start the drag operation ADD R13,R13,#56 ;Restore the stack now ; --- Now set up the unknown handler to catch the drag --- ADR R0,rpt__ukEvents ;Point to my handler routine MOV R1,#0 ;Don't care about R4 MOV R2,#0 ;Nothing to pass in R10 MOV R3,R12 ;Pass workspace in R12 BL win_unknownHandler ;Add the handler BVS %90repeater ;If it failed, skip to end ; --- Set up the alarm for the first repeat --- SWI OS_ReadMonotonicTime ;Read the current time ADD R0,R4,R0 ;Add time to keyboard delay STR R0,rpt__alarm ;Store the time of the alarm STR R0,rpt__last ;And this is correct time ADR R1,rpt__alarms ;Point to my alarm handler MOV R2,#0 ;Nothing to pass in R10 MOV R3,R12 ;Pass workspace in R12 BL idle_setAlarm ;Set the alarm call then 90repeater LDMFD R13!,{R0-R4,R12,PC}^ ;Return to caller if all OK LTORG ; --- rpt__call --- ; ; On entry: R0 == value to pass client in R0 ; ; On exit: -- ; ; Use: Calls client routine. rpt__call ROUT STMFD R13!,{R1,R10,R12,R14} ;Save some registers LDMIA R12,{R1,R10,R12} ;Load client's routine data MOV R14,PC ;Set up return address MOV PC,R1 ;And call the routine LDMFD R13!,{R1,R10,R12,PC}^ ;And return to caller LTORG ; --- rpt__alarms --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Handles alarms while a repeater is in operation. rpt__alarms ROUT STMFD R13!,{R0-R4,R14} ;Save some registers LDR R0,rpt__routine ;Load the current addr CMP R0,#-1 ;Have we ended? BEQ %70rpt__alarms ;Yes -- do special things ; --- Things go odd if zero repeat rate set up --- LDR R2,rpt__repeat ;Load current repeat rate CMP R2,#0 ;Is it currently zero? BEQ %50rpt__alarms ;Yes -- handle specially ; --- Work out how many we missed --- ; ; We also add in the next alarm, and resynch to the ; expected time. SWI OS_ReadMonotonicTime ;Get the current time MOV R4,R0 ;Look after this value LDR R3,rpt__last ;Get the time I was expecting SUB R0,R4,R3 ;Find how late I was called MOV R1,R2 ;Get the repeat rate ready BL divide ;Find out how many I missed ADD R0,R0,#1 ;Add in one extra repeat BL rpt__call ;Call client code again SUB R14,R2,R1 ;Work out next repeat time ADD R0,R4,R14 ;Add that to current time STR R0,rpt__last ;This is the new alarm time SWI OS_ReadMonotonicTime ;Get the current time MOV R4,R0 ;Look after this now SUB R0,R4,R3 ;Find how late I was called MOV R1,R2 ;Get the repeat rate ready BL divide ;Find out how many I missed SUB R14,R2,R1 ;Work out next repeat time ADD R0,R4,R14 ;Add that to current time STR R0,rpt__alarm ;This is the new alarm time ADR R1,rpt__alarms ;Point to me again MOV R2,R10 ;Pass the dialogue handle MOV R3,R12 ;And my workspace pointer BL idle_setAlarm ;Add in next alarm handler LDMFD R13!,{R0-R4,PC}^ ;And return to caller ; --- If no repeat rate, stop the whole operation --- 50rpt__alarms MOV R0,#1 ;Just send one call BL rpt__call ;Send that off to the client MOV R1,#-1 ;Tell Wimp to stop dragging SWI Wimp_DragBox ;Send that off nicely ADR R0,rpt__ukEvents ;Point to the unknown handler MOV R1,#0 ;I'm passing 0 in R4 MOV R2,#0 ;And 0 in R10 too MOV R3,R12 ;But R12 is my workspace BL win_removeUnknownHandler ;Remove it from the list MOV R0,#0 ;Tell client it's all over BL rpt__call ;Finish that off nicely LDMFD R13!,{R0-R4,PC}^ ;And return to caller ; --- The repeater was ended before its time --- 70rpt__alarms MOV R1,#-1 ;Tell Wimp to stop dragging SWI Wimp_DragBox ;Send that off nicely ADR R0,rpt__ukEvents ;Point to the unknown handler MOV R1,#0 ;I'm passing 0 in R4 MOV R2,#0 ;And 0 in R10 too MOV R3,R12 ;But R12 is my workspace BL win_removeUnknownHandler ;Remove it from the list LDMFD R13!,{R0-R4,PC}^ ;And return to caller LTORG ; --- rpt_end --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Ends a repeater before the drag is released. No final ; 0 is sent to the handler. EXPORT rpt_end rpt_end ROUT STMFD R13!,{R12,R14} ;Stack registers WSPACE rpt__wSpace ;Locate my workspace nicely MOV R14,#-1 ;Get a silly routine value STR R14,rpt__routine ;Store in routine address LDMFD R13!,{R12,PC}^ ;Return to caller LTORG ; --- rpt__ukEvents --- ; ; On entry: R0 == event code ; R1 == pointer to event information ; ; On exit: -- ; ; Use: Picks up end-of-drag events from the WindowMangler and ; diables the alarm for the repeat op. rpt__ukEvents ROUT CMP R0,#7 ;Is this a drag event? MOVNES PC,R14 ;No -- return right away STMFD R13!,{R0-R3,R14} ;Save some registers away ; --- Remove the alarm and unknown handlers --- LDR R0,rpt__alarm ;Get the time it's due for ADR R1,rpt__alarms ;Point to my alarms routine MOV R2,#0 ;I passed 0 as it's R10 MOV R3,R12 ;Get its R12 value BL idle_removeAlarm ;And remove it from the list ADR R0,rpt__ukEvents ;Point to the unknown handler MOV R1,#0 ;I'm passing 0 in R4 BL win_removeUnknownHandler ;Remove it from the list ; --- Tell the client it's over --- MOV R0,#0 ;Say this is it then BL rpt__call ;Send the event over LDMFD R13!,{R0-R3,PC}^ ;Return to caller LTORG rpt__wSpace DCD 0 ;----- Workspace ------------------------------------------------------------ ^ 0,R12 rpt__wStart # 0 rpt__routine # 4 ;Client routine to call rpt__R10 # 4 ;R10 value to pass routine rpt__R12 # 4 ;R12 value to pass routine rpt__repeat # 4 ;Current autorepeat rate rpt__alarm # 4 ;Time for next alarm pop rpt__last # 4 ;When last pop expected rpt__wSize EQU {VAR}-rpt__wStart AREA |Sapphire$$LibData|,CODE,READONLY DCD rpt__wSize DCD rpt__wSpace DCD 0 DCD 0 ;----- That's all, folks ---------------------------------------------------- END