; ; nopoll.s ; ; Handling nonpolling dialogue boxes (MDW) ; ; © 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. ;----- Some notes before we start ------------------------------------------- ; ; This is utterly different to the STEEL nopoll. It nabs a prehandler, and ; then uses it to direct fake events at the nonpolling window, which it then ; can pick up and do whatever it wants with. Fake events generated so far: ; ; Click -- for any click on an icon ; Key -- for *only* Escape and Return -- we can't risk people using ; Wimp_ProcessKey or strange caret-moving calls ; Redraw -- when the thing gets opened, we fake a redraw for it, so that ; it gets painted nicely on the screen ; ; Basically, you get much more control over the dialogue box than you would ; with STEEL, but at the small price of a little more responsibility for ; what actually happens. So your nonpolling dialogue boxes can be that much ; cleverer! ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis ;----- External dependencies ------------------------------------------------ GET sapphire:defHandler GET sapphire:event GET sapphire:hour GET sapphire:sapphire GET sapphire:screen ;----- Main code ------------------------------------------------------------ AREA |Sapphire$$Code|,CODE,READONLY ; --- nopoll_open --- ; ; On entry: R0 == a window handle to take over ; ; On exit: -- ; ; Use: Sets up the window with the given handle to be a nonpolling ; dialogue box. The window must already be open on the screen. ; This call will force it to be painted on the screen, and ; then start faking events for it. EXPORT nopoll_open nopoll_open ROUT STMFD R13!,{R12,R14} ;Save some registers WSPACE nopoll__wSpace ;Load my workspace pointer ; --- Make sure there's only one window --- LDR R14,nopoll__window ;Load the nonpolling window CMP R14,#0 ;Is it nonzero? LDMNEFD R13!,{R12,PC}^ ;Yes -- ignore the new one ; --- Remember this window handle --- STR R0,nopoll__window ;Store this handle away LDR R14,nopoll__flags ;Load my flags word ORR R14,R14,#npFlag__redraw ;Window needs painting STR R14,nopoll__flags ;Store flags away again TST R0,#1<<31 ;Is the top bit set? LDMNEFD R13!,{R12,PC}^ ;Yes -- do nothing else ; --- Now read the initial mouse state --- STMFD R13!,{R0-R6} ;Save some more registers SWI OS_Mouse ;Read the mouse position STR R2,nopoll__buttons ;Store the button state away ; --- Read the window position --- LDR R0,[R13,#0] ;Reread the window handle SUB R13,R13,#36 ;Make space for window blk STR R0,[R13,#0] ;Store handle in the block MOV R1,R13 ;Point to the block SWI Wimp_GetWindowState ;Get the window information ; --- Remember the origin position --- LDMIB R13,{R1-R6} ;Load the coordinates out SUB R5,R1,R5 ;Get the x origin position SUB R6,R4,R6 ;Get the y origin position ADR R14,nopoll__ox ;Point to the origin cache STMIA R14,{R5,R6} ;Store these values away ; --- Constrain the mouse --- BL screen_getInfo ;Get the screen information ADD R0,R0,#screen_dx ;Point to the pixel sizes LDMIA R0,{R5,R6} ;Load the pixel sizes SUB R1,R1,R5 ;Knock a pixel off the left SUB R2,R2,R6 ;And off the bottom ; --- Build the constrain block --- ; ; We build the parameter block in registers using lots of ; barrel shifting and then stuff it in the stack. This is ; much quicker and also takes less space. We assume that ; all the coordinates are two bytes wide already. MOV R0,#1 ;Subreason code ORR R0,R0,R1,LSL #8 ;Move in left hand side ORR R0,R0,R2,LSL #24 ;Move in the bottom byte MOV R1,R2,LSR #8 ;Get top byte of bottom edge ORR R1,R1,R3,LSL #8 ;Get right hand side in ORR R1,R1,R4,LSL #24 ;Get bottom byte of top edge MOV R2,R4,LSR #8 ;And the top byte of top edge STMFD R13!,{R0-R2} ;Save them on the stack MOV R0,#21 ;OS_Word mouse operations MOV R1,R13 ;Point to the block SWI XOS_Word ;Perform the constrain ADD R13,R13,#48 ;Return the block nicely LDMFD R13!,{R0-R6,R12,PC}^ ;Return to caller now LTORG ; --- nopoll_close --- ; ; On entry: R0 == return value for nopoll_process (can be anything) ; ; On exit: -- ; ; Use: Tells nopoll that the nonpolling window has been killed, ; and hence that polling can return to normal again. You can ; specify a return value to give from nopoll_process (if that ; system is being used). EXPORT nopoll_close nopoll_close ROUT STMFD R13!,{R0-R4,R12,R14} ;Save some registers WSPACE nopoll__wSpace ;Find my workspace STR R0,nopoll__return ;Save the return value away ; --- Clear the window handle --- LDR R0,nopoll__window ;Load the old window MOV R14,#0 ;No nonpolling window STR R14,nopoll__window ;Store it over the window TST R0,#1<<31 ;Is the top bit set? LDMNEFD R13!,{R0-R4,R12,PC}^ ;Yes -- do nothing else then ; --- Now release the mouse pointer --- BL screen_getInfo ;Read the screen information ADD R0,R0,#screen_width ;Point to the right bit LDMIA R0,{R1-R4} ;Load screen and pixel sizes SUB R3,R1,R3 ;Reduce screen width by 1 SUB R4,R2,R4 ;Reduce screen height by 1 MOV R0,#&00000001 ;Bottom word for constrain MOV R1,R3,LSL #8 ;Shift the width word in ORR R1,R1,R4,LSL #24 ;Move bottom byte of width in MOV R2,R4,LSR #8 ;And get top byte in R2 STMFD R13!,{R0-R2} ;Create the block on stack MOV R1,R13 ;Point to the block MOV R0,#21 ;Reason code SWI XOS_Word ;Do the constraining ADD R13,R13,#12 ;Skip past the OS_Word block LDMFD R13!,{R0-R4,R12,PC}^ ;Return to caller LTORG ; --- nopoll_init --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Initialises nopoll so it can be used. EXPORT nopoll_init nopoll_init ROUT STMFD R13!,{R0,R1,R12,R14} ;Save some registers ; --- Make sure I'm not already going --- WSPACE nopoll__wSpace ;Load my workspace pointer LDR R14,nopoll__flags ;Load my flags word TST R14,#npFlag__inited ;Am I initialised yet? MOV R0,#0 ;Zero window handle STR R0,nopoll__window ;No nonpolling window yet LDMNEFD R13!,{R0,R1,R12,PC}^ ;Yes -- return right now ; --- Register my prefilter nicely --- BL event_init ;Initialise the event system ADR R0,nopoll__filter ;Point to my filter routine MOV R1,R12 ;Pass my workspace pointer BL event_preFilter ;Register the prefilter LDMFD R13!,{R0,R1,R12,PC}^ ;Return to caller nopoll__wSpace DCD 0 LTORG ; --- nopoll__filter --- ; ; On entry: R0 == an event mask ; R1 == pointer to event block ; R2 == earliest time to return with null event ; R3 == pointer to poll word if any ; ; On exit: Carry clear and registers preserved, or carry set and: ; ; R0 == WIMP event code ; R1 preserved, block updated ; ; Use: Generates WIMP-like events from the nonpolling window. nopoll__filter ROUT ; --- Pass on quickly if nothing to do --- STMFD R13!,{R14} ;Just save the link register LDR R14,nopoll__window ;Is there a nonpolling window CMP R14,#0 ;Just check it's nonzero LDMEQFD R13!,{PC}^ ;No -- return right now ; --- Now dispatch redraw events if necessary --- STMFD R13!,{R10} ;Save another register BIC R10,R14,#1<<31 ;Look after the window handle LDR R14,nopoll__flags ;Load my flags word TST R14,#npFlag__redraw ;Do I have to fake a redraw? BEQ %00nopoll__filter ;No -- skip ahead BIC R14,R14,#npFlag__redraw ;Clear the flag STR R14,nopoll__flags ;Store the new flags word MOV R0,#1 ;Pass a redraw reason code STR R10,[R1,#0] ;Store window handle in block LDMFD R13!,{R10,R14} ;Load the registers back ORRS PC,R14,#C_flag ;Return the fake event ; --- Find out if a key was pressed --- 00 STMFD R13!,{R1-R7} ;Save some more registers SUB R13,R13,#8 ;Make space for hourglass blk MOV R0,R13 ;Point to the block BL hour_suspend ;Turn hourglass off a while 01 MOV R0,#&81 ;Read keyboard status MOV R1,#0 ;No delay on key reading MOV R2,#0 ;No delay on key reading SWI OS_Byte ;Read the key value CMP R1,#&0D ;Was Return pressed? CMPNE R1,#&1B ;Or was it Escape? BNE %10nopoll__filter ;No -- handle mouse buttons ; --- Build a dummy caret record in the block --- MOV R0,R13 ;Point to hourglass block BL hour_resume ;Restore old hourglass state ADD R13,R13,#8 ;And restore the stack ptr MOV R0,R1 LDMFD R13!,{R1} ;Restore the Wimp block ptr STR R10,[R1,#0] ;Store window handle away STR R0,[R1,#24] ;Save the key number too ;Don't bother with the rest MOV R0,#8 ;Fake a key event LDMFD R13!,{R2-R7,R10,R14} ;Restore registers ORRS PC,R14,#C_flag ;And fake the event ; --- Read the mouse status --- 10 SWI OS_Mouse ;Read the mouse status LDR R14,nopoll__buttons ;Read the old button state STR R2,nopoll__buttons ;Store new button state BIC R2,R2,R14 ;Leave only newly clicked BICS R2,R2,#2 ;And clear the menu button BEQ %01nopoll__filter ;If nothing clicked, loop LDR R4,[R13,#8] ;Load the Wimp block pointer STMIA R4,{R0-R2,R10,R12,R14} ;Save the registers away ; --- Now find which icon got clicked --- ADR R14,nopoll__ox ;Point to the origin coords LDMIA R14,{R6,R7} ;Load them into registers SUB R6,R0,R6 ;Convert mouse coords to... SUB R7,R1,R7 ;... window-relative ones SUB R13,R13,#40 ;Create an icon block STR R10,[R13,#0] ;Save the window handle in it MOV R10,#-1 ;No icons found yet MOV R5,#0 ;Start searching from icon 0 ; --- Loop through the icons nicely now --- 20 STR R5,[R13,#4] ;Store it in the block MOV R1,R13 ;Point to my icon block SWI Wimp_GetIconState ;Get the icon information LDR R14,[R13,#24] ;Load the icon flags out CMP R14,#&00800000 ;Is it deleted-only? BEQ %30nopoll__filter ;Yes -- we've scanned 'em all EOR R14,R14,#&00DF0000 ;Toggle deleted, shaded, ESG TST R14,#&00800000 ;Is it deleted? TSTNE R14,#&00400000 ;Is it shaded? TSTNE R14,#&001F0000 ;Or is the ESG all 1s? ADDEQ R5,R5,#1 ;Check the next icon along BEQ %20nopoll__filter ;Yes -- skip this icon ADD R1,R13,#8 ;Point to icon coordinates LDMIA R1,{R0-R3} ;Load the icon coordinates CMP R0,R6 CMPLE R1,R7 CMPLE R6,R2 CMPLT R7,R3 MOVLT R10,R5 ;If over icon, remember no. ADD R5,R5,#1 ;Check the next icon along B %20nopoll__filter ;And carry on ; --- Return a fake mouse click event --- 30 ADD R13,R13,#40 ;Recover the icon block MOV R0,R13 ;Point to hourglass block BL hour_resume ;Restore hourglass state ADD R13,R13,#8 ;And restore stack block STR R10,[R4,#16] ;Store the icon number away MOV R0,#6 ;Fake a mouse click event LDMFD R13!,{R1-R7,R10,R14} ;Restore loads of registers ORRS PC,R14,#C_flag ;Return to caller at last LTORG ; --- nopoll_process --- ; ; On entry: -- ; ; On exit: R0 == value passed to nopoll_close ; ; Use: Processes a nonpolling window until it calls nopoll_close. ; It then returns the value passed to nopoll_close in R0, ; which can be defined in any way you want. ; ; Some notes on the use of this routine: ; ; * It calls event_poll, so any functions that get called ; after the normal event_poll don't get called. Since the ; Wimp isn't actually being polled at all, this isn't a ; real problem as long as your handlers are registered at the ; event filter level or higher (e.g. win event handlers or ; even dbox handlers). ; ; * It uses up an extra 256 bytes of stack for a poll block. ; If you think you might miss this stack space, then you'd ; better not use this routine. ; ; * It isn't reentrant, but then again, nor is the rest of the ; nopoll system -- you can only have one nonpolling box open ; at a time. EXPORT nopoll_process nopoll_process ROUT STMFD R13!,{R1-R3,R12,R14} ;Save some registers WSPACE nopoll__wSpace ;Locate my workspace SUB R13,R13,#256 ;Make a poll block (!) 00 LDR R14,nopoll__window ;Is there a nonpolling window CMP R14,#0 ;Quick check... BEQ %10nopoll_process ;No -- jump ahead then MOV R1,R13 ;Point to the poll block BL event_poll ;Get a dodgy event back BLCC defHandler ;If not handled, handle it B %00nopoll_process ;And loop back again 10 LDR R0,nopoll__return ;Load the return value ADD R13,R13,#256 ;Restore the stack pointer LDMFD R13!,{R1-R3,R12,PC}^ ;Return to caller again LTORG ;----- Workspace ------------------------------------------------------------ ^ 0,R12 nopoll__wStart # 0 nopoll__flags # 4 ;Various interesting flags nopoll__window # 4 ;The nonpolling window handle nopoll__buttons # 4 ;Button state for icons nopoll__ox # 4 ;Window origin x position nopoll__oy # 4 ;Window origin y position nopoll__return # 4 ;nopoll_process return value nopoll__wSize EQU {VAR}-nopoll__wStart ;My workspace size npFlag__inited EQU (1<<0) ;Am I initialised yet? npFlag__redraw EQU (1<<1) ;Does window need redrawing? AREA |Sapphire$$LibData|,CODE,READONLY DCD nopoll__wSize DCD nopoll__wSpace DCD 0 DCD nopoll_init ;----- That's all, folks ---------------------------------------------------- END