; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; > Sources.Wimp03

;
; Entry:     [taskhandle] = current task handle
;         [newtaskhandle] = new task handle
; Exit:   R14, [flagword] = R0 on entry to Wimp_Poll (plus version bits)
;                 userblk = R1 on entry to Wimp_Poll
; Errors:  address exception if a dead task is paged IN here
;

pageintask
; Need to preserve flags, and return stuff in R14. Hence EntryS/EXITS are
; no good :(

      [ No32bitCode
        Push    "R0,R14"
      |
        Push    "R0,R1,R14"
        MRS     R1,CPSR
      ]
;
        Debuga  sw,"Page in task:",#newtaskhandle
        LDR     R14,newtaskhandle
        CMP     R14,#0                  ; taskhandle = -1 ==> menu owner
      [ debugsw
        BNE     %FT01
        Debug   sw," - system task"
01
      ]
        BEQ     pageintaskdone          ; 0 ==> no owner at all (ie. Wimp)
        LDRLT   R14,menutaskhandle      ; -1 ==> menus
      [ debugsw
        BGE     %FT01
        Debuga  sw," - menu task =",R14
01
      ]
        CMPLT   R14,#0
      [ debugsw
        BGE     %FT01
        Debuga  sw," - deleted!"
01
      ]
        Debug   sw,""
        BLT     pageintaskdone          ; if no menu owner, give up
        LDR     R0,taskhandle
        TEQ     R0,R14
        LDREQ   R14,flagword
        BEQ     pageintaskdone          ; DON'T RELOAD userblk if not nec.!!!
        Push    "R14"
        LDR     R14,[wsptr,R14]
        TST     R14,#task_unused
        BLEQ    mapslotout              ; map slot out, if still alive
        Pull    "R14"
        STR     R14,taskhandle
        BL      mapslotin
        LDR     R14,taskhandle
        LDR     R14,[wsptr,R14]
        TST     R14,#task_unused        ; tasks are sometimes allowed
      [ debugtask1                      ; to be dead here
        BEQ     %FT01
        Debug   task1,"Dead task paged in:",#taskhandle
01
      ]
        LDREQ   userblk,[R14,#task_registers+4*1]      ; get user R1
        LDREQ   R14,[R14,#task_flagword]
        STREQ   R14,flagword
pageintaskdone
      [ No32bitCode
        Pull    "R0,PC",,^                      ; must preserve flags
      |
        MSR     CPSR_f,R1
        Pull    "R0,R1,PC"                      ; must preserve flags
      ]

lookfornewtask
        Debug   task1,"Task dead on entry to Wimp_Poll"
;
; disconnect the task from any further contact with the Wimp
;
        LDR     R14,ptrtask
        LDR     R5,taskhandle
        TEQ     R5,R14
        MOVEQ   R14,#nullptr            ; avoid ptr_leaving_window!
        STREQ   R14,ptrwindow
;
        MOV     R0,#EscapeHandler       ; the domain is about to die -
        SWI     XOS_ReadDefaultHandler  ; prevent nasty handlers from being
        SWIVC   XOS_ChangeEnvironment   ; called when they shouldn't be!
;
        MOV     R0,#EventHandler
        SWI     XOS_ReadDefaultHandler
        SWIVC   XOS_ChangeEnvironment
;
        MOV     R0,#UpCallHandler
        SWI     XOS_ReadDefaultHandler
        SWIVC   XOS_ChangeEnvironment
;
        BL      deallocatependingtask   ; delete task block (gone for good)
                                        ; reclaim memory as well!
;
        Debug   co,"Closing down task: commandflag =",#commandflag
;
        LDR     R7,commandflag          ; R7 used further down!!!
;
        MOV     R0,#0                   ; 'Press SPACE' if anything printed
        BL      int_commandwindow       ; can't call SWI since task is dead!
        CLRV                            ; ignore errors
;
; now look for an alternative task (preserve R7 in this code)
;
        ADRL    R5,taskpointers
        MOV     R6,#maxtasks
01
        LDR     R14,[R5],#4
        TST     R14,#task_unused
        BEQ     %FT02
        SUBS    R6,R6,#1
        BNE     %BT01
;
        Debug   task1,"No tasks left - calling OS_Exit"
        SWI     XOS_Exit
02
        SUB     R5,R5,#4
        SUB     R5,R5,wsptr
        LDR     R6,taskhandle          ; R6 = previous task handle
        STR     R5,taskhandle
        LDR     userblk,[R14,#task_registers+4*1]
        Debug   task1,"Switching to task",R5
;
        BL      mapslotin               ; previous slot is already mapped out
;
; if that was the single task, change mode now!
; the rule is that the screen is reset unless the commandwindow is pending
; (used to be based on singletaskhandle)
;
        LDR     R5,singletaskhandle
        TEQ     R5,R6
        MOVEQ   R14,#nullptr            ; reset singletaskhandle BEFORE resetk
        STREQ   R14,singletaskhandle
        STREQ   R14,backwindow          ; must have been deleted
;
        TEQ     R7,#cf_pending          ; R7 set up further up !!!
        LDRNE   R0,currentmode          ; only change mode if chars printed

        BLNE    int_setmode
      [ Medusa
        ADRL    R14,greys_mode
        LDRB    R14,[R14]
        TEQ     R14,#0
        BLNE    recalc_greys_palette
      ]

        Debug   task1,"Returned from int_setmode"
;
        ADRL    R3,tempiconblk          ; forget old settings
        BL      resetkeycodes           ; *FX 4,2 etc.
;
        Debug   task1,"Paranoid resetting of state when task exits"
;
        B       taskisused



;;-----------------------------------------------------------------------------
;; Wimp_RegisterFilter - install/deinstall a filter routine
;;
;; in   R0 = filter reason
;;              =0 => pre-poll filter (prior to returning to task)
;;              =1 => post-poll filter (on return back to Wimp)
;;              =2 => block copy filter
;;              =3 => get rectangle filter
;;
;;      R1 -> filter routine / =0 for default
;;      R2 -> workspace for filter
;; out  -
;;-----------------------------------------------------------------------------

        ASSERT  postfilter =prefilter +8
        ASSERT  copyfilter =postfilter +8
        ASSERT  rectanglefilter =copyfilter +8
	ASSERT	postrectfilter =rectanglefilter +8
	ASSERT	posticonfilter =postrectfilter +8

        ASSERT  prefilterWP =prefilter -4

SWIWimp_RegisterFilter

        MyEntry "RegisterFilter"

        CMP     R0,#WimpFilter_MAX      ; filter number valid?
        BHS     err_badR0               ; if not then return an error

        TEQ     R1,#0                   ; de-register a filter
        ADREQ   R1,filtertable
        LDREQ   R2,[R1,R0,LSL #2]       ; get the default owner
        ADDEQ   R1,R1,R2                ; and resolve to an address, rather than offset

        ADRL    R14,prefilter
        ADD     R14,R14,R0,LSL #3       ; -> vector to store into
        STR     R1,[R14]
        STR     R2,[R14,#-4]            ; setup the handler correctly

        B       ExitWimp

;..............................................................................

; table of default vector owners

filtertable
        & prefilter_default -filtertable
        & postfilter_default -filtertable
        & copyfilter_default -filtertable
        & rectanglefilter_default -filtertable
        & postrectfilter_default -filtertable
	& posticonfilter_default -filtertable
        & -1

prefilter_default
postfilter_default
copyfilter_default
rectanglefilter_default
postrectfilter_default
posticonfilter_default

        MOV     PC,LR

;;-----------------------------------------------------------------------------
;; Reset the filters back to their default state, called on init and svc_reset,
;; once done issue a svc_RegisterFilters.
;;-----------------------------------------------------------------------------

defaultfilters EntryS "R0-R2"

        ADR     R0,filtertable          ; -> list of default owners
        ADRL    R1,prefilter            ; -> list of vectors to install
        MOV     R2,R0

10      LDR     R14,[R0],#4             ; get offset to routine
        CMP     R14,#-1
        ADDNE   R14,R14,R2
        STRNE   R14,[R1],#8             ; if not end of the table then store away
        BNE     %BT10

        ADR     R0,svc_callback
        MOV     R1,WsPtr
        SWI     XOS_AddCallBack         ; add the callback routine

        EXITS

;..............................................................................

; callback routine used to warn the outside world that they should register
; filters with the Window Manager.

svc_callback ROUT

        Push    "R1,LR"
        MOV     R1,#Service_WimpRegisterFilters
        SWI     XOS_ServiceCall
        Pull    "R1,PC"                 ; broadcast to the world


;;----------------------------------------------------------------------------
;; Wimp_Poll
;;
;; return codes are prioritised according to number:
;;
;;       1       Redraw_Window_Request
;;       2       Open_Window_Request
;;       3       Close_Window_Request
;;       4       Pointer_Leaving_Window
;;       5       Pointer_Entering_Window
;;       6       Mouse_Click
;;       7       User_Dragbox
;;       8       Key_Pressed
;;       9       Menu_Select
;;      10       Scroll_Request
;;
;; This is also the entry point after deleting a task
;;----------------------------------------------------------------------------

err_badR3
        MyXError  WimpBadR3
        B       ExitWimp
        MakeErrorBlock WimpBadR3

SWIWimp_PollIdle
        Debug   poll2, "Wimp_PollIdle entry"
        ORR     R0,R0,#flag_pollidle    ; time limit is in R2
        B       %FT01

SWIWimp_Poll
        Debug   poll2, "Wimp_Poll entry"
        BIC     R0,R0,#flag_pollidle    ; no time limit supplied

01
        MyEntry "Poll"

; remember task number, to allow optimisation of return to caller
;
; Entry: R0 = flag word
;        R1 = taskhandle
;        R2 = target time (if R0 & flag_pollidle) - as in ReadMonotonicTime
;        R3 -> poll word  (if R0 & flag_pollword, and Wimp 2.23 known)
;   userblk = original R1 --> poll block
;

      [ CnP
; is there a pending Message_DataLoad for the cnp task to handle?
        LDR     R14,clipboard_spritearea_addr
        TEQ     R14,#0
        BEQ     %FT00                   ; definitely not

        ADD     R14,R14,#cnp_message_dataload_park
        LDR     R14,[R14]
        TEQ     R14,#0
        BEQ     %FT00                   ; no message parked at the moment

        ADRL    R14,clipboard_pollword
        LDR     R4,[R14]
        ORR     R4,R4,#clipboard_pw_dataload_flag
        STR     R4,[R14]                ; set the poll word flag so the task can pick up the stored message
00
      ]

; attempt to call a pre-poll filter routine, it may modify the flags in R0

        CallFilter prefilter
;
      [ AutoHourglass                   ; it could still be a while until the next task switch,
        LDR     R1, hourglass_status    ; so ensure the hourglass is off at this point
      [ debugautohg
	Push	"handle"
	LDR	handle, taskhandle
        Debug	autohg, "Hourglass_Off at Wimp_Poll entry: taskhandle, old status =", handle, R1
        Pull	"handle"
      ]
        TEQ     R1, #2                  ; is hourglass still pending or active?
        MOVEQ   R1, #1                  ; yes, so turn off hourglass (but keep on vector for speed reasons)
        STREQ   R1, hourglass_status
        SWIEQ   XHourglass_Off
        CLRV                            ; ignore errors
      ]

        LDR     R1,taskhandle
        LDR     R4,[wsptr,R1]           ; R4 = task data pointer
        TST     R4,#task_unused         ; if task not used, it's been deleted
        BNE     lookfornewtask

        LDR     R5,[R4,#task_flagword]  ; R5 = flag word
        LDR     R14,singletaskhandle
        TEQ     R1,R14                  ; if single-tasking
        LDREQ   R14,=masknewcodes
        ORREQ   R0,R0,R14               ; disallow new reason codes

; set up R3 -> poll word, with bottom bit set => urgent
; also validate R0,R3 (but only if task knows about Wimp 2.23)

        LDR     R14,[R4,#task_wimpver]
        CMP     R14,#223
        MOVLO   R3,#0                   ; R3=0 => no poll word
        BLLO    killfpblock             ; get rid of this!
        BLO     %FT01

; this stuff only applies to tasks that know about Wimp 2.23 or later
; bits 22,23 => poll word, bit 24 => save/restore FP registers

        TST     R0,#pollword_bit        ; if this event is disabled,
        BICNE   R0,R0,#flag_pollword    ; then there's no point polling!

        TST     R0,#flag_pollword       ; no poll word if this bit unset
        MOVEQ   R3,#0

        TST     R3,#3                   ; check that address is word-aligned
        BNE     err_badR3
        CMP     R3,#ApplicationStart    ; and not in application space

        [ Medusa
        LDRHS   R14,orig_applicationspacesize
        CMPHS   R14,R3
        |
        RSBHSS  R14,R3,#&1000000        ; APPSPACE
        ]
        BHS     err_badR3

	CLRV				; make sure we don't leave V set
        TST     R0,#flag_pollword
        TSTNE   R0,#flag_pollfast       ; bottom bit set => poll quickly
        ORRNE   R3,R3,#1

        MOV     R14,R0,LSR #flag_versionbit   ; object if others set
        BICS    R14,R14,#flag_allowed :SHR: flag_versionbit
        BNE     err_badR0                       ; error from Wimp_Poll !!!

; does the task want to save the FP registers?

        TST     R0,#flag_fpsave
        BLEQ    killfpblock                     ; preserves flags
        BLNE    saveFPregs
        BVS     ExitWimp

; save toned-down flag word in task workspace

01
        STR     R3,[R4,#task_pollword]

        LDR     R14,[R4,#task_priority]
        TST     R0,#flag_pollword
        TEQNE   R3,#0
        ORRNE   R14,R14,#priority_pollword      ; Has a poll word ?
        BICEQ   R14,R14,#priority_pollword

      [ debugpoll
        BEQ     %FT00
        Debug   poll,"New poll task: handle, posn",R1,#PollTaskPtr
00
      ]

        LDRNE   R3,PollTaskPtr
        STRNE   R1,[R3],#4                      ; Add task handle to list of polled tasks
        STRNE   R3,PollTaskPtr

        TST     R0,#flag_pollidle
        ORRNE   R14,R14,#priority_idle          ; Poll_idle ?
        BICNE   R14,R14,#priority_null
        BICEQ   R14,R14,#priority_idle
        BNE     %FT01

        TST     R0,#null_bit
        ORREQ   R14,R14,#priority_null          ; Wants Null events ?
        BICNE   R14,R14,#priority_null
01
        STR     R14,[R4,#task_priority]

        MOV     R0,R0,LSL #32-flag_versionbit   ; clear version bits
        MOV     R0,R0,LSR #32-flag_versionbit
        MOV     R5,R5,LSR #flag_versionbit      ; so we can retain the old ones
        ORR     R0,R0,R5,LSL #flag_versionbit
        STR     R0,[R4,#task_flagword]          ; remember original flags
        STR     R0,flagword
        STR     userblk,[R4,#task_registers+4*1]        ; and user R1
        STR     R2,[R4,#task_registers+4*2]             ; and target time
;
; save VFP context, lazily if possible
;
        MOV     R0,#0
        MOV     R1,#VFPSupport_ChangeContext_Lazy+VFPSupport_ChangeContext_AppSpace
        SWI     XVFPSupport_ChangeContext
        MOVVS   R0,#0 ; Ignore error (probably means VFPSupport isn't loaded)
        Debug   fp,"VFP on Wimp_Poll entry",R0
        STR     R0,[R4,#task_vfpcontext]
        CLRV
;
; check to see if there are any more outstanding parents
;
taskisused
        LDR     R14,taskSP
        ADR     R1,taskstack
        TEQ     R14,R1
        BEQ     %FT01

        LDR     R1,[R14,#-4]!           ; empty ascending stack
        STR     R14,taskSP

        LDR     R0,polltaskhandle       ; R0 = child's task handle
        LDR     R14,[wsptr,R0]          ; R14 = task block pointer
        TST     R14,#task_unused
        MOVNE   R0,#0                   ; R0=0 => child is already dead
        LDREQ   R14,[R14,#task_flagword]
        MOVEQ   R14,R14,LSR #flag_versionbit
        ORREQ   R0,R0,R14,LSL #flag_versionbit

        Task    R1,,"Parent"            ; make sure correct page swaps happen
        LDR     R14,[wsptr,R1]
        TST     R14,#task_unused        ; parent might be dead!
        BNE     lookfornewtask

        B       ExitPoll_toparent       ; exit back to parent task
01

;
; if menus marked for deletion, kill them off!
;
        LDR     R0,menus_temporary      ; -4 ==> kill menus
        TEQ     R0,#0
        STRNE   R0,menutaskhandle       ; NE ==> R0 = -4
        MOVNE   R14,#0
        STRNE   R14,menus_temporary
        BLNE    closemenus
;
; ensure that escape condition generation is disabled on entry
;
      [ debugescape
        LDR     R14,singletaskhandle
        CMP     R14,#nullptr            ; old Wimp didn't set up escape!
        BNE     %FT02
;
        Push    "R0-R2"

        MOV     R0,#229                 ; *FX 229,1 (escape ==> ascii 27)
        MOV     R1,#1
        MOV     R2,#0
        SWI     XOS_Byte
        TEQ     R1,#0                   ; was escape already disabled?
        TOGPSR  Z_bit, R14              ; NE => escape enabled
        MOVEQ   R0,#126
        SWIEQ   XOS_Byte
        TEQEQ   R1,#0                   ; or was there an escape condition?

        Pull    "R0-R2"
        BEQ     %FT02
        MyXError  WimpBadEscapeState
        B       ExitPoll                ; Wimp reports errors itself
        MakeErrorBlock WimpBadEscapeState
02
      ]
;
; re-entry point from within polling routines
;
repollwimp
        Debug   poll2, "repollwimp"
        BL      powersave_tick
        LDR     R14,taskhandle          ; get userblk back (may be corrupted)
        LDR     R14,[wsptr,R14]
        LDR     userblk,[R14,#task_registers+4*1]
;
        MOV     R0,#1
        BL      scanpollwords           ; scan high-priority tasks
        BNE     ExitPoll

        LDR     R2,headpointer          ; are there any outstanding messages?
        CMP     R2,#nullptr
        BNE     returnmessage
;
      [ MultiClose
        LDR     R2, nextwindowtoiconise
        TEQ     R2, #0
        BLNE    iconisenextwindow
        LDR     R2, headpointer         ; are there any outstanding messages now?
        CMP     R2, #nullptr
        BNE     returnmessage
      ]
;
        MOV     R14,#0
        STR     R14,sender              ; all Wimp messages from now on
        STR     R14,hotkeyptr           ; reset this if no more messages
;
; check for a recent mode change - if so, deliver Open_Window_Requests
;
        LDRB    R14,modechanged
        TEQ     R14,#0
        BEQ     nomodechange
        MOV     R14,#0
        STRB    R14,modechanged

; copy scrx1,y1 into lastmode_x1,y1 for next time

        LDR     R14,scrx1
        STR     R14,lastmode_x1
        LDR     R14,scry1
        STR     R14,lastmode_y1

; send Message_ModeChange first

        MOV     R14,#ms_data            ; size of block
        STR     R14,[sp,#-ms_data]!
        MOVVC   R0,#User_Message
        MOV     R1,sp
        MOV     R2,#0
        STRVC   R2,[R1,#ms_yourref]
        LDR     R14,=Message_ModeChange
        STRVC   R14,[R1,#ms_action]
        BLVC    int_sendmessage_fromwimp
        ADD     sp,sp,#ms_data          ; correct stack
;
        Debug   task1,"Mode change message sent"

; first make a copy of the window stack, so that changes in the order are OK
; Enumerate the window stack from back to front

        LDR     R5,activewinds+lh_backwards
01
        LDR     R4,[R5,#ll_backwards]
        CMP     R4,#nullptr
        BEQ     repollwimp
        SUB     handle,R5,#w_active_link
        MOV     R5,R4
;
        Debug   ms,"Mode Change: re-opening window handle",R0,handle
;
        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_isapane         ; don't re-open panes
        BNE     %BT01
;
        Push    "R4,R5,userblk"

        LDR     R14,[handle,#w_flags]   ; ensure window is put on-screen

        LDR     R4,forceflags           ; 0 or ws_onscreenonce
        ORR     R14,R14,R4              ; R4 = 0 or ws_onscreenonce
        STR     R14,[handle,#w_flags]

        ADD     R14,handle,#w_wax0
        LDMIA   R14,{cx0,cy0,cx1,cy1,x0,y0}
        Rel     R14,handle              ; bhandle (open at same height)
        MOV     R0,R14

        Push    "R0,cx0,cy0,cx1,cy1,x0,y0,R14"
        MOV     R1,sp

        LDR     R2,[handle,#w_taskhandle]
        CMP     R2,#0                   ; if system window, open automatically
        BGT     %FT11

; Skip backwindow and iconbarwindow

      [ true
        B       %FT22
      |
        LDR     R14,backwindowhandle    ; if back window, open at full size
        TEQ     R0,R14
        LDRNE   R14,iconbarhandle
        TEQNE   R0,R14
        BEQ     %FT22                   ; ignore these (already done first)
      ]
11
        Rel     R2,handle               ; must send the handle, so that
        MOV     R0,#Open_Window_Request ; the message is lost if window deleted
        Debug   mode,"Sending open request to",R2
        BL      int_sendmessage_fromwimp
22
        ADD     sp,sp,#u_ow1            ; correct stack (& ignore errors)
        Pull    "R4,R5,userblk"
        B       %BT01

nomodechange
      [ redrawlast
        B       lookatpointer

check_redraw
      ]
;
; flush pending opens - make sure this is done BEFORE redraw stuff
;
      [ ChildWindows
        BL      int_flush_opens         ; may cause a braindead panic redraw
      ]
;
; look at invalid list to see if a redraw is necessary
;
        BL      checkredrawhandle
      [ :LNOT: ChildWindows             ; this is pointless - can't get an error from checkredrawhandle!
        BVS     ExitPoll                ; <<<< wot?
      ]
;
; Improved handling of rectangle area full:
; Redraw whole screen intelligently first, then, if that fails, draw
; all the windows from the back to the front (braindead isn't it!)

        LDR     R1,BPR_indication
        TEQ     R1,#BPR_panicnow
        BEQ     start_braindead_panic_redraw
        CMP     R1,#BPR_continuelevel
        BHS     continue_braindead_panic_redraw
        CMP     R1,#BPR_gotfullarea
        BLEQ    BPR_startintelligentredraw
;
        LDR     R1,rlinks+invalidrects
        CMP     R1,#nullptr

; Downgrade to no panic

        MOVEQ   R1,#BPR_notatall
        STREQ   R1,BPR_indication
      [ redrawlast
        BEQ     check_null
      |
        BEQ     lookatpointer           ; screen is all valid!
      ]
;
        LDR     R1,activewinds+lh_forwards

pollredrawlp
        LDR     R2,[R1,#ll_forwards]
        CMP     R2,#nullptr
        BEQ     clearrects

        SUB     handle,R1,#w_active_link

        Push    "R2"
        BL      invalidouterportion
        Pull    "R1"

        LDR     R2,rlinks+windowrects
        CMP     R2,#nullptr
        BEQ     pollredrawlp

;
; Right - windowrects is the intersection of this window's outer portion with the invalid list
; We need to check whether any of the window's children should be redrawn first
;

process_redrawable_window ROUT

        Debug   child,"process_redrawable_window",handle

      [ ChildWindows
        LDR     R2,[handle,#w_children + lh_forwards]           ; start from the top

01      LDR     R14,[R2,#ll_forwards]
        CMP     R14,#nullptr
        BEQ     %FT04

        Push    "R2"

        LDR     R14,[R2,#w_flags - w_active_link]
        TST     R14,#wf_inborder
        ADDNE   R14,handle,#w_x0                ; clip to outer box if it can go in the border
        ADDEQ   R14,handle,#w_wax0              ; clip to work area otherwise
        LDMIA   R14,{x0,y0,x1,y1}

        ADD     R14,R2,#w_x0 - w_active_link
        LDMIA   R14,{cx0,cy0,cx1,cy1}

        max     cx0,x0                          ; intersect child's outer box with parent's work area or outline
        max     cy0,y0
        min     cx1,x1
        min     cy1,y1

        Debug   child,"process_redrawable: child rectangle",cx0,cy0,cx1,cy1
        MOV     R0,#windowrects
        MOV     R1,#torects
        BL      intrect                         ; see if this intersects with the invalid list

        Pull    "R2"

        LDR     R14,rlinks+torects
        CMP     R14,#nullptr
        BNE     %FT03

02      LDR     R2,[R2,#ll_forwards]
        B       %BT01

; Right - redraw the child first, rather than the parent

03      MOV     R0,#windowrects
        BL      assign_set                      ; calls SetRectPtrs internally
        SUB     handle,R2,#w_active_link
      [ debug
        LDR     R14,rlinks + windowrects
        Debug   child,"Detected redraw for child window,rects",handle,R14
      ]
        B       process_redrawable_window       ; in case of nested child windows
04

process_redrawable_window_actually ROUT

        Debug   child,"Actually redraw window",handle
      ]
;
; check for auto-redraw bit in window flags
;
        LDR     R14,[handle,#w_flags]
        Debug   child,"window flags are",R14
        TST     R14,#wf_autoredraw
        BEQ     tryreturnit
;
        BL      int_redraw_window
redrlp  BVS     ExitPoll                        ; <<<< wot?
        TEQ     R0,#0
        BEQ     repollwimp
 [ Twitter
        BL      checktwitter
        LDRNE   r14, getrectflags
        ORRNE   r14, r14, #getrect_twitter
        STRNE   r14, getrectflags
 ]
        BL      int_get_rectangle               ; will draw the user icons
        B       redrlp
;
; check that task did not disable Redraw_Window_Request
;
tryreturnit
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"RedrawRq"         ; can't be a menu (auto-redraw)
        TST     R14,#redraw_bit
      [ redrawlast
        BNE     check_null
      |
        BNE     lookatpointer           ; CAN'T CARRY ON (doesn't work!)
      ]
;
; return Redraw_Window_Request (and remember the handle we want back!)
;
        Rel     R14,handle
        STR     R14,[userblk]
        STR     R14,redrawhandle
      [ debug
        LDR     R14,rlinks + windowrects
        Debug   child,"Returning Redraw_Window_Request for window,rects",#redrawhandle,R14
      ]
        MOV     R0,#Redraw_Window_Request
        B       ExitPoll

powersave_tick
        Entry   "r0"
        LDR     R14,IdlePerSec          ; update the idle information for the portable modules
        ADD     R14,R14,#1
        STR     R14,IdlePerSec
 [ Stork
        LDR     R14, WimpPortableFlags
        TST     R14, #PowerSave                 ; if power saving
        TSTNE   R14, #PortableFeature_Idle      ; and Portable_Idle works
        SWINE   XPortable_Idle                  ; then go dormant until next interrupt or centi-second tick
 ]
        EXIT

        LTORG

;
; clearrects - no more windows left, so clear the rest out
;
clearrects
        BL      defaultwindow
        LDR     R0,scrx0
        LDR     R1,scry1
        SUB     R1,R1,#1                ; default is top-left of screen
        SWI     XOS_SetECFOrigin
        MOV     R0,#4
        BL      background
;
        Push    "userblk"
        SetRectPtrs
;
        MOV     R1,#invalidrects
        B       endclearrects
clearrectslp
        getxy   R1,x,y
        BL      graphicswindow
        [ windowsprite
        Push    "R0"
        LDRB    R0,[handle,#w_wbcol]

        BL      plotspritebackground
        Pull    "R0"
        |
        SWI     XOS_WriteI+16
        ]
      [ Autoscr
        LDR     R14, dragflags
        TST     R14, #dragf_clip ; clipped dragboxes must only be redrawn within their own window
        LDRNE   R14, draghandle
        Abs     R14, R14, NE
        TEQNE   R14, handle
        BLEQ    forcedrag_on            ; put drag rectangle back if nec.
      |
        BL      forcedrag_on            ; put drag rectangle back if nec.
      ]
endclearrects
        LDR     R1,[rectlinks,R1]
        CMP     R1,#nullptr
        BNE     clearrectslp
;
        MOV     R0,#invalidrects
        BL      loserects
        BL      defaultwindow
;
        Pull    "userblk"
        B       repollwimp              ; try again!

start_braindead_panic_redraw
        Debug   bpr,"Starting braindead panic redraw"

; Clear screen to background

        BL      defaultwindow
        LDR     R0,scrx0
        LDR     R1,scry1
        SUB     R1,R1,#1                ; default is top-left of screen
        SWI     XOS_SetECFOrigin
        MOV     R0,#4
        BL      background
        SWI     XOS_WriteI+16           ; CLG

; Find the backmost window and store in BPR_indication -
; BPR_notatall is used to indicate no windows

        LDR     R14,activewinds+lh_backwards
        LDR     R0,[R14,#ll_backwards]
        CMP     R0,#nullptr
        MOVEQ   R14,#BPR_notatall
        SUBNE   R14,R14,#w_active_link
        Rel     R14,R14,NE
        STR     R14,BPR_indication

continue_braindead_panic_redraw

; Run out of windows? - done with redrawing

        LDR     handle,BPR_indication
        TEQ     handle,#BPR_notatall
        BEQ     repollwimp

        Debug   bpr,"Continuing braindead panic redraw on handle",handle

; Window gone? - start from the start again

        BL      checkhandle
        BVS     start_braindead_panic_redraw

; Window closed? - start from the start again

        LDR     R14,[handle,#w_flags]
        Debug   bpr,"handle, flags are",handle,R14
        TST     R14,#ws_open
        BEQ     start_braindead_panic_redraw

; Move BPR_indication to next window in stack (must deal with child window stacks as well)

      [ ChildWindows
        Push    "handle"

        LDR     R0,[handle,#w_children + lh_backwards]          ; go for backmost child window first
        LDR     R14,[R0,#ll_backwards]
        CMP     R14,#nullptr
        BNE     %FT02

01      LDR     R0,[handle,#w_active_link + ll_backwards]       ; then go for next sibling
        Debug   bpr,"Next sibling",handle,R0
        LDR     R14,[R0,#ll_backwards]
        CMP     R14,#nullptr
        BNE     %FT02

        LDR     handle,[handle,#w_parent]                       ; else go for parent's next sibling, etc.
        Debug   bpr,"Move up to parent",handle
        CMP     handle,#nullptr
        BNE     %BT01

02      MOVEQ   R0,#BPR_notatall
        SUBNE   R0,R0,#w_active_link
        Rel     R0,R0,NE
        STR     R0,BPR_indication

        Pull    "handle"

        Debug   bpr,"Braindead redraw: this,next",handle,R0
      |
        LDR     R0,[handle,#w_active_link+ll_backwards]
        LDR     R14,[R0,#ll_backwards]
        CMP     R14,#nullptr
        MOVEQ   R0,#BPR_notatall
        SUBNE   R0,R0,#w_active_link
        Rel     R0,R0,NE
        STR     R0,BPR_indication
      ]

; Process this window (exactly)

        BL      initrectptrs
        ADD     R0,handle,#w_x0
        LDMIA   R0,{cx0,cy0,cx1,cy1}

; If this is a child window, clip to parent's work area/outline

      [ ChildWindows
        MOV     R1,handle
        B       %FT12

11      TST     R0,#wf_inborder
        ADDNE   R14,R1,#w_x0            ; clip to outer box if allowed to overlap border
        ADDEQ   R14,R1,#w_wax0          ; clip to parent's work area otherwise
        LDMIA   R14,{x0,y0,x1,y1}
        max     cx0,x0
        max     cy0,y0
        min     cx1,x1
        min     cy1,y1

12      LDR     R0,[R1,#w_flags]
        LDR     R1,[R1,#w_parent]
        CMP     R1,#nullptr
        BNE     %BT11
      ]

; make one-rectangle list of the window's outer box (clipped to its parent's work area)

        MOV     R0,#windowrects
        MOV     R1,R0
        BL      addrect
        LDR     R14,rlinks+windowrects
        CMP     R14,#nullptr
        BEQ     continue_braindead_panic_redraw
      [ ChildWindows
        B       process_redrawable_window_actually      ; DON'T consider the children this time
      |
        B       process_redrawable_window
      ]


;;----------------------------------------------------------------------------
;; Just before looking at the mouse, check all poll words
;;----------------------------------------------------------------------------

lookatpointer
        MOV     R0,#-1
        BL      scanpollwords           ; scan all tasks with pollwords
        BNE     ExitPoll
;
; Has the ticker gone off to see if we need to look at the
; mouse co-ordinates, have they changed yet?!
;
      [ mousecache
        LDRB    R0,recacheposn
        TEQ     R0,#0                   ; do we recache the information?
        BEQ     trykeys                 ; no, so ignore
      ]
;
; now see what user input has occurred
;
        Debug   poll2, "Processing mouse"
        BL      getmouseposn            ; ie. MOUSE R0,R1,R2

      [ PoppingIconBar
	BL	checkiconbarpop
      ]

      [ CnP
        ; autoscroll an icon?
        LDR     R14,cnp_iconautoscr_state
        TST     R14,#cnp_af_enabled
        BLNE    poll_iconautoscroll
      ]
;
; are we dragging a box?
;
        LDR     R14,dragtype
        TEQ     R14,#0
        BEQ     notdragging
      [ CnP
        TEQ     R14,#drag_icon_selection
        BEQ     dragging_iconselection
      ]

;
; update coords (depends on drag type) and do bound checking
;
        Push    "R0-R2"                 ; mouse x,y coords and buttons

        LDR     R14,dragoldx
        STR     R0,dragoldx
        SUB     R0,R0,R14               ; x-offset this time
        LDR     R14,dragoldy
        STR     R1,dragoldy
        SUB     R1,R1,R14

        ADR     R14,dragx0
        LDMIA   R14,{x0,y0,x1,y1}
        LDR     R14,dragtype
        TEQ     R14,#drag_size          ; do we move both coords?
        TEQNE   R14,#drag_user2         ; user rubber-box
        TEQNE   R14,#drag_subr_size     ; user-supplied subroutine, rubber box
        TEQNE   R14,#drag_subr_size2    ; user-supplied subroutine, rubber box
        ADDNE   x0,x0,R0
        ADDNE   y1,y1,R1
        ADD     x1,x1,R0
        ADD     y0,y0,R1
;
; if drag types 10 or 11, or still holding button down, update box and continue
;
        TEQ     R14,#drag_subr_posn2
        TEQNE   R14,#drag_subr_size2
        TOGPSR  Z_bit,R14               ; NE ==> it is one of these
        TSTEQ   R2,#button_left:OR:button_middle:OR:button_right
        BLEQ    nodrag
        BLNE    yesdrag                 ; updates dragx0,y0,x1,y1
        Pull    "R0-R2"
        BEQ     boxdropped
;
; check sysflags to see if continuous dragging occurs
;
        LDR     R14,dragtype
        TEQ     R14,#drag_scrollboth    ; use vscroll bit of sysflags for this
        MOVEQ   R14,#drag_vscroll
        CMP     R14,#drag_user
        BHS     notdragging             ; only system drags are elegible

        Push    "R0"
        LDRB    R0,sysflags             ; drag bits are 0..3
        MOVS    R0,R0,LSR R14
        Pull    "R0"
        BCC     notdragging             ; R0,R1 = mouse coords here

        LDR     R1,dragtype             ; R14 may not = dragtype
        B       returndrag

      [ CnP
dragging_iconselection
; continue dragging selection
        TST     R2,#button_left:OR:button_middle:OR:button_right
        BEQ     %FT91                   ; all done, terminate the drag

        ; get current string index from mouse coords
        MOV     R2,R0
        MOV     R3,R1
        LDR     R0,caretdata+caretwindow
        LDR     R1,caretdata+careticon
        Abs     handle,R0

        ; page in task as we need access to the icon data
        LDR     R14,taskhandle
        Push    "R14"
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"dragging_iconselection"

        ; adjust coords to be relative
        LDR     R4,[handle,#w_scx]
        ADD     R2,R2,R4
        LDR     R4,[handle,#w_wax0]
        SUB     R2,R2,R4

        LDR     R4,[handle,#w_scy]
        ADD     R3,R3,R4
        LDR     R4,[handle,#w_way1]
        SUB     R3,R3,R4
        BL      findclickindex

        ; what do we need to do?
        LDRB    R14,cnp_pending_dragtype

        CMP     R14,#cnp_drag_limit
        ADDLE   PC,PC,R14,LSL #2
        B       %FT80                   ; invalid type, cancel the drag
        B       %FT80                   ; we shouldn't have a type 0
        B       %FT20                   ; create_charselection
        B       %FT30                   ; create_wordselection
        B       %FT80                   ; drag and drop not done through type 13 dragbox
        B       %FT40                   ; adjust_high
        B       %FT50                   ; adjust_low
20
; doing a character selection
; select between caret and new index (R5)

        LDR     R2,[handle,#w_seldata+wselxoverride]  ; get scroll offset

        ; it was set up when iconautoscroll was started
21
        MOV     R3,#0
        MOV     R4,#crf_selection :OR: crf_nocentre
        LDR     R7,caretdata+caretindex

        ; determine order - ensure we are low to high
        CMP     R5,R7
        MOVLT   R6,R7
        MOVGE   R6,R5
        MOVGE   R5,R7

        BL      int_set_caret_position
        B       %FT90
30
        ; doing a word selection
        ; need to select up (or down) to the caret position
        ; we then extend the selection to the next word boundary in whichever
        ; direction

        LDR     R2,[handle,#w_seldata+wselxoverride]  ; current scroll offset
        MOV     R3,#0
        MOV     R4,#crf_selection :OR: crf_nocentre
        ; R5 is mouse index
        Push    "R0,R2"

        ; get icon data pointer
        LDR     R0,[handle,#w_icons]
        ADD     R0,R0,R1,LSL #i_shift
        LDR     R14,[R0,#i_flags]
        TST     R14,#if_indirected
        ADDEQ   R2,R0,#i_data
        LDRNE   R2,[R0,#i_data]
        LDR     R7,caretdata+caretindex         ; current caret position

        CMP     R5,R7
        BLT     %FT35                   ; select to the left
        BGT     %FT38                   ; select to the right
        ; we're at the caret position
        ; issue a zero-width request for now
        BL      skipwordR
        MOV     R6,R5
        MOV     R5,R7
        BL      skipwordL
        Pull    "R0,R2"
        BL      int_set_caret_position
        B       %FT90
35
        ; select word(s) to the left
        BL      skipwordL
        Push    "R5"
        MOV     R5,R7
        BL      skipwordR
        MOV     R6,R5
        Pull    "R5"
        Pull    "R0,R2"
        BL      int_set_caret_position
        B       %FT90
38
        ; select word(s) to the right
        BL      skipwordR
        MOV     R6,R5
        MOV     R5,R7
        BL      skipwordL
        Pull    "R0,R2"
        BL      int_set_caret_position
        B       %FT90
50
        ; dragging adjust selection for low bound

        ; have we swapped over to the high bound now?
        LDR     R6,[handle,#w_seldata+wselhighindex]
        CMP     R5,R6
        MOVGT   R14,R6
        MOVGT   R6,R5
        MOVGT   R5,R14                  ; swap over indices
        MOVGT   R14,#cnp_drag_adjust_high
        STRGTB  R14,cnp_pending_dragtype ; change the drag type

        LDR     R2,[handle,#w_seldata+wselxoverride]  ; we want to keep the offset unchanged
        MOV     R3,#0
        MOV     R4,#crf_selection :OR: crf_nocentre
        BL      int_set_caret_position
        B       %FT90
40
        ; dragging adjust selection for high bound
        ; have we swapped over to the low bound?
        LDR     R14,[handle,#w_seldata+wsellowindex]
        CMP     R14,R5
        MOVLE   R6,R5
        MOVLE   R5,R14                  ; no, so set up regs for setcaretposition call
        MOVGT   R6,R14                  ; yes, so we need to change the drag type as well
        MOVGT   R14,#cnp_drag_adjust_low
        STRGTB  R14,cnp_pending_dragtype

        LDR     R2,[handle,#w_seldata+wselxoverride]  ; we want to keep the offset unchanged
        MOV     R3,#0
        MOV     R4,#crf_selection :OR: crf_nocentre
        BL      int_set_caret_position
        B       %FT90
80
        ; finished dragging selection
        ; just tidy up, nothing to report to clients
        MOV     R14,#0
        STR     R14,dragtype
        MOV     R14,#bignum
        STR     R14,[handle,#w_seldata+wselxoverride] ; no scroll override now

        BL      clearpointerwindow
        BL      iconautoscroll_stop
90
        Pull    "R14"
        Task    R14,,"dragging_iconselection 90"
        B       repollwimp
91
        ; exit, no task to reset
        LDR     R0,caretdata+caretwindow
        Abs     handle,R0

        MOV     R14,#0
        STR     R14,dragtype
        MOV     R14,#bignum
        STR     R14,[handle,#w_seldata+wselxoverride] ; no scroll override now

        BL      clearpointerwindow
        BL      iconautoscroll_stop

        MOV     R0,#0                   ; null event
        B       ExitPoll
      ]
;
; box has been dropped
; cancel drag box, then return appropriate reason code
;
boxdropped
        Push    "x0,y0,x1,y1"
        BL      clearpointerwindow      ; corrupts x0,y0,x1,y1
        Pull    "x0,y0,x1,y1"
;
        LDR     R1,dragtype
        TEQ     R1,#drag_scrollboth
        BLEQ    pointeron

        MOV     R0,#0                           ; cancel drag operation
        STR     R0,dragtype                     ; >>> leave [draghandle] alone!
        LDR     R0,mouseflags                   ; (for compatibility)
        BIC     R0,R0,#mf_oldcoords
        STR     R0,mouseflags
;
; come here for continuous dragging as well
; R1 = drag type ([dragtype] may already be 0)
;
returndrag
        ADR     R14,dragoffx0                   ; compute box coords
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        ADD     cx0,x0,cx0
        ADD     cy0,y0,cy0
        ADD     cx1,x1,cx1
        ADD     cy1,y1,cy1
        LDR     R14,dragtask                    ; originator of drag
        TEQ     R14,#0                          ; if menu,
        ADREQL  userblk,tempiconblk             ; set up dummy userblk
        Task    R14,NE,"Drag"                   ; else point to user's block

        CMP     R1,#drag_scrollboth
        CMPNE   R1,#drag_user-1
        STMHIIA userblk,{cx0,cy0,cx1,cy1}       ; final box coords
        MOVHI   R0,#User_Dragbox
        BHI     ExitPoll                        ; return User_DragBox
;
; return Open_Window_Request to the application
; R1 = drag type
; [dragtype] = 0 (drag has terminated) - unless continuous dragging
;
        TEQ     R1,#drag_posn
        TEQNE   R1,#drag_size
        BEQ     gonewposnsize
;
        LDR     handle,draghandle
        STR     handle,[userblk]
        BL      checkhandle                     ; might have been deleted?
        BLVS    nodragging                      ; stop it looping
        BVS     notdragging
;
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{x0,y0,x1,y1}
        ADD     R14,userblk,#4
        STMIA   R14!,{x0,y0,x1,y1}
;
; scroll bar was dragged - now work out new scroll bar positions
;
        TEQ     R1,#drag_hscroll
        TEQNE   R1,#drag_scrollboth
        LDRNE   x0,[handle,#w_scx]
        BLEQ    getnewscx                       ; get x0 from cx0,cx1 etc.
;
        TEQ     R1,#drag_vscroll
        TEQNE   R1,#drag_scrollboth
        LDRNE   y0,[handle,#w_scy]
        BLEQ    getnewscy                       ; get y0 from cy0,cy1 etc.
;
        Push    "x0,y0"
        BL      calc_w_status
        Pull    "x0,y0"
        LDR     x1,[handle,#w_bhandle]          ; open at same place in stack
        ADD     R14,userblk,#u_scx
        STMIA   R14,{x0,y0,x1}
;
        B       Exit_OpenWindow

gonewposnsize
        LDR     handle,draghandle               ; handle of window to open
        STR     handle,[userblk,#u_handle]
        BL      checkhandle                     ; window deleted?
        BLVS    nodragging
        BVS     notdragging     ; CLRV
;
        TEQ     R1,#drag_size                   ; R1 = drag type
        BNE     %FT01
        LDR     R14,[handle,#w_flags]
        ORR     R14,R14,#ws_onscreenonce        ; force window onto screen!
        STR     R14,[handle,#w_flags]           ; (don't touch toggled bit)
01
        ADD     R14,handle,#w_scx
        LDMIA   R14,{x1,y1}
        ADD     R14,userblk,#u_wax0
        STMIA   R14!,{cx0,cy0,cx1,cy1}
        STMIA   R14,{x1,y1}                     ; scroll positions

        BL      calc_w_status           ; ensure bhandle up-to-date

        LDR     R2,oldbuttons
        B       openwindow_checkbuttons

	[ PoppingIconBar
	; Note - need to sort out Service_MouseTrap for all this!
	ROUT
checkiconbarpop
	Push	"R0,LR"
	LDR	R14,singletaskhandle
	CMP	R14,#nullptr
	Pull	"R0,PC",NE		; old-style tasks don't have an icon bar!
	LDR	R14,iconbar_pop_state
	ADD	PC,PC,R14,LSL #2
	NOP
	B	%F10
	B	%F20
	B	%F30
	[ OldStyleIconBar
; Icon bar is held by menu
	|
	NOP ; drops through
; Icon bar is held by menu, or fronted by keyboard
        ]
	Pull	"R0,PC"

; Icon bar is at back
10      LDRB    R14, popiconbar
        TEQ     R14, #0
        Pull    "R0, PC", EQ    ; don't bring to front if configured off

        TEQ	R1,#0		; are we at the bottom of the screen?
	Pull	"R0,PC",NE

      [ Autoscr
        ; Don't bring to front if autoscrolling
        LDR     R14, autoscr_state
        TST     R14, #af_enable
        Pull    "R0, PC", NE
      ]
	; Don't bring to front if dragging a window
	LDR	R14,dragtype
	CMP	R14,#drag_posn
	BLO	%FT11
	CMP	R14,#drag_vscroll
	Pull	"R0,PC",LO
	TEQ	R14,#drag_scrollboth
	Pull	"R0,PC",EQ

11	SWI	XOS_ReadMonotonicTime
	Pull	"R0,PC",VS
	[ true
	LDR	R14, popiconbar_pause
	ADD	R0,R0,R14
	|
	ADD	R0,R0,#pop_DelayTime
	]
	STR	R0,iconbar_pop_time
	MOV	R0,#pop_Delaying
	STR	R0,iconbar_pop_state
	Pull	"R0,PC"

; Icon bar is delaying
20	TEQ	R1,#0
	MOVNE	R14,#pop_Back
	STRNE	R14,iconbar_pop_state
	Pull	"R0,PC",NE
	SWI	XOS_ReadMonotonicTime
	LDR	R14,iconbar_pop_time
	CMP	R0,R14
	Pull	"R0,PC",LO
	MOV	R14,#pop_Front
	STR	R14,iconbar_pop_state
	MOV	R14,#0
	B	%FT50

; Icon bar is at front
30	LDR	R14,iconbarheight
	CMP	R1,R14
	Pull	"R0,PC",LE
	MOV	R14,#pop_Back
	STR	R14,iconbar_pop_state
	MOV	R14,#1

50
	MOV	R0,R14
	LDR	handle,iconbarhandle
	CMP	handle,#nullptr
	Pull	"R0,PC",EQ

;
; As we enter here, R0=0 means pop to front, else go to back (or previous position)
;
	Push	"R1-R11"
	BL	checkhandle             ; handle -> window block
	BLVC	calc_w_status           ; set up flag word
	LDRVC	R14,[handle,#w_flags]
	BVS	%FT70
	TEQ	R0,#0
	BNE	%FT60
	; Going to front. Remember where to go back to.
	[ OldStyleIconBar
	TST	R14,#wf_backwindow
	MOVNE	R6,#-2
	LDREQ	R6,[handle,#w_bhandle]
	ADRL	R0,iconbar_pop_previous
	STR	R6,[R0]
	]
	BIC	R14,R14,#wf_backwindow
	STR	R14,[handle,#w_flags]
	MOV	R6,#-1
	B	%FT65

60	; Going back to previous position.
        [ OldStyleIconBar
	ADRL	R6,iconbar_pop_previous
	LDR	R6,[R6]
	CMP	R6,#-2
	ORREQ	R14,R14,#wf_backwindow
	STREQ	R14,[handle,#w_flags]
	|
	ORR     R14, R14, #wf_backwindow
	STR     R14, [handle, #w_flags]
 [ HideIconBar
	MOV     R6, #-3
 |
	MOV     R6, #-2
 ]
	]
65
	ADD	R14,handle,#w_wax0
	LDMIA	R14,{R0-R3,R4,R5}
	Push	"R0-R6"
	LDR	R14,iconbarhandle
	Push	"R14"
	MOV	userblk,sp
	BL	int_open_window
	ADD	sp,sp,#8*4
70
	Pull	"R1-R11"
	Pull	"R0,PC"
	]

;;----------------------------------------------------------------------------
;; Check for pointer changing window
;;----------------------------------------------------------------------------

notdragging

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

      ; CLRV                            ; ignore errors if they reach here
        MOV     R5,#0                   ; don't match shaded icons
        BL      int_get_pointer_info    ; R3,R4 = window/icon handle

        LDR     R14,dragtype            ; We've just changed icon, do we
        TEQ     R14,#0                  ; to change pointer shape?
        BNE     %FT92
        LDR     R14,mouseflags
        TST     R14,#mf_waitdrag
        BNE     %FT92

        Push    "r0"

        ADRL    r0,old_icon
        LDR     R14,[r0]
        TEQ     R3,R14
        STRNE   R3,[r0]
        ADRL    r0,old_window
        LDREQ   R14,[r0]
        TEQEQ   R4,R14
        STRNE   R4,[r0]

        Pull    "r0"

        BEQ     %FT92

        Push    "R0-R7,handle"

        CMP     R3,#nullptr
        BEQ     %FT90

        Abs     R14,R3
        LDR     R14,[R14,#w_taskhandle]
        CMP     R14,#-1                 ; is the task -1, ie. owned by a menu
        BNE     %FT01
;
        SWI     XOS_ReadMonotonicTime
        LDR     R1,automenu_timelimit
        ADD     R1,R1,R0                ; reset the time limit
        STR     R1,automenu_timeouttime
      [ ClickSubmenus
        MOV     R14, #0
        STRB    R14, submenuopenedbyclick
      ]
01
        CMP     R4,#nullptr
        CMPNE   R4,#nullptr2
        BEQ     %FT90

        Debug   bo,"New window,icon",r3,r4

        LDR     r14,iconbarhandle
        CMP     r14,r3
        Debug   bo,"iconbar handle , handle",r14,r3
        TEQ     r14,r3
        BNE     %FT01

        Push    "R1-R4,R7"
        BL      findicon
        LDR     R14,[R2,#icb_taskhandle]
        Task    R14,,"Icon bar pointer shape check"
        Pull    "R1-R4,R7"
        Abs     r14,r3
        B       ptr_iconbar_icon

01
        Abs     R14,r3
        LDR     r3,[r14,#w_taskhandle]
        Push    "R14"
        Task    r3
        Pull    "R14"

ptr_iconbar_icon
        LDR     R14,[R14,#w_icons]
        ADD     R14,R14,R4,ASL #i_shift ; Point at the icon.
        LDR     r3,[r14,#i_flags]
        TST     r3,#if_text
        TSTNE   r3,#if_indirected       ; Is it indirected text ?
        BEQ     %FT90

        MOV     r2,#WimpValidation_Pointer
        LDR     r3,[r14,#i_data+4]      ; Pointer to validation string.
        AcceptLoosePointer_NegOrZero r3,-1
        CMP     r3,r3,ASR #31
        BEQ     %FT90
        DebugS  bo,"Validation string is ",r3
        BL      findcommand
        BNE     %FT90

        ADRL    r2,pointer_sprite
        Debug   bo,"Copy Sprite name"
88
        LDRB    R14,[r3],#1
        CMP     R14,#","
        MOVEQ   R14,#-1
        CMP     R14,#";"
        MOVEQ   R14,#0
        CMP     R14,#" "
        STRGTB  r14,[r2],#1
        BGT     %BT88

        MOV     R4,#0                   ; Default active point is 0,0
        MOV     R5,#0
        STRB    R4,[r2]                 ; Terminate the sprite name.

        CMP     R14,#-1
        BNE     %FT02

        Debug   bo,"Sprite name copied"

        BL      getnumber
        MOV     R4,R0                   ; Returns 0 if not found.

        LDRB    R14,[R3],#1
        CMP     R14,#","
        BNE     %FT02
        BL      getnumber
        MOV     R5,R0                   ; Returns 0 if not found.

        [ debugbo
         ADRL    r2,pointer_sprite
         DebugS  bo,"Sprite name is :",r2
        ]
02
        MOV     R0,#SpriteReason_SetPointerShape
        ADRL    R2,pointer_sprite
        MOV     R3,#1
      [ Autoscr
        LDR     R14, autoscr_state      ; don't reprogram pointer if autoscrolling is enabled
        TST     R14, #af_enable
        ORRNE   R3, R3, #&10
      ]
        MOV     R6,#0
        MOV     R7,#0
        SWI     XWimp_SpriteOp          ; take from Wimp's sprite area(s)
        MOV     R14,#-1
        ADRL    R1,special_pointer
        STR     R14,[r1]
        B       %FT91
90
        ADRL    R1,special_pointer
        LDR     R14,[R1]
        CMP     R14,#0
        MOVNE   R14,#0
;        ADRNEL  R1,special_pointer
        STRNE   R14,[r1]
        MOVNE   R3,#1
        BLNE    doubleptr_off
91
        Pull    "r0-r7,handle"
92

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; if icon changes while drag pending, return code now!

        LDR     R14,pending_window      ; if icon changes while drag pending
        TEQ     R3,R14                  ; do it now!
        LDREQ   R14,pending_icon
        TEQEQ   R4,R14
        LDRNE   R7,mouseflags
        TSTNE   R7,#mf_waitdrag
        ORRNE   R7,R7,#mf_oldcoords     ; avoid missing clicks

        BNE     waitdrag_action         ; pre-emptive strike!

      [ Autoscr
; do we need to do something about autoscrolling?
        LDR     R14, autoscr_state
        TST     R14, #af_enable
        BLNE    poll_autoscroll         ; may or may not return, preserves flags if it does
        BNE     %FT01                   ; don't scroll icon bar if scrolling something else!
      ]

; if in iconbar, check whether we should scroll automatically

 [ false
        LDR     R14,iconbarhandle
        CMP     R14,#nullptr
        BEQ     %FT01                   ; no iconbar
        TEQ     R3,R14
        BNE     %FT01                   ; this isn't it!
;
        LDR     R14,scrx0
        ADD     R14,R14,#iconbarsepgap
        CMP     R0,R14
        MOVLT   R8,#-16                 ; scroll left
        BLT     %FT02
        LDR     R14,scrx1
        SUB     R14,R14,#iconbarsepgap
        CMP     R0,R14
        MOVGT   R8,#16                  ; scroll right
        BLE     %FT01
02
        Push    "R0-R8,handle,userblk"
        Abs     handle,R3
        BL      calc_w_status           ; ensure bhandle is correct
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{R1-R7}             ; x0,y0,x1,y1,scx,scy,bhandle
        LDR     R8,[sp,#8*4]
        ADD     R5,R5,R8                ; add in appropriate offset
        LDR     R0,iconbarhandle
        Push    "R0,R1-R7"              ; first word is window handle
        MOV     userblk,sp
        BL      int_open_window
        ADD     sp,sp,#4*8              ; handle,x0,y0,x1,y1,scx,scy,bhandle
        Pull    "R0-R8,handle,userblk"
  |
        LDR     R14,iconbarhandle
        CMP     R14,#nullptr
        BEQ     %FT01                   ; no iconbar
        TEQ     R3,R14
	MOVNE	R14,#0
	STRNE	R14,iconbar_scroll_start_time
        BNE     %FT01                   ; this isn't it!
;
        LDR     R14,scrx0
        ADD     R14,R14,#iconbarsepgap
        CMP     R0,R14
        LDRLT   R8, iconbar_scroll_speed
        MVNLT   R8, R8 ; ensure msb set                        ; scroll left
        BLT     %FT02
;
        LDR     R14,scrx1
        SUB     R14,R14,#iconbarsepgap
        CMP     R0,R14
	MOVLE	R14,#0
	STRLE	R14,iconbar_scroll_start_time
        BLE     %FT01
        LDRGT   R8, iconbar_scroll_speed                       ; scroll right
02
        Push    "R0-R8,handle,userblk"
        Abs     handle,R3
        BL      calc_w_status           ; ensure bhandle is correct
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{R1-R7}             ; x0,y0,x1,y1,scx,scy,bhandle
        LDR     R8,[sp,#8*4]

	SWI	XOS_ReadMonotonicTime
	LDR	R14,iconbar_scroll_start_time
	TEQ	R14,#0
	STREQ	R0,iconbar_scroll_start_time
	STREQ	R5,iconbar_scroll_start_scx
	BEQ	%FT04
	; The following formula gives speed = iconbar_scroll_speed+iconbar_scroll_accel * time
	Push	"R2"
	LDR	R5,iconbar_scroll_start_scx
	LDR	R2,iconbar_scroll_start_time
	SUB	R2,R0,R2		; R2=time since scroll start (in centi-seconds)
	CMP     R2, #&3800              ;; much more than this, and accel*time^2 will overflow
	MOVHI   R2, #&3800              ;; when maximum acceleration is configured
	LDR	LR, iconbar_scroll_accel
	MUL	LR,R2,LR		; LR=accel * time (cs)
	MOV     LR, LR, ASR#6
	ADD     LR, LR, LR, ASR#2       ; LR=accel * time (s) (within 3%)
	CMP	R8,#0
	ADDGE	R8,R8,LR
	SUBLT	R8,R8,LR		; R8=(+/-)(speed + accel*time)
	ADDLT   R8, R8, #1              ; adjust because we used NOT(speed) rather than -(speed) for velocity
	MUL	R8,R2,R8		; R8=(+/-)(speed + accel*time) * time (cs)
	ADD     R8, R8, R8, ASR#2
	ADD	R5,R5,R8,ASR #6		; R5+=(+/-)(speed + accel*time) * time (s)
	Pull	"R2"

        LDR     R0,iconbarhandle
        Push    "R0,R1-R7"              ; first word is window handle
        MOV     userblk,sp
        BL      int_open_window
        ADD     sp,sp,#4*8              ; handle,x0,y0,x1,y1,scx,scy,bhandle
04
        Pull    "R0-R8,handle,userblk"
  ]
01

; if dragging, treat as window -1 (do this after iconbar scrolling)

        LDR     R14,dragtype            ; avoid ptr_changing events
        TEQ     R14,#0
        TEQNE   R14,#drag_subr_posn2
        TEQNE   R14,#drag_subr_size2
        MOVNE   R3,#nullptr

; Ptr_Entering/Leaving_Window treats system area as window -1

        Push    "R3"                    ; actual window handle
      [ Autoscr
; also treat as window -1 (but just for the purposes of Pointer Leaving/Entering Window events) if
; we're autoscrolling (which is not necessarily a subset of if dragging)
        LDR     R14, autoscr_state
        TST     R14, #af_enable
        MOVNE   R3, #nullptr
      ]
        LDR     R14,iconbarhandle
        TEQ     R3,R14                  ; treat as window handle -1:
        LDRNE   R14,backwindow          ; (a) if in back window
        TEQNE   R3,R14
        LDRNE   R14,backwindowhandle    ;     (either of them)
        TEQNE   R3,R14
        CMPNE   R4,#nullptr2            ; (b) if in iconbar window
        MOVEQ   R3,#nullptr             ; (c) if in system area
        LDR     R14,ptrwindow
        CMP     R3,R14                  ; same window?
        Pull    "R3",EQ                 ; actual window handle
        BEQ     cantchange
        Debug   ptr,"pointer new,old",R3,r14


;
; if old window not null, return 'leaving window', and set ptrwindow to null
; otherwise set ptrwindow to new window, and return 'entering window'
; in both cases set mf_oldcoords bit.
;
        Push    "R0"                    ; now R0,R3 on stack
        CMP     R14,#nullptr
        BEQ     entering
leaving
        MOV     R8,R14                  ; old handle
        LDR     R9,ptrtask              ; old task

        CMP     R9,#nullptr
        LDRNE   R14,[wsptr,R9]          ; Check if old task is dead
        TSTNE   R14,#task_unused        ; If so, just return 'entering window'
        BEQ     %FT01
        CMP     R3,#nullptr             ; Unless new window is null !!
        BNE     entering                ;
        STR     R3,ptrwindow
        Pull    "r0,r3"
        B       cantchange
01
        MOV     R3,#nullptr             ; set ptrwindow to null
        MOV     R0,#Pointer_Leaving_Window
        MOV     R14,#ptrleaving_bit
        B       ptrboth

entering
        Abs     handle,R3
        LDR     R9,[handle,#w_taskhandle]

        MOV     R8,R3                   ; new handle
        MOV     R0,#Pointer_Entering_Window
        MOV     R14,#ptrentering_bit

ptrboth
        STR     R3,ptrwindow
;
        Push    "R14"
        CMP     R3,#nullptr          ; if ptrwindow not null, task is relevant
        Abs     handle,R3,NE
        LDRNE   R14,[handle,#w_taskhandle]
        STRNE   R14,ptrtask             ; not nec. same as taskhandle
        Pull    "R14"
;
        CMP     R9,#0                   ; if this is a menu or a system window
        Pull    "R0,R3",LE
        BLE     cantchange              ; ignore it!
;
        Push    "R14"
        Task    R9,,"PtrWindow"
        MOV     R9,R14                  ; get flag word
        Pull    "R14"

        TST     R9,R14                  ; see if relevant bit is set
        STREQ   R0,[sp]                 ; keep 'action' flag
        Pull    "R0,R3"
        BNE     cantchange              ; user isn't interested!
;
        LDR     R14,mouseflags
        ORR     R14,R14,#mf_oldcoords
        STR     R14,mouseflags           ; use same values again!
;
        STR     R8,[userblk]            ; R0 = reason code already
        B       ExitPoll


;;----------------------------------------------------------------------------
;; Check for interesting mouse clicks
;;   -  depends on 'button type' of relevant icon
;;----------------------------------------------------------------------------

;
; button attributes
;

ibb_notpressed  *       2_0000000000001
ibb_select      *       2_0000000000010
ibb_writeable   *       2_0000000000100      ; for writeable icons
ibb_canreport   *       2_0100000000000

ibb_waitrepeat  *       2_0000000001000
ibb_waitclick   *       2_0000000010000
ibb_waitdrag    *       2_0000000100000
ibb_waitrelease *       2_0000001000000
ibb_wait2clicks *       2_0000010000000
ibb_waitremove  *       2_0000100000000
ibb_pending     *       2_0000111111000
ibb_report5     *       2_0001000000000
ibb_report6     *       2_0010000000000
                [ CnP
ibb_cnpwriteable *      2_1000000000000
                ]

ibb_justselect  *       ibb_pending :AND: (:NOT:ibb_waitdrag)

bb_setflags     *       3               ; bit number of pending flags


buttonattributes
        DCD     2_0000000000000  ; 0 never report
        DCD     2_0100000000001  ; 1 always report
        DCD     2_0100000001000  ; 2 report if button clicked (auto-repeat)
        DCD     2_0100000000000  ; 3 report if button clicked
        DCD     2_0100101000010  ; 4 select if clicked, report if released
        DCD     2_0100010000010  ; 5 select if clicked, report if 2 clicks
        DCD     2_0100000100000  ; 6 as for (3), but can also drag
        DCD     2_0100001100010  ; 7 as for (4), but can also drag
        DCD     2_0100010100010  ; 8 as for (5), but can also drag
        DCD     2_0100100010011  ; 9 select always, report if clicked
        DCD     2_0101010100010  ;10 report 1 or 2 clicks (:SHL:8 if 1 click)
        DCD     2_0100000100010  ;11 as (8), but select+notify on single click
        DCD     2_0110010100010  ;12
        DCD     2_0100010000000  ;13
        DCD     2_0100000100100  ;14 writeable, but also report as for (6)
      [ CnP
        DCD     2_1101010100010  ;15 writeable, interested in click/drag (will filter out later)
      |
        DCD     2_0000000000100  ;15 writeable icon
      ]
        ALIGN

; R0,R1 = mouse coords (absolute)
;    R2 = mouse button state
; R3,R4 = actual window/icon handles (R3 not forced to -1 for background)

cantchange
        LDR     R14,dragtype
        TEQ     R14,#0
        TEQNE   R14,#drag_subr_posn2
        TEQNE   R14,#drag_subr_size2
        BNE     trykeys                 ; don't recognise any other buttons
;
        BL      scanmenus               ; preserves task if it returns here
        BVS     ExitPoll
;
; window handle can come back -1 if we clicked on a greyed-out menu item
;
        CMP     R3,#nullptr             ; should only happen for grey menu items
        BEQ     trykeys
;
; translate icon -2 to appropriate system icon handle
;
        CMP     R4,#nullptr2            ; in system area?
        BNE     %FT01

        BL      setwimpicon             ; set up R4 and R5
        B       gotbttype
;
; obtain 'button type' of user icon
;
01      CMP     R4,#nullptr
        LDREQ   R5,[handle,#w_workflags]; look up work area button type
        LDRNE   R5,[handle,#w_icons]
        ADDNE   R5,R5,#i_flags
        LDRNE   R5,[R5,R4,ASL #i_shift] ; R5 = flag word
;
; always report the menu button unless in border region
;

gotbttype
        CMP     R4,#nullptr2
        BLE     issystemarea
        LDR     R14,oldbuttons
        BIC     R14,R2,R14
        TST     R14,#button_middle      ; look for +ve edge of MENU button
        BNE     justtellhim
        BIC     R2,R2,#button_middle    ; pretend it's off!

issystemarea

;
; for select and adjust, check the button type
;
        AND     R14,R5,#if_buttontype           ; use field as index offset
        MOVS    R14,R14,LSR #ib_buttontype
        ADR     R6,buttonattributes
        LDR     R6,[R6,R14,ASL #2]              ; R6 = internal flag word
        LDR     R7,mouseflags                   ; R7 = mouse flags
        LDR     R8,oldbuttons                   ; R8 = old button state

; R0,R1 => x,y mouse co-ordinates
; R2 => button state
; R4 => icon handle
; R6 => button attributes
; R7 => mouse flag
; R8 => old button state
; handle => internal window handle

        Push    "R0-R2,handle"
;
        BL      modifytool                      ; attempt to handle window tools changing (when possible)
;
        TST     R2,#button_left+button_right
        BNE     %FT01                           ; if not released then ignore
;
        ASSERT  nullptr = windowicon_workarea
;
        LDR     R0,border_iconselected          ; may have changed!
        CMP     R0,#windowicon_workarea
        BLE     %FT01                           ; if -VE then ignore its not worth the hassle
;
        MOV     R1,#0
        MOV     R2,#is_inverted
        LDR     handle,border_windowselected
        BL      int_set_icon_state              ; modify the tool icon
;
        MOV     R0,#nullptr
        STR     R0,border_iconselected
        STR     R0,border_windowselected        ; flag as no icon / window selected
01
        Pull    "R0-R2,handle"
;
; cancel certain pending actions if icon has changed
;
        LDR     R14,pending_window
        TEQ     R3,R14
        LDREQ   R14,pending_icon
        TEQEQ   R4,R14
        BEQ     sameicon
;
        TST     R7,#mf_waitremove               ; cancel previous icon
        BEQ     dontpratabout
        Push    "R0,R4,handle"
        LDR     handle,pending_window
        BL      checkhandle                     ; IGNORE errors!
        LDRVC   R4,pending_icon
        BLVC    deselecticon
        Pull    "R0,R4,handle"                  ; V cleared lower down

dontpratabout
        TST     R7,#mf_wait2clicks              ; reset pointer shape if nec.
        BLNE    doubleptr_off
        BIC     R7,R7,#mf_pendingexceptdrag     ; cancel pending (except drag)
        STR     R7,mouseflags
;
sameicon
        [ CnP
        TST     R6,#ibb_cnpwriteable            ; special code for writeable icons
        BEQ     %FT00
        TST     R2,#button_left:OR:button_middle:OR:button_right
        BLEQ    mf_writeablerelease
00
        ]
        TST     R7,#mf_waitdrag
        BLNE    waitdrag                        ; may or may not return here
        TST     R7,#mf_waitrelease
        BNE     waitrelease
        TST     R7,#mf_wait2clicks
        BNE     wait2clicks
        TST     R7,#mf_waitclick
        BNE     waitclick
        TST     R7,#mf_waitrepeat
        BNE     waitrepeat
;
testbutton
        CMP     R3,#nullptr             ; can't have select or adjust here!
        BEQ     trykeys
;
        BIC     R2,R2,R8                ; look for +ve edges
;
        TST     R6,#ibb_notpressed      ; unless notpressed, check key is down
        TSTEQ   R2,#button_left:OR:button_middle:OR:button_right
        BEQ     trykeys
;
; icon responds - set up appropriate pending flags
;
        AND     R14,R6,#ibb_pending
        BIC     R7,R7,#mf_pending
        ORR     R7,R7,R14,LSL #mfb_setflags-bb_setflags  ; set up mouseflags
        STR     R7,mouseflags

        TST     R14,#ibb_waitrepeat
        BLNE    getfx12                 ; get auto-repeat settings
        LDRNEB  R14,repeatdelay
        STRNEB  R14,repeatlimit
;
        LDR     R14,timeblk             ; go!
        STR     R14,pending_time
;
        ADR     R14,pending_x
        STMIA   R14,{R0-R4}             ; saves x,y,buttons,window,icon

; change pointer shape for double-clicks to help wally users

        TST     R7,#mf_wait2clicks
        [ CnP
        MOV     R14,#1
        STRB    R14,cnp_clickcounter
        BEQ     %FT00
        TST     R6,#ibb_cnpwriteable
        BLNE    doublewritable_on
        BLEQ    doubleptr_on
00
        |
        BLNE    doubleptr_on            ; if current shape = 1, set double ptr
        ]

; see if the icon reports back, or is just selected

        TST     R6,#ibb_writeable       ; special code for writeable icons
        BLNE    clickonwriteable        ; will return here
        TST     R6,#ibb_canreport
        BEQ     trykeys                 ; type 15 is writeable but can't report
        TST     R6,#ibb_select
        BEQ     justtellhim             ; report direct to the user!
        TST     R6,#ibb_report5         ; report 1 click as well
        MOVNE   R2,R2,LSL #8            ; shift up by 8 bits
        BNE     justtellhim
        TST     R6,#ibb_report6
        BNE     trykeys                 ; don't tell him!
;
; implicit selection takes place (don't tell the user!)
;
        CMP     R4,#nullptr             ; don't select work or system area
        BLGT    selecticon              ; take note of ESG's etc.
;
        TST     R6,#ibb_justselect      ; if no pending actions,
        BEQ     justtellhim             ; notify the user AS WELL as selecting
;
        TST     R6,#ibb_waitclick       ; button could go down AT THE SAME TIME
        BICNES  R14,R2,R8               ; as the pointer enters the icon!
        BNE     justtellhim
;
        B       trykeys

;
; check for pending icon
;

waitrelease
        TST     R2,#button_left:OR:button_middle:OR:button_right
        BICEQ   R7,R7,#mf_waitrelease
        STREQ   R7,mouseflags
        LDREQ   R2,pending_buttons      ; remember original button press
        BEQ     justtellhim
        B       trykeys                 ; keep waiting!

wait2clicks

; new version changes the pointer shape during the double-click period

        LDRB    R5,doubleclick_movelimit
        BL      checkpointermoved       ; sets C if pointer moved too far

        LDR     R5,doubleclick_timelimit
        BLCC    pendingtime             ; preserves flags
        CMPCC   R14,R5
        BCS     %FT01                   ; too late!

        BICS    R2,R2,R8                ; doesn't affect C flag
        BEQ     testbutton

        LDR     R14,pending_buttons     ; can't double-click with different buttons
        TST     R2,R14
        CMPEQ   R2,R2                   ; set C => don't do the double-click
01
        BIC     R7,R7,#mf_wait2clicks
        [ CnP
        BCS     %FT02                   ; if different buttons, then skip the next bit

        TST     R6,#ibb_cnpwriteable
        STREQ   R7,mouseflags
        BLNE    doublewritable_off
        BLEQ    doubleptr_off
        BEQ     justtellhim                 ; normal action if CC and not a writeable

        ; we are a cnp writeable at this point
        ; click counter
        LDRB    R14,cnp_clickcounter
        ADD     R14,R14,#1
        STRB    R14,cnp_clickcounter
        TEQ     R14,#2                  ; double-click or triple?
        ORREQ   R7,R7,#mf_waitdrag :OR: mf_wait2clicks ; enable click-drag and allow an extra click
        ORRNE   R7,R7,#mf_waitdrag      ; enable drag, no more clicks though
        LDR     R14,timeblk
        STR     R14,pending_time        ; reset timer
        STR     R7,mouseflags
        BLNE    doublewritable_off      ; if triple, then reset the pointer
        B       justtellhim

02
        STR     R7,mouseflags
        TST     R6,#ibb_cnpwriteable
        BLNE    doublewritable_off
        BLEQ    doubleptr_off
        CMP     R2,R2                   ; set carry again
        |
        STR     R7,mouseflags
        BL      doubleptr_off           ; preserves flags
        ]
        BCS     testbutton              ; CS: don't do the double-click
        B       justtellhim             ; CC: do the double-click

; In    R0,R1 = current mouse position
;       R5 = movement limit (OS units)
;       [pending_x/y] = mouse position when initial click occurred
; Out   CS => mouse has moved away from that position

checkpointermoved

        Push    "R6,LR"
        LDR     R14,pending_x           ; get chicago dist. from orig. point
        SUBS    R14,R14,R0
        RSBMI   R14,R14,#0
        LDR     R6,pending_y            ; R5 not used any more
        SUBS    R6,R6,R1
        RSBMI   R6,R6,#0
        ADD     R6,R6,R14
        CMP     R6,R5
        Pull    "R6,PC"                 ; CS => pointer has moved away

waitclick
        BICS    R14,R2,R8
        BEQ     testbutton              ; not interested
        B       justtellhim

waitrepeat
        LDR     R14,pending_buttons
        TST     R2,R14                  ; still held down?
        BICEQ   R7,R7,#mf_waitrepeat
        STREQ   R7,mouseflags
        BEQ     testbutton
;
        BL      pendingtime
        Push    "R2"
        LDRB    R2,repeatlimit
        CMP     R14,R2
        Pull    "R2"
        BCC     trykeys                 ; still waiting
;
        LDR     R14,timeblk             ; reset timer
        STR     R14,pending_time
        BL      getfx12
        LDRB    R14,repeatrate          ; different delay this time!
        STRB    R14,repeatlimit
        MOV     R14,#1
        STRB    R14,autorepeating
        B       justtellhim             ; return button press

getfx12
        Push    "R0-R2,LR"
        MOVNE   R0,#&C4
        MOVNE   R1,#0
        MOVNE   R2,#&FF
        SWINE   XOS_Byte
        STRNEB  R1,repeatdelay
        STRNEB  R2,repeatrate
        Pull    "R0-R2,PC"

waitdrag
        Push    "LR"
;
        TST     R2,#button_left:OR:button_middle:OR:button_right
        BICEQ   R7,R7,#mf_waitdrag      ; button released!
        STREQ   R7,mouseflags
        Pull    "PC",EQ
;
        LDRB    R5,drag_movelimit
        BL      checkpointermoved       ; CS => pointer has moved
        BLCC    pendingtime             ; can't return an error
        LDRCC   R5,drag_timelimit
        CMPCC   R14,R5
        Pull    "PC",CC                 ; still waiting
        Pull    "LR"                    ; drop through
;
; This code is also called from higher up, if the window/icon have changed
; Entry:  R7 = current mouseflags
; Exit:   drag buttons reported to the application
;
waitdrag_action
        BL      mousetrap               ; buttons haven't changed
;
        BIC     R7,R7,#mf_waitdrag      ; don't cancel others!
        STR     R7,mouseflags
        ADR     R14,pending_x
        LDMIA   R14,{R0-R4}
        STR     R0,mousexpos            ; to fool Drag_Box if called
        STR     R1,mouseypos
        MOV     R2,R2,ASL #4            ; 'alias' to different buttons
                                        ; drop through

;-----------------------------------------------------------------------------
; Return mouse buttons to the user
; Entry:  R0,R1 = mouse x,y coords
;         R2 = mouse buttons:

;              bits 0-3  = normal response    (depends on button type)
;              bits 4-7  = 'drag' operation   (button held down)
;              bits 8-11 = 'select' operation (ie. 1 click if it's that type)
;         R3,R4 = window/icon handles (-1 if none)
; Exit:   data put into [userblk], and Mouse_Click returned
;-----------------------------------------------------------------------------

justtellhim
        CMP     R4,#nullptr2
        BLE     wimpaction              ; click in system area
;
; if R3 is really -1, then we shouldn't return it to anyone
; (menu clicks on background only work in single-tasking mode)
;
        LDR     R14,backwindowhandle    ; this counts as the background too!
        CMP     R3,R14
        CMPNE   R3,#nullptr
        BEQ     repollwimp
        [ CnP
        CMP     R4,#0
        BMI     %FT00                   ; not over an icon, so no point checking on cnpwritable actions
        TST     R2,#2                   ; menu button?
        BNE     %FT00
        TST     R6,#ibb_cnpwriteable
        BNE     mouseaction_cnpwriteable
00
        ]
;
; page in correct task (again) - R3 may be changed by waitdrag
;
        LDR     R14,iconbarhandle
        CMP     R14,#nullptr            ; don't do this if no iconbar!
        BEQ     notiniconbar
        TEQ     R3,R14
        BNE     notiniconbar
;
        Push    "R0-R4"                 ; save mouse data!
        BL      findicon                ; finds icon R4 in iconbar
        LDREQ   R14,[R2,#icb_taskhandle]
        Task    R14,EQ,"IconBar"
        Pull    "R0-R4"
        BNE     repollwimp              ; unrecognised!
        MOV     R3,#nullptr2            ; return window handle -2 for iconbar
        B       returnclick
;
; ensure that correct task is paged in
; NB ptrwindow may not be the same as R3 (eg. drag button activates later)
;
notiniconbar
        CMP     R3,#nullptr
        BEQ     repollwimp              ; window has been deleted!!!
        Abs     handle,R3
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"ReturnClick"
;
        LDR     R14,flagword
        TST     R14,#buttonchange_bit
        BNE     trykeys
;
; if backwindow, pretend it's really window -1
;
        LDR     R14,backwindow
        TEQ     R3,R14
        MOVEQ   R3,#nullptr             ; (for menu clicks in singletask mode)
;
        CMP     R3,#nullptr             ; is it a real window?
        BEQ     %FT10                   ; if not then icon
;
        CMP     R4,#0
        BMI     %FT10                   ; if its the background then ignore it
;
	TST	R2,#button_middle	; ignore menu clicks for slabbing
	BNE	%FT10

        Push    "R0-R3"

; R3 = window handle / handle => window defn ( <= 0 if none )
; R4 = icon handle

        LDR     R14,[handle,#w_icons]
        ADD     R14,R14,R4,LSL #i_shift ; R14 -> icon defn
;
        LDR     R1,[R14,#i_flags]
        LDR     R3,[R14,#i_data+4]      ; get flags, -> validation string
      [ true
        AND     R14, R1, #if_buttontype ; border_icon/windowselected behaviour is only suitable for a few button types
        MOV     R14, R14, LSR #ib_buttontype
        TEQ     R14, #ibt_autorepeat
        TEQNE   R14, #ibt_click
        TEQNE   R14, #ibt_clickrelease
        TEQNE   R14, #ibt_click2
        TEQNE   R14, #ibt_dclick
        TEQNE   R14, #ibt_dclickrelease
        TEQNE   R14, #ibt_dclick2
        BNE     %FT03
      ]
        BL      getborder               ; decode to setup the border information
;
        TEQ     R0,#border_action
        TEQNE   R0,#border_defaultaction
	BNE	%FT03
;
	LDR	R14,border_windowselected    ; Make sure previous selected icon
	CMP	R14,#nullptr                 ; is unslabbed
	BEQ	%F1
	TEQ	R14,handle
	LDR	R0,border_iconselected
	CMP	R0,#nullptr
	BLE	%F1
	TEQ	R0,R4
	TEQEQ	R14,handle
	BEQ	%F2
	Push	"handle"
	MOV	handle,R14
	MOV	R1,#0
	MOV	R2,#is_inverted
	BL	int_set_icon_state
	Pull	"handle"

1       STR     handle,border_windowselected
        STR     R4,border_iconselected
;
2       LDRB    R0,autorepeating
        CMP     R0,#0
3       MOVNE   R0,#0
        STRNEB  R0,autorepeating
	BNE	%F5

        MOV     R0,R4                   ; R1 => icon handle
        MOV     R1,#is_inverted
        MOV     R2,#0
        BL      int_set_icon_state      ; and then select the icon
;
5
        Pull    "R0-R3"
10
;
; to ensure that Message_MenusDeleted arrives before the next click,
; send mouse clicks as messages if the message queue is not empty
;

returnclick
        LDR     R14,oldbuttons
        STMIA   userblk,{R0,R1,R2,R3,R4,R14}
        MOV     R0,#Mouse_Click

        LDR     R14,headpointer
        CMP     R14,#nullptr
        BEQ     ExitPoll                ; return directly for efficiency

        MOV     R1,userblk
        LDR     R2,taskhandle
        BL      int_sendmessage_fromwimp
        B       repollwimp              ; deliver outstanding messages


        [ CnP
mf_writeablerelease
        Push    "R14"
        ; released a button over a writeable icon
        ; did we originally click over a selection?
        LDRB    R14,cnp_clickcounter
        TEQ     R14,#1
        Pull    "PC",NE                 ; we're only interested in releases after a single click

        LDRB    R14,cnp_pending_dragtype
        TEQ     R14,#cnp_drag_dragdrop  ; if this is true, then we had clicked over a selection initially
        Pull    "PC",NE

        MOV     R14,#0
        STRB    R14,cnp_pending_dragtype

        ; clear selection
        Push    "R0-R9"
        Rel     R0,handle
        MOV     R1,R4
        MOV     R2,#0
        MOV     R3,#0
        MOV     R4,#crf_selection
        MOV     R5,#0
        MOV     R6,#0
        BL      int_set_caret_position
        Pull    "R0-R9,PC"

mouseaction_cnpwriteable
        ; click/drag on a writeable 15.
        ; Entry:
        ; r0=mouse X
        ; r1=mouse Y
        ; r2=mouse buttons
        ; r3=window handle
        ; r4=icon handle
        TEQ     R2,#0
        BEQ     repollwimp              ; no buttons down, so ignore

        Abs     handle,R3
        ; buttons returned:
        ; (20) &400 - select single
        ; (30) &100 - adjust single
        ; (40) &004 - select double/triple (check clickcounter)
        ; (30) &001 - adjust multiple (handled as just single adjust clicks)
        ; (60) &040 - select drag (or click-drag - check clickcounter)
        ; (70) &010 - adjust drag

        LDRB    R14,cnp_clickcounter

        ; select options
        TEQ     R2,#&400
        BEQ     %FT20
        TEQ     R2,#&004
        BEQ     %FT40
        TEQ     R2,#&040
        BEQ     %FT70
        ; adjust options
        TEQ     R2,#&100
        TEQNE   R2,#&001
        BEQ     %FT30
        TEQ     R2,#&010
        BNE     repollwimp              ; not a recognised button option, so drop it
        ; drop through...
70
        ; select drag or click-drag
        LDRB    R14,cnp_pending_dragtype
        TEQ     R14,#cnp_drag_dragdrop
        BNE     %FT72

        MOV     R14,#0
        STRB    R14,cnp_pending_dragtype ; we don't want release to clear it now as we're dragging

        ; we need a pointer to the validation string
        LDR     R14,[handle,#w_icons]
        ADD     R14,R14,R4,LSL #i_shift

        LDR     R0,[R14,#i_flags]
        TST     R0,#if_indirected
        BEQ     %FT71                     ; not indirected => no validation string, so all ok

        LDR     R3,[R14,#i_data+4]        ; validation string...
        CMP     R3,#0
        BLE     %FT71                     ; not present, so all ok

        ; we need to page in the task so we can access the validation data
        LDR     R14,taskhandle
        Push    "R14"
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"DragStartCnP"

        BL      clipboard_check_password
        Pull    "R14"
        Task    R14,,"DragStartCnP2"

        TEQ     R0,#0                     ; if icon is passworded, dragging out is banned
        BNE     repollwimp
71
        ; arrange for drag/drop export
        MOV     R14,#clipboard_pw_dragstart
        ADRL    R0,clipboard_pollword
        STR     R14,[R0]
        B       repollwimp
72
        ; arrange for drag within the icon
        ; drop through to call wimp_dragbox
60
        ; adjust drag
        MOV     R0,#drag_icon_selection
        MOV     R1,#0
        BL      int_drag_box
        B       repollwimp
40
        ; double/triple select click
        LDRB    R14,cnp_clickcounter
        TEQ     R14,#3
        BNE     %FT42

        ; tripleclick - select line
        MOV     R14,#cnp_drag_dragdrop
        STRB    R14,cnp_pending_dragtype ; so we can do click-click-drag to select whole line and start moving it
        MOV     R0,R3
        MOV     R1,R4
        MOV     R2,#0
        MOV     R3,#0
        MOV     R4,#crf_selection
        MOV     R5,#0
        MOV     R6,#bignum
        BL      int_set_caret_position
        B       repollwimp
42
        ; doubleclick - select word
        ; select based on current caret position - if the first click caused the icon to scroll then the user would
        ; probably want to select the word they were originally pointing at, not a new one
        MOV     R14,#cnp_drag_create_wordselection
        STRB    R14,cnp_pending_dragtype

        ; page in the task so we can look at the icon data
        LDR     R14,taskhandle
        Push    "R14"
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"DoubleClickCnP"

        ; get current caret position
        LDR     R0,caretdata+caretindex

        ; get offsets of word around this position
        MOV     R1,R4                   ; icon handle
        BL      cnp_get_word_offsets    ; R5,R6 = start, end offset of word

        Pull    "R14"
        Task    R14,,"DoubleClickCnP2"

        ; set selection
        Rel     R0,handle
        MOV     R2,#0
        MOV     R3,#0
        MOV     R4,#crf_selection

        BL      int_set_caret_position
        B       repollwimp
30
        ; single adjust click

        ; get caret position - in this window/icon
        ; no caret -> set caret position
        LDR     R14,caretdata
        TEQ     R14,R3
        LDREQ   R14,caretdata+careticon
        TEQEQ   R14,R4
        BNE     %FT39

        ; we need task memory for the next bits
        LDR     R14,taskhandle
        Push    "R14"
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"AdjClickCnP"

        MOV     R0,R3
        MOV     R1,R4
        LDR     R2,mousexrel
        LDR     R3,mouseyrel
        BL      findclickindex

        Pull    "R14"
        Task    R14,,"AdjClickCnP2"

        ; r5 is now the byte offset to the click in the string
        ; selection?
        LDR     R14,[handle,#w_seldata+wselicon]
        LDR     R1,caretdata+careticon
        TEQ     R14,R1
        BEQ     %FT35                   ; selection present

        ; no selection

        ; select all between caret and the mouse click
        ; convert mouse click to byte offset
        LDR     R0,caretdata+caretindex
        CMP     R0,R5
        MOVLE   R6,R5
        MOVLE   R5,R0
        MOVLE   R14,#cnp_drag_adjust_high
        MOVGT   R6,R0
        MOVGT   R14,#cnp_drag_adjust_low
        STRB    R14,cnp_pending_dragtype
        MOV     R4,#crf_selection
        MOV     R3,#0
        MOV     R2,#0
        LDR     R0,caretdata
        BL      int_set_caret_position
        B       repollwimp
35
        ; adjust selection size
        LDR     R4,[handle,#w_seldata+wsellowindex]  ; current low index
        LDR     R6,[handle,#w_seldata+wselhighindex]  ; current high index

        ; halfway point
        SUB     R3,R6,R4
        ADD     R3,R4,R3,LSR #1

        ; if closer to low side, adjust that end
        CMP     R3,R5
        MOVLE   R6,R5
        MOVLE   R5,R4
        MOVGT   R14,#cnp_drag_adjust_low
        MOVLE   R14,#cnp_drag_adjust_high
        STRB    R14,cnp_pending_dragtype
        ; if GT then R5=R5 anyway
        LDR     R0,caretdata+caretwindow
        MOV     R2,#0
        MOV     R3,#0
        MOV     R4,#crf_selection
        BL      int_set_caret_position
        B       repollwimp
39
        BL      clickonwriteable
        B       repollwimp
20
        ; single select click
        BL      clickonwriteable

        ; caret position is set
        ; is it inside a selection or not?

        ; same icon?
        Abs     handle,R3                  ; was corrupted, reset
        LDR     R5,caretdata+careticon
        LDR     R6,[handle,#w_seldata+wselicon]
        CMP     R5,R6
        BNE     %FT22

        LDR     R5,[handle,#w_seldata+wsellowindex]  ; low index
        LDR     R6,[handle,#w_seldata+wselhighindex]  ; high index
        LDR     R4,caretdata+caretindex         ; caret index

        CMP     R4,R5
        CMPGE   R6,R4
        BLE     %FT21                   ; outside the selection box, so remove and set up for selection drag

        MOV     R14,#cnp_drag_dragdrop
        STRB    R14,cnp_pending_dragtype

        B       repollwimp
21
        ; clear the selection
        LDR     R0,caretdata
        LDR     R1,caretdata+careticon
        MOV     R4,#crf_selection
        MOV     R5,#0
        MOV     R6,#0
        BL      int_set_caret_position
22
        ; char selection drag is next action
        MOV     R14,#cnp_drag_create_charselection
        STRB    R14,cnp_pending_dragtype

        B       repollwimp
        ]

;----------------------------------------------------------------------------
; Check for a key press
;----------------------------------------------------------------------------

trykeys
        Debug   poll2, "Processing keys"
 [ UTF8
        ; First, check if caret task is queueing keypresses for later handling
        LDR     handle, caretdata + 0
        CMP     handle, #nullptr
        BEQ     %FT11                   ; still read character (may be hot!)
        BL      checkhandle             ; check it hasn't been deleted!
        MOVVS   handle, #nullptr
        STRVS   handle, caretdata + 0   ; if deleted, turn it off
        BVS     %FT11

        LDR     R14, [handle, #w_taskhandle]
        CMP     R14, #0
        LDRMI   R14, menutaskhandle
        LDR     R14, [wsptr, R14]
        LDR     R14, [R14, #task_flagword]
        TST     R14, #keypress_bit
        BNE     nothing                 ; queue for later handling
11

        ; See if we're in the middle of a multi-byte external key event
        LDRB    R14, keyout_buflen
        TEQ     R14, #0
        BEQ     trykeys_getinternalkeycode
        ADRL    R1, keyout_buffer
        LDRB    R6, [R1]                ; next byte to process
        ADD     R2, R1, R14
01      LDRB    R0, [R1, #1]            ; copy the other bytes down
        STRB    R0, [R1], #1
        CMP     R1, R2
        BLO     %BT01
        SUB     R14, R14, #1            ; decrement byte count
        STRB    R14, keyout_buflen

        LDR     R14, singletaskhandle
        CMP     R14, #nullptr
        BLEQ    topmost_window
        STREQ   R0, hotkeyptr           ; initialise hotkey system, unless we're single-tasking
        ADRL    R14, savedcaretdata     ; get caret data
        LDMIA   R14, {R0-R5}
        CMP     R0, #nullptr            ; is there a caret?
        BNE     %FT02
        BL      int_processkey          ; if not, send hotkey
        B       repollwimp              ; (no need to check if single-tasking, as we won't get here otherwise)
02      MOV     handle, R0
        BL      checkhandle             ; check savedcaret window hasn't been deleted
        MOVVS   R0, #0
        STRVSB  R0, keyout_buflen       ; if it has (!) then delete continuation byte queue
        BVS     trykeys                 ; and restart key processing
        LDR     R14, [handle, #w_taskhandle]
        CMP     R14, #0
        LDRMI   R14, menutaskhandle
        Task    R14,, "KeyPressed continuation"
        STMIA   userblk, {R0-R6}
        MOV     R0, #Key_Pressed
        B       ExitPoll                ; return Key_Pressed event

trykeys_getinternalkeycode
        ; Get the next "key" from the expanded function key (if applicable)
        ADR     R5, keystring_buffer
        ADRL    R7, keystring_buflen
        BL      get_internal_keycode_from_buffer
        CMP     R6, #-1                 ; was there one?
        BNE     trykeys_gotinternalkeycode

        ; Get the next task-generated key
        ADR     R5, keyprocess_buffer
        ADRL    R7, keyprocess_buflen
        BL      get_internal_keycode_from_buffer
        CMP     R6, #-1                 ; was there one?
        BNE     trykeys_gotinternalkeycode

        ; Fill our keyboard input buffer from kernel keyboard buffer (needed so we can look ahead)
        LDRB    R3, keyin_buflen
        ADRL    R5, keyin_buffer
      [ true
        MOV     R0, #&81                ; OS_Byte 129 (INKEY)
        MOV     R1, #0
        MOV     R2, #0                  ; time limit 0
        SWI     XOS_Byte
        TEQ     R2, #&FF
        BEQ     %FT02                   ; break if no byte available
        STRB    R1, [R5, R3]            ; stick byte in my buffer
        ADD     R3, R3, #1
        TEQ     R1, #0                  ; null byte (=> either a null or a function key) ?
        MOVEQ   R7, #2
        BEQ     %FT01
        BL      read_current_alphabet
        BNE     %FT02                   ; except in UTF-8, all chars are one byte
        MOV     R0, R1
        BL      estimate_UTF8_char_len  ; may return length 0, but that's okay because we've already read one byte
        MOV     R7, R1
01      CMP     R3, R7
      |
01      CMP     R3, #6
      ]
        BHS     %FT02                   ; break from loop if full
        MOV     R0, #&81                ; OS_Byte 129 (INKEY)
        MOV     R1, #0
        MOV     R2, #0                  ; time limit 0
        SWI     XOS_Byte
        TEQ     R2, #&FF
        BEQ     %FT02                   ; break if no byte available
        STRB    R1, [R5, R3]            ; stick byte in my buffer
        ADD     R3, R3, #1
        B       %BT01
02      STRB    R3, keyin_buflen

        ; Get a key from our input buffer
        ADRL    R7, keyin_buflen        ; R5 already set up
        BL      get_internal_keycode_from_buffer
        CMP     R6, #-1                 ; was there one?
        BEQ     nothing                 ; no keys to handle

trykeys_gotinternalkeycode
        ; *All* Key_Pressed events now return caretdata as it is at this time
        ; - ie. before the writable icon handling messes around with it.
        ; This saves on the horrible kludge of storing the Key_PressedOldData
        ; event number in order to specify where to get the caret data from.
        Push    "R0-R5"
        ADR     R14, caretdata
        LDMIA   R14, {R0-R5}
        ADRL    R14, savedcaretdata
        STMIA   R14, {R0-R5}
        Pull    "R0-R5"

        ; Page in correct task
        CMP     handle, #nullptr
        BEQ     %FT01
        LDR     R14, [handle, #w_taskhandle]
        CMP     R14, #0
        LDRMI   R14, menutaskhandle
        Task    R14,, "KeyPressed"
01

        ; KeyboardMenus gets a look in first
      [ KeyboardMenus
        LDR     R4, menuSP
        CMP     R4, #0
        BLT     %FT00                   ; no menu open so don't grab special keys

        ADR     R5, menudata            ; if it's a dbox then don't grab keys
        LDR     R5, [R5, R4]
        TST     R5, #3
        BNE     %FT00

        ADR     R14, menuhandles        ; if menu has caret then don't grab keys
        LDR     R14, [R14, R4]
        LDR     R0, menucaretwindow
        TEQ     R0, R14
        BEQ     %FT00

        TEQ     R6, #13                 ; check for return and cursor keys
        BEQ     handlemenukey
        TST     R6, #1:SHL:31
        BEQ     %FT00
        BIC     R14, R6, #1:SHL:31
        TEQ     R14, #&8C
        TEQNE   R14, #&8D
        TEQNE   R14, #&8E
        TEQNE   R14, #&8F
        SUBEQ   R6, R14, #(&8C-136)     ; map onto range 136-139
        BEQ     handlemenukey
00
      ]

        ; Handle escape keypresses
        TEQ     R6,#&1B                 ; escape?
        BNE     %FT02
      [ KeyboardMenus
        CMP     r4, #0                  ; if no menu open then skip
        BLT     %FT02
        TST     r5, #3                  ; check for dialogue
        SUBNE   r0, r4, #4              ; if dialogue open then just remove it
        MOVEQ   r0, #-4                 ; otherwise remove whole menu tree
        CMP     r0, #0
        BLLT    menusdeleted            ; send Message_MenusDeleted if whole menu going (preserves r0)
        BL      closemenus
      |
        LDR     R0,menuSP
        CMP     R0,#0
        BLT     %FT02

        BL      menusdeleted            ; send Message_MenusDeleted
        MOVVC   R0,#-4
        BLVC    closemenus
      ]
        BVC     nothing                 ; absorb escape if used to close menus
        B       ExitPoll                ; report error if any

02      ; Examine caret state to determine what to do with keypress
        ADRL    R14, savedcaretdata
        LDMIA   R14, {R0-R5}
        CMP     R0, #nullptr
        BEQ     tryhotkeys              ; no input focus
        CMP     R1, #nullptr
        BNE     processkey              ; wimp gets first bash at this one!

keypressed
; Return a Key_Pressed event to the caret-owning task
        BL      prepare_external_key_event
        STMIA   userblk, {R0-R6}
        MOV     R0,#Key_Pressed         ; task handle already set up
        B       ExitPoll

tryhotkeys
; Initiate hotkey chain of events
        BL      prepare_external_key_event
        MOVGT   R0, #0
        STRGTB  R0, keyout_buflen       ; don't cache the other bytes if we're single-tasking!
        BLLE    int_processkey
        B       repollwimp


; Support routines for the above

prepare_external_key_event
; Munges internal keycode back into UTF-8 / external keycode format, and initialises hotkey system
; Entry: R6 = internal key code
; Exit:  R6 = first external key code
;        keyout_buffer filled with any remaining external keycodes
;        hotkeyptr set up (unless we're not multitasking)
;        LE => we are multitasking
        Entry   "R0,R4,R5"
        TST     R6, #1:SHL:31           ; was it a function key?
        ANDNE   R6, R6, #&FF
        ORRNE   R6, R6, #&100           ; convert into external form if so
        BNE     %FT01

        BL      read_current_alphabet   ; are we in UTF-8 alphabet?
        ANDNE   R6, R6, #&FF
        BNE     %FT01                   ; return bottom byte if not

        SUB     sp, sp, #8
        MOV     R5, sp
        BL      convert_UCS4_to_UTF8
        LDRB    R6, [sp]                ; first byte
        SUB     R4, R4, #1
        STRB    R4, keyout_buflen       ; held-over byte count
        ADD     R14, R5, R4             ; -> last byte in sequence
        ADRL    R4, keyout_buffer
02      LDRB    R0, [R5, #1]!
        STRB    R0, [R4], #1
        CMP     R5, R14
        BLO     %BT02
        ADD     sp, sp, #8

01      LDR     R14, singletaskhandle
        CMP     R14, #nullptr
        BLEQ    topmost_window          ; if we're multitasking, get top window handle
        STREQ   R0, hotkeyptr           ; and store it as the first hotkey window
        EXIT                            ; leave flags as they are

get_internal_keycode_from_buffer
; Reads a key from one of keyin_buffer, keyprocess_buffer or keystring_buffer,
; from a range appropriate to the current alphabet
; Skips any malformed characters
; Entry: R5 -> buffer
;        R7 -> buffer length byte
; Exit:  R6 = internal keycode : -1 means no character
;                                &800000xx represents a function key
;                                else character code ( <= &7FFFFFFF if UTF-8 alphabet, <= &FF otherwise )
        Entry   "R0-R4"
        LDRB    R3, [R7]                ; get buffer length
get_internal_keycode_restartpoint
        CMP     R3, #0                  ; nothing in my buffer?
        MOVLE   R6, #-1
        EXIT    LE

        LDRB    R6, [R5]
        TEQ     R6, #0                  ; first byte null => either a null or a function key
        BNE     get_internal_keycode_isntafunctionkey

get_internal_keycode_isafunctionkey
        TEQ     R3, #1                  ; if it's just one null byte, it's a malformed character
        MOVEQ   R4, #1
        MOVEQ   R6, #-1
        BEQ     get_internal_keycode_removefrombuffer

        LDRB    R6, [R5, #1]
        TEQ     R6, #0
        ORRNE   R6, R6, #1:SHL:31       ; unless a null, set top bit to indicate function key status
        MOV     R4, #2                  ; either way, it's a 2-byte character
        B       get_internal_keycode_removefrombuffer

get_internal_keycode_isntafunctionkey
        BL      read_current_alphabet
        MOVNE   R4, #1                  ; if not UTF-8, then it's got to be a valid 1-byte character
        BNE     get_internal_keycode_removefrombuffer

        MOV     R6, R5
        MOV     R4, R3
        BL      convert_UTF8_to_UCS4    ; sets up R4 and R6

get_internal_keycode_removefrombuffer
        ; R6 = internal Wimp key number, or -1 if malformed
        ; R4 = number of bytes of keyboard buffer it used
        SUB     R3, R3, R4              ; number of bytes that will still be in buffer after removal
        STRB    R3, [R7]
        ADD     R1, R5, R3              ; -> byte after final byte in buffer (after copying down)
        MOV     R2, R5                  ; rover
01      LDRB    R0, [R2, R4]
        STRB    R0, [R2], #1            ; copy down
        CMP     R2, R1
        BLO     %BT01

        CMP     R6, #-1                 ; was it a malformed character?
        BEQ     get_internal_keycode_restartpoint  ; swallow it if so!

        EXIT

 | ; else not UTF8...

 [ KeyboardMenus
        MOV     r6, #-1                 ; so we can tell later on if we read a key
        LDR     r4, menuSP
        CMP     r4, #0
        BLT     %FT00                   ; no menu open so don't grab special keys

        ADR     r5, menudata            ; if it's a dbox then don't grab keys
        LDR     r5, [r5, r4]
        TST     r5, #3
        BNE     %FT00

        ADR     r14, menuhandles        ; if menu has caret then don't grab keys
        LDR     r14, [r14, r4]
        LDR     r0, menucaretwindow
        TEQ     r0, r14
        BEQ     %FT00

        MOV     r0, #4                  ; allow us to read cursor keys
        MOV     r1, #1
        SWI     XOS_Byte
        MOV     r3, r1
        MOV     r0, #&81                ; INKEY(0)
        MOV     r1, #0
        MOV     r2, #0
        SWI     XOS_Byte
        MOV     r6, r1
        MOV     r7, r2
        MOV     r0, #4                  ; restore cursor key state
        MOV     r1, r3
        SWI     XOS_Byte
        CMP     r7, #&FF                ; was there a key?
        BEQ     nothing

        TEQ     r6, #13                 ; check for return and cursor keys
        TEQNE   r6, #136
        TEQNE   r6, #137
        TEQNE   r6, #138
        TEQNE   r6, #139
        BEQ     handlemenukey
 ]
00
        LDR     handle,caretdata+caretwindow
        CMP     handle,#nullptr
        BEQ     %FT01                   ; still read character (may be hot!)
;
        BL      checkhandle             ; check it hasn't been deleted!
        MOVVS   handle,#nullptr
        STRVS   handle,caretdata+caretwindow      ; if deleted, turn it off
        BVS     %BT00
;
        LDR     R14,[handle,#w_taskhandle]
        CMP     R14,#0
        LDRMI   R14,menutaskhandle

        LDR     R14,[wsptr,R14]
        LDR     R14,[R14,#task_flagword]
        TST     R14,#keypress_bit
        BNE     nothing                 ; temporarily disabled

01
 [ KeyboardMenus
        CMP     r6, #0
        BGE     %FT22
        BL      inkey0                  ; if we don't have a key yet then get one
        MOVS    r6, r1
22
 |
        BL      inkey0                  ; may branch elsewhere
        MOVS    R6,R1                   ; actual key value
 ]
        BNE     %FT01
        BL      inkey0                  ; may branch elsewhere!
        MOVS    R6,R1
        ADDNE   R6,R6,#&100             ; add &100 to the value (unless 0)
01
        LDR     R14,caretdata+caretwindow
        CMP     R14,#nullptr
        LDRNE   R14,[handle,#w_taskhandle]
        LDREQ   R14,menutaskhandle
        Task    R14,,"KeyPressed"       ; page in correct task

        TEQ     R6,#&1B                 ; escape?
        BNE     %FT02

 [ KeyboardMenus
        CMP     r4, #0                  ; if no menu open then skip
        BLT     %FT02
        TST     r5, #3                  ; check for dialogue
        SUBNE   r0, r4, #4              ; if dialogue open then just remove it
        MOVEQ   r0, #-4                 ; otherwise remove whole menu tree
        CMP     r0, #0
        BLLT    menusdeleted            ; send Message_MenusDeleted if whole menu going (preserves r0)
        BL      closemenus
 |
        LDR     R0,menuSP
        CMP     R0,#0
        BLT     %FT02

        BL      menusdeleted            ; send Message_MenusDeleted
        MOVVC   R0,#-4
        BLVC    closemenus
 ]
        BVC     nothing                 ; absorb escape if used to close menus
        B       ExitPoll                ; report error if any

02      ADR     R14,caretdata
        LDMIA   R14,{R0-R5}
        CMP     R0,#nullptr
        BEQ     tryhotkeys              ; no input focus
        CMP     R1,#nullptr
        BNE     processkey              ; wimp gets first bash at this one!

keypressed
        STMIA   userblk,{R0-R6}         ; including the key value
        LDR     R14,singletaskhandle
        CMP     R14,#nullptr
        BLEQ    topmost_window
        STREQ   R0,hotkeyptr
;

        MOV     R0,#Key_Pressed         ; task handle already set up
        B       ExitPoll

tryhotkeys
        LDR     R14,singletaskhandle
        CMP     R14,#nullptr

; Set hotkeyptr to topmost window

        BLEQ    topmost_window
        STREQ   R0,hotkeyptr
        BLEQ    int_processkey          ; R6 = key code
        B       repollwimp

inkey0
        Push    "LR"
        MOV     R0,#&81                 ; INKEY(0)
        MOV     R1,#0
        MOV     R2,#0
        SWI     XOS_Byte
        Pull    "LR"
        BVS     ExitPoll                ; wot?
;
        CMP     R2,#&FF                 ; was there a key?
        BEQ     nothing
        MOV     PC,LR

 ] ; end UTF8 conditional


 [ KeyboardMenus
; In:   r4 = menuSP
;       r5 -> menu data
;       r6 = key pressed
;
handlemenukey
        LDR     R14, menutaskhandle
        Task    R14,,"KeyPressed"       ; page in correct task

        LDR     r14, mousexpos
        STR     r14, lastxpos
        LDR     r14, mouseypos
        STR     r14, lastypos

        ADR     r14, menuselections
        LDR     r1, [r14, r4]           ; r1 = previous selection index

        TEQ     r6, #13
        BNE     trymenucursorkeys
        CMP     r1, #0                  ; handle return
        BLT     nothing                 ; do nothing if no selection
        STR     r4, whichmenu
        MOV     r2, #4                  ; fake SELECT mouse button
        B       gomenuselect

trymenucursorkeys
        TEQ     r6, #137                ; check for right (sub-menu)
        BNE     trymenuleft
        CMP     r1, #0                  ; handle right
        BLT     nothing
        ADD     r7, r1, r1, LSL #1      ; r7 = selected item * 3 (also used later)
        ADD     r6, r5, r7, LSL #3      ; r6 -> selected menu item
        LDR     r1, [r6, #mi_submenu]   ; r1 -> sub-menu data block
        AcceptLoosePointer_NegOrZero r1,0
        CMP     r1, r1, ASR #31         ; check for sub-menu/dbox
        BEQ     nothing
        ADR     r14, menuhandles        ; get menu window handle
        LDR     handle, [r14, r4]
        BL      checkhandle
        BVS     nothing
        LDR     r2, reversedmenu
        CMP     r2, #"\\"
        LDREQ   r2, [handle, #w_wax0]   ; get menu x
        LDRNE   r2, [handle, #w_wax1]
        LDR     r14, [handle, #w_icons]
        ADD     r14, r14, #i_bby1
        ADD     r7, r7, #1              ; r7 = middle icon
        LDR     r3, [r14, r7, ASL #i_shift]
        LDR     r14, [handle, #w_way1]
        ADD     r3, r3, r14             ; get menu y
        LDR     r14, [handle, #w_scy]
        SUB     r3, r3, r14             ; adjust for scrolling
 [ NCMenus
        ADD     r3, r3, #24             ; adjust for NCMenus border
 ]
        LDR     r14, [r6, #mi_mflags]
        TST     r14, #mif_warning
        BLNE    sendmenuwarning         ; preserves flags
        BLEQ    int_create_menu
        B       nothing

trymenuleft
        TEQ     r6, #136
        BLNE    trymenuupdown           ; preserves flags
        BNE     nothing
        SUBS    r0, r4, #4              ; handle left
        BLGE    closemenus
        B       nothing

trymenuupdown
; In:   r1 = current selected menu item
;       r4 = menuSP
;       r5 -> menu data
;       r6 = key pressed
; Out:  Could corrupt almost anything
;       Flags preserved
;
        EntryS
        MOV     r7, #0                  ; determine how many items in menu
00
        ADD     r7, r7, #1
        LDR     r0, [r5], #mi_size
        TST     r0, #mif_lastone
        BEQ     %BT00

        CMP     r1, #-1
        SUBEQ   r1, r7, #1
        MOV     r0, r1                  ; start with current menu item
trynextupdown
        TEQ     r6, #138                ; check for down
        BNE     trymenuupkey
        ADD     r0, r0, #1              ; handle down key
        CMP     r0, r7
        MOVGE   r0, #0                  ; wrap if necessary
        B       %FT10

trymenuupkey
        TEQ     r6, #139                ; check for up
        BNE     %FT20
        SUBS    r0, r0, #1              ; handle up key
        SUBLT   r0, r7, #1              ; wrap if necessary
10
        TEQ     r0, r1                  ; if all menu items grey then don't loop forever
        BEQ     %FT20

        ADR     r14, menuhandles
        LDR     handle, [r14, r4]       ; get menu window handle
        BL      checkhandle
        BVS     %FT20
        Push    "r6,r7"
        BL      menuhighlight
        Pull    "r6,r7"
        BVS     %FT20
        TEQ     r14, #0
        BEQ     trynextupdown           ; if item greyed out then try next
20
        EXITS
 ]

;----------------------------------------------------------------------------
; Return to next task with null event
; The 'PollIdle' class of tasks is considered first,
; followed by the 'Poll' tasks (if none of the others have timed-out)
; The current task is taken as the starting point.
;----------------------------------------------------------------------------

nothing
      [ redrawlast
        B       check_redraw

check_null
      ]

        SWI     XOS_ReadMonotonicTime
        BVS     ExitPoll                ; wot?
;
; if an old-style program is running, the other clients are ignored
;
        LDR     R14,polltaskhandle
        LDR     R5,singletaskhandle
        TEQ     R14,R5
        BEQ     nullexit
;
; Wimp_Poll treated as Wimp_PollIdle but with immediate timeout
;
        LDR     R5,nulltaskhandle
        ADD     R5,wsptr,R5             ; R5 --> current task data
        ADD     R5,R5,#4                ; start at next one along
        ADRL    R7,taskpointers+maxtasks*4 ; R7 = wrap address
        MOV     R6,#maxtasks            ; R6 = number to try
01
        CMP     R5,R7
        ADRCSL  R5,taskpointers
        LDR     R4,[R5],#4
        TST     R4,#task_unused
        LDREQ   R14,[R4,#task_flagword]
        TSTEQ   R14,#null_bit
        BNE     %FT02
        TST     R14,#flag_pollidle
        BEQ     returnnull
        LDR     R2,[R4,#task_registers+2*4]
        CMP     R0,R2
        BPL     returnnull              ; time's up! (use PL not CS)
02
        SUBS    R6,R6,#1
        BNE     %BT01
        B       triggercallbacks        ; no 'null return' tasks

returnnull

        SUB     R14,R5,#4
        SUB     R14,R14,wsptr           ; R14 = task handle
        STR     R14,nulltaskhandle
        Task    R14,,"Null"

        MOV     R0,#No_Reason           ; null reason code
        B       ExitPoll

nullexit
        LDR     R14,flagword            ; single-tasking exit
        TST     R14,#null_bit
        BNE     triggercallbacks
        MOV     R0,#No_Reason           ; null reason code
        B       ExitPoll

triggercallbacks
        ; No null events to deliver, so try triggering callbacks
      [ UseLeaveOS
        SWI     XOS_LeaveOS
      |
      [ Medusa
        MRS     r0, CPSR
        BIC     r0, r0, #&F             ; drop to same 32-bitness of USR mode
        MSR     CPSR_c, r0
      |
        TEQP    pc, #0
        NOP
      ]
        SWI     XOS_IntOn               ; callbacks will be triggered on exit
      ]
        SWI     XOS_EnterOS
        B       repollwimp

;-----------------------------------------------------------
; Remove task from list of tasks with pollwords
;
; In:
;   handle = task to remove (if found)
;
; Out:
;   None, preserves flags
;
;-----------------------------------------------------------


DeletePollTask  EntryS "R0,R1"
        ADR     R0,PollTasks            ; Get -> start of polltask list
        LDR     R1,PollTaskPtr          ; Get -> first free slot
01
        CMP     R0,R1                   ; End of list?
        EXITS   HS                      ; Yes then exit, not found

        LDR     LR,[R0],#4              ; Get polled task handle
        TEQ     LR,handle               ; Match?
        BNE     %BT01                   ; No then try next polled task

        LDR     LR,[R1,#-4]!            ; Get last entry
        STR     LR,[R0,#-4]!            ; Overwrite current entry
        STR     R1,PollTaskPtr          ; New end of list

        Debug   poll,"Delete poll: handle, posn, new, end",handle,R0,LR,R1

        EXITS



;;----------------------------------------------------------------------------
;; Check tasks' poll words to see if they are non-zero
;;----------------------------------------------------------------------------

; In    R0 = mask word to apply to poll words (only consider if word & R0 set)
; Out   NE => R0=reason code, correct task paged in
;       [userblk,#0] = poll word address
;       [userblk,#4] = poll word contents, as read (once) by Wimp


scanpollwords   Entry "R1-R4"
        ADR     R1,PollTasks            ; Get -> start of polltask list
        LDR     R2,PollTaskPtr          ; Get -> first free slot
01
        TEQ     R1,R2                   ; End of list?
        EXIT    EQ                      ; Yes then exit, not found

        LDR     LR,[R1],#4              ; Read polled task handle
        LDR     R3,[LR,wsptr]           ; Read address of task data
        LDR     R3,[R3,#task_pollword]  ; Task pollword
        TST     R3,R0                   ; NE => consider this
        BICNE   R3,R3,#1                ; Can't combine this with LDR because non-rotated value mus be stored below
        LDRNE   R4,[R3]                 ; Read pollword
        TEQNE   R4,#0                   ; NE => word non-zero!
        BEQ     %BT01                   ; Jump if not found

        Debug   poll,"Pollword non-zero: task,list,address",LR,R1,R3

        Task    LR,,"Poll word"         ; preserves flags
        MOV     R0,#PollWord_NonZero    ; R0 = reason code
        STMIA   userblk,{R3,R4}

        EXIT                            ; Returns NE




;-----------------------------------------------------------------------------

pendingtime
        Push    "R2,LR"
;
        LDR     R14,timeblk
        LDR     R2,pending_time
        SUB     R14,R14,R2              ; R14 = elapsed time since 1st press
;
        Pull    "R2,PC"                 ; preserves flags


;----------------------------------------------------------------------------
; Routine to select an icon
; Also deselects any other icons in the same ESG
; Entry:  handle,R4 = window/icon to select
;         R2 = button state causing selection
;----------------------------------------------------------------------------

selecticon
        EntryS  "R0-R8"
;
        LDR     R5,[handle,#w_icons]
        ADD     R5,R5,#i_flags

        LDR     R6,[R5,R4,ASL #i_shift]         ; get flags
        ANDS    R14,R6,#if_esg2                 ; get ESG field
        MOVEQ   R2,#0                           ; invert state if on it's own
        BEQ     goselect
;
        TST     R6,#if_canadjust                ; can we select multiply?
        TSTNE   R2,#button_right
        BNE     dontcancel                      ; allow 'adjust' to work
        MOV     R6,R14                          ; get ESG
;
        LDR     R8,[handle,#w_nicons]
sellp
        SUBS    R8,R8,#1
        BMI     dontcancel                      ; finished
        CMP     R8,R4
        BEQ     sellp                           ; ignore this one
;
        LDR     R7,[R5,R8,ASL #i_shift]
        AND     R14,R7,#if_esg2                 ; see if same ESG
        TEQ     R14,R6
        BNE     sellp
;
        TST     R7,#is_inverted
        MOVNE   R0,R8
        MOVNE   R1,#0                   ; EOR value
        MOVNE   R2,#is_inverted         ; BIC value
        BLNE    int_set_icon_state
        B       sellp

dontcancel
        LDR     R2,[R13,#Proc_RegOffset+8] ; get button state again!
        TST     R2,#button_right
        MOVNE   R2,#0
        BNE     goselect                ; invert state if using ADJUST
;
        LDR     R14,[R5,R4,ASL #i_shift]        ; don't bother if already done
        TST     R14,#is_inverted
        BNE     exitselect
;
        MOV     R2,#is_inverted         ; BIC value (ie. set this state)
goselect
        MOV     R0,R4                   ; icon handle
        MOV     R1,#is_inverted         ; EOR value
        BL      int_set_icon_state
;
exitselect
        STRVS   R0,[R13,#Proc_RegOffset]
        EXITV                           ; preserve V


;;----------------------------------------------------------------------------
;; Wimp system icon detection
;; Entry:  R0,R1 = coords of mouse pointer
;;         R4 = -2 (in system area)
;;         handle -> window definition
;; Exit :  R4 = -2 to -n (corresponding to which icon the pointer is over)
;;         R5 = appropriate flag word
;;         other registers preserved (inc. flags)
;;----------------------------------------------------------------------------

setwimpicon
        CMP     R3,#nullptr
        MOVEQ   PC,LR                           ; forget it!
;
        Push    "R2,R3,x0,y0,x1,y1,LR"
;
        MOV     R3,#wf_icon1
        MOV     R4,#1
        LDR     R2,[handle,#w_flags]
01
        TST     R2,R3
        BEQ     %FT02
05
        Push    "R0,R1"
        MOV     R0,R4
        BL      calc_w_iconposn
        Pull    "R0,R1"
        CMP     R0,x0
        CMPGT   R1,y0
        CMPGT   x1,R0
        CMPGT   y1,R1
        BLE     %FT02
;        Debug   ub,"on an icon !"
;
        CMP     R4,#5                   ; icon 5 is really 3 icons
        BEQ     check_v_arrows
        ADDHI   R4,R4,#2
        CMP     R4,#7+2                 ; fudge!
        BEQ     check_h_arrows          ; icon 7 is also really 3 icons
      [ IconiseButton
        ADDHI	R4,R4,#3		; and skip -13 (which is the border)
      ]
03
      [ BounceClose
        LDRB    R14, buttontype
        TEQ     R14, #0
        ADREQ   R14, wiconflags - 4
        ADRNE   R14, wiconflags_release - 4
      |
        ADR     R14,wiconflags-4
      ]
        LDR     R5,[R14,R4,ASL #2]
        CMP     R5,#-1
        MOVEQ   R5,#ibt_click:SHL:ib_buttontype
        TSTEQ   R2,#wf_userscroll2      ; debounced form of userscroll
        MOVEQ   R5,#ibt_autorepeat:SHL:ib_buttontype
        MOV     R14,#-1
        SUB     R4,R14,R4               ; R4 in range -2 to -n
        Pull    "R2,R3,x0,y0,x1,y1,PC"

check_v_arrows
      [ ChildWindows
        LDR     R14,up_height           ; if scrollbar is small, only the arrows are shown (scaled down)
        LDR     R5,down_height
        ADD     R5,R14,R5
        SUB     R14,y1,y0
        CMP     R5,R14
        BLE     %FT10

        ADD     R14,y0,R14,LSR #1
        CMP     R1,R14
        ADDLT   R4,R4,#2                ; down arrow
        B       %BT03
10
      ]
        LDR     R14,up_height
        SUB     R14,y1,R14
        CMP     R1,R14
        ADDLT   R4,R4,#1                ; not up-arrow
        LDR     R14,down_height
        ADD     R14,y0,R14
        CMP     R1,R14
        ADDLT   R4,R4,#1                ; not middle bit
        B       %BT03

check_h_arrows
      [ ChildWindows
        LDR     R14,left_width          ; if scrollbar is small, only the arrows are shown (scaled down)
        LDR     R5,right_width
        ADD     R5,R14,R5
        SUB     R14,x1,x0
        CMP     R5,R14
        BLE     %FT11

        ADD     R14,x0,R14,LSR #1
        CMP     R0,R14
        ADDGT   R4,R4,#2                ; right arrow
        B       %BT03
11
      ]
        LDR     R14,left_width
        ADD     R14,x0,R14
        CMP     R0,R14
        ADDGT   R4,R4,#1                ; not left-arrow
        LDR     R14,right_width
        SUB     R14,x1,R14
        CMP     R0,R14
        ADDGT   R4,R4,#1                ; not middle bit
        B       %BT03

02
        MOVS    R3,R3,ASL #1
        ADD     R4,R4,#1
        CMP     R4,#8
        BLO     %BT01
      [ IconiseButton
	BHI	%FT04
	LDR	R2,[handle,#w_flags]
	TST	R2,#wf_icon2
	BEQ     %FT04
	LDR     R2, [handle, #w_parent]
	CMP     R2, #-1
	BNE     %FT04
	LDRB    R2, iconisebutton
	TEQ     R2, #0
	BNE     %BT05
04
      ]
;
        MOV     R4,#bordericon                          ; in the border
        MOV     R5,#ibt_never :SHL: ib_buttontype       ; has no effect
        Pull    "R2,R3,x0,y0,x1,y1,PC"


wiconflags
        DCD     ibt_click        :SHL: ib_buttontype        ; back
        DCD     ibt_click        :SHL: ib_buttontype        ; quit
        DCD     ibt_click        :SHL: ib_buttontype        ; move BLOBBY
        DCD     ibt_click        :SHL: ib_buttontype        ; toggle
        DCD     -1                                          ; scup
        DCD     ibt_click        :SHL: ib_buttontype        ; scvert
        DCD     -1                                          ; scdown
        DCD     ibt_click        :SHL: ib_buttontype        ; size
        DCD     -1                                          ; scleft
        DCD     ibt_click        :SHL: ib_buttontype        ; schoriz
        DCD     -1                                          ; scright
bordericon      *       -2 - (.-wiconflags)/4
	[ IconiseButton
	DCD	-1
	DCD	ibt_click        :SHL: ib_buttontype	    ; iconise
	]

    [ BounceClose
wiconflags_release
        DCD     ibt_clickrelease :SHL: ib_buttontype        ; back
        DCD     ibt_clickrelease :SHL: ib_buttontype        ; quit
        DCD     ibt_click        :SHL: ib_buttontype        ; move BLOBBY
        DCD     ibt_clickrelease :SHL: ib_buttontype        ; toggle
        DCD     -1                                          ; scup
        DCD     ibt_click        :SHL: ib_buttontype        ; scvert
        DCD     -1                                          ; scdown
        DCD     ibt_click        :SHL: ib_buttontype        ; size
        DCD     -1                                          ; scleft
        DCD     ibt_click        :SHL: ib_buttontype        ; schoriz
        DCD     -1                                          ; scright
	[ IconiseButton
	DCD	-1
	DCD	ibt_clickrelease :SHL: ib_buttontype	    ; iconise
	]
    ]

wimpaction
        CMP     R3,#nullptr             ; is this a real window?
        BLNE    pageinicontask_R3R4     ; takes note of iconbar

        TST     R2,#button_left:OR:button_right
        BEQ     trykeys                         ; menu not allowed here!
        ADR     R14,wiconjump-8
        SUB     PC,R14,R4,ASL #2                ; wow!

wiconjump
        B       wicon_back
        B       wicon_quit
        B       wicon_move
        B       wicon_toggle
        B       wicon_scup
        B       wicon_scvert
        B       wicon_scdown
        B       wicon_size
        B       wicon_scleft
        B       wicon_schoriz
        B       wicon_scright
	[ IconiseButton
	MOV	PC,#0
	B	wicon_iconise
	]


;;----------------------------------------------------------------------------
;; jump destinations for possible wimp icons
;;----------------------------------------------------------------------------

wicon_size
        MOV     R0,#drag_size
        BL      int_drag_box            ; handle,R0 are params
        B       trykeys

wicon_move
        MOV     R0,#drag_posn
        BL      int_drag_box            ; handle,R0 are params
        B       trykeys

wicon_toggle
        MOV     R1,#wf_isapane          ; we want to ignore panes
        BL      go_get_window_state     ; 2nd entry point - uses R1
        LDR     R14,[handle,#w_flags]
      [ togglebits
        TST     R14,#ws_toggled :OR: ws_toggled2
        BIC     R14,R14,#ws_toggled2
      |
        TST     R14,#ws_toggled
      ]
        ORR     R14,R14,#ws_toggling     ; set up to toggle when opened
        ORREQ   R14,R14,#ws_onscreenonce ; keep on-screen this time
        STR     R14,[handle,#w_flags]
        BNE     makesmall
;
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        ADD     R14,handle,#w_bwax0     ; remember old position
        STMIA   R14!,{cx0,cy0,cx1,cy1}
        LDR     x0,[handle,#w_scx]
        LDR     y0,[handle,#w_scy]
        LDR     x1,[handle,#w_bhandle]

        STMIA   R14,{x0,y0,x1}
;
        ADD     R14,handle,#w_wex0      ; add max size to get other coords
        LDMIA   R14,{x0,y0,x1,y1}
        SUB     x1,x1,x0
        SUB     y1,y1,y0
        ADD     cx1,cx0,x1
        SUB     cy0,cy1,y1

; Now check to see if <shift> is depressed, either will do.  If it
; is then attempt to toggle the window to a sensible size, not obscuring
; the icon bar.

        Push    "R0-R2,R4,R6"

      [ ChildWindows
        LDR     R14,[handle,#w_parent]
        CMP     R14,#nullptr
        BNE     %FT01                   ; this stuff only applies to top-level windows
      ]

	LDR	R4,iconbarheight
	SUB	R4,R4,#8;-40
	LDR	R14,[handle,#w_flags]
	TSTS	R14,#wf_icon7
        LDRNE   R14,hscroll_height
        ADDNE   R4,R4,R14               ; area not to be obscured

        SUBS    R6,cy0,R4               ; does the bottom of the window obscure the icon bar?
        BGE     %FT01                   ; if not then don't bother faffing around

        MOV     R0,#121
        MOV     R1,#&80
        SWI     XOS_Byte                ; scan for shift being depressed

      [ togglebits
        Push    "R1"
        MOV     R0,#ReadCMOS
        MOV     R1,#FileSwitchCMOS
        SWI     XOS_Byte                ; check for special <shift> toggle action
        TST     R2,#WimpShiftToggleCMOSBit
        Pull    "R1"
        EORNE   R1,R1,#255              ; if active then invert the action
      ]

        TEQ     R1,#&FF
        BNE     %FT01                   ; if it isn't then ignore the munging of the bits

        MOV     cy0,R4
        SUB     cy1,cy1,R6              ; setup the new co-ordinates for bottom & top Y's

	LDR	R6,scry1
      [ togglebits
        LDR     R14, [handle, #w_flags]
        TST     R14, #wf_icon3          ; we can still have a toggle-size button without a title bar!
        LDRNE   R14, title_height
        SUBNE   R6, R6, R14
      |
	LDR	R14,title_height
	SUB	R6,R6,R14
      ]
	LDR	R14,iconbarheight
	SUB	R14,R14,#8
	SUB	R0,R6,R14
	LDR	R14,[handle,#w_flags]
	TSTS	R14,#wf_icon7
	LDRNE	R14,hscroll_height
	SUBNE	R0,R0,R14		; R0 = max window height that can fit on screen
	CMPS	y1,R0
	MOVGT	cy1,R6		        ; window won't fit at maximum extent, so carefully trim
	SUBGT	y1,cy1,cy0

      [ togglebits
        LDR     R14,[handle,#w_flags]   ; set toggled in silly way bit
        ORR     R14,R14,#ws_toggled2
        STR     R14,[handle,#w_flags]
      ]
01
        Pull    "R0-R2,R4,R6"

        ADD     R14,userblk,#u_wax0
        STMIA   R14,{cx0,cy0,cx1,cy1}
;
      [ togglebits
        SUB     cx0,cx1,cx0
        SUB     cy0,cy1,cy0             ; work out the area really shown
;
        ASSERT  w_toggleheight = w_togglewidth +4
        ADD     R14,handle,#w_togglewidth
        STMIA   R14,{cx0,cy0}           ; push the co-ordinates away
      ]
;
        BL      calc_w_status           ; ensure bhandle up-to-date

      [ BounceClose
        LDR     R2,pending_buttons      ; Horrible hack so adjust works even with release buttons
      |
        LDR     R2,mousebuttons         ; bhandle depends on adjust button
      ]

openwindow_checkbuttons
        TST     R2,#button_right        ; if adjust used, open at same level,
      [ true
        LDR     R14,[handle,#w_bhandle]
        BNE     %FT02
        ; first the special cases: if *this* window is a pane or a foreground window, use a bhandle of -1
        MOV     R4,#wf_isapane
        ORR     R4,R4,#wf_inborder      ; wf_isapane:OR:wf_inborder isn't a valid immediate constant
        LDR     R3,[handle,#w_flags]
        TST     R3,R4
        MOVNE   R14,#nullptr
        BNE     %FT02
        ; now check the windows above us in our window stack: if they're all either panes or foreground
        ; windows, then don't attempt any reordering, otherwise we end up in an endless loop opening
        ; panes in front of their main windows and vice versa, and nobody else gets any processor time
        LDR     R1,[handle,#w_active_link+ll_backwards]
01      LDR     R2,[R1,#ll_backwards]
        CMP     R2,#nullptr             ; if we've reached the front of the window stack
        BEQ     %FT02                   ; then break leaving R14 = current bhandle
        LDR     R3,[R1,#w_flags-w_active_link]
        TST     R3,R4
        MOVNE   R1,R2
        BNE     %BT01
        MOV     R14,#nullptr            ; found a non-pane non-foreground window: move drag window to top of stack
02
      |
        MOVEQ   R14,#nullptr            ; otherwise open window at front
        LDRNE   R14,[handle,#w_bhandle]
      ]
        STR     R14,[userblk,#u_bhandle]
;
        B       Exit_OpenWindow


makesmall
        ADD     R14,handle,#w_bwax0
        LDMIA   R14,{cx0,cy0,cx1,cy1,x0,y0,x1}  ; Load previous x0,y0,x1,y1,scx,scy,bhandle

        ; SMC: 18th January 1995
        ; Correct fix for MED-03674, original fix (amg: 28th September 1994) was too simple
        ; and caused MED-04376 which the following also fixes.

        CMP     x1, #nullptr                    ; If this window was at the top
        BEQ     %FT20                           ; then open it there.

        Push    "r2,r3"
      [ ChildWindows
        LDR     r14, [handle, #w_parent]            ; depending on whether this window is at the top-level,
        CMP     r14, #nullptr
        LDREQ   r2, activewinds+lh_forwards         ; scan the list of topmost windows
        LDRNE   r2, [r14, #w_children+lh_forwards]  ; or the list of siblings
      |
        LDR     r2, activewinds+lh_forwards     ; Otherwise, scan down the list of windows.
      ]
        BIC     r3, x1, #3                      ; Align bhandle for comparisons (should really use the Abs macro)
10
        LDR     r2, [r2, #ll_forwards]
        CMP     r2, #nullptr                    ; If we hit the end,
        LDRNE   r14, [r2, #w_flags-w_active_link]
        ANDNE   r14, r14, #wf_backwindow
        TEQNE   r14, #wf_backwindow             ; or if we hit a backwindow
        MOVEQ   x1, #-2                         ; then open our window at the bottom.
        SUBNE   r14, r2, #w_active_link
        TEQNE   r14, handle                     ; If we hit this window???
        TEQNE   r14, r3                         ; or the one to open behind then drop out and open behind stored window
        BNE     %BT10                           ; else go round loop again.
        Pull    "r2,r3"
20

        ADD     R14,userblk,#u_wax0
        STMIA   R14,{cx0,cy0,cx1,cy1,x0,y0,x1}
        B       Exit_OpenWindow

wicon_back
        BL      int_get_window_state
      [ true
        MOV     R0, #121
        MOV     R1, #&80
        SWI     XOS_Byte                ; Scan keyboard for Shift key
        TEQ     R1, #&FF
      ]
      [ true :LOR: RO4
        LDR     R14,pending_buttons
        AND     R14,R14,#button_right
        BEQ     %FT12                   ; Branch if so
        ASSERT  button_right = (nullptr - nullptr2)
11      ADD     R14,R14,#nullptr2
      |
        MOV     R14,#nullptr2                   ; will not match
      ]
        STR     R14,[userblk,#u_bhandle]        ; so will go to bottom
        B       Exit_OpenWindow

      [ true
12      LDR     R0,[userblk,#u_handle]
        Abs     R0,R0
        ADD     R0,R0,#w_active_link
        MOV     R2,#wf_isapane
        ORR     R2,R2,#wf_grabkeys
        TEQ     R14,#button_right
        MOVEQ   R3,#ll_backwards
        MOVNE   R3,#ll_forwards
        BNE     %FT14                   ; need to search 1 window down, or 2 windows up
13      LDR     R0,[R0,R3]
        LDR     R1,[R0,R3]
        CMP     R1,#nullptr             ; hit the stops?
        BEQ     %BT11                   ; yes, so behave as though shift wasn't pressed
        LDR     R1,[R0,#w_flags - w_active_link]
        TST     R1,R2
        BNE     %BT13                   ; skip over any panes or hotkey windows
14      LDR     R0,[R0,R3]
        LDR     R1,[R0,R3]
        CMP     R1,#nullptr             ; hit the stops?
        BEQ     %BT11                   ; yes, so behave as though shift wasn't pressed
        LDR     R1,[R0,#w_flags - w_active_link]
        TST     R1,#wf_backwindow       ; hit a backwindow (presumably going down)?
        BNE     %BT11                   ; don't open behind it!
        TST     R1,R2
        BNE     %BT14                   ; skip over any panes or hotkey windows
        SUB     R0,R0,#w_active_link
        Rel     R0,R0
        STR     R0,[userblk,#u_bhandle]
        B       Exit_OpenWindow
      ]

      [ IconiseButton
wicon_iconise
        Push	"r0-r4"
      [ MultiClose
        TST     R2, #button_right       ; Ctrl-Alt-*Adjust* isn't special
        MOVNE   R1, #0                  ; flag as due to iconise button
        BNE     %FT20

        MOV     R0, #121
        MOV     R1, #&81                ; Scan keyboard for Ctrl key
        SWI     XOS_Byte
        MOV     R4, R1
        MOV     R1, #&82                ; Scan keyboard for Alt key
        SWI     XOS_Byte
        TEQ     R4, #&FF
        TEQEQ   R1, #&FF
        MOVNE   R1, #0                  ; flag as due to iconise button
        BNE     %FT20

        ; Iconise all top-level windows that have close buttons (except hotkey windows)
        LDR     handle, activewinds + lh_forwards
        SUB     R0, handle, #w_active_link
        Rel     R0, R0
        STR     R0, nextwindowtoiconise
        BL      iconisenextwindow
        Pull    "R0-R4"
        B       trykeys

      |
        MOV     R1, #0                  ; NOT a shift+click on close icon
        B	%FT20
      ]
      ]

wicon_quit
        Push    "r0-r4"

        [ StickyMenus
        LDR     R0,[handle,#w_taskhandle]
        CMP     R0,#-1
        Pull    "r0-r4",EQ
        BEQ     not_alt                 ; can't iconise menu
        ]

        TST     R2,#button_right        ; If adjust, ignore it.
        Pull    "r0-r4",NE
        BNE     not_alt

      [ MultiClose
        MOV     R0, #121
        MOV     R1, #&81                ; Scan keyboard for Ctrl key
        SWI     XOS_Byte
        MOV     R4, R1
        MOV     R1, #&82                ; Scan keyboard for Alt key
        SWI     XOS_Byte
        TEQ     R4, #&FF
        TEQEQ   R1, #&FF
        BNE     %FT66

      [ BounceClose
        LDR     R2, pending_buttons
        STR     R2, mousebuttons        ; The usual Horrible Hack
      ]
        ; Close all windows at this level that have close buttons (except hotkey windows)
        LDR     handle, [handle, #w_parent]
        CMP     handle, #nullptr
        LDREQ   handle, activewinds + lh_backwards
        LDRNE   handle, [handle, #w_children + lh_backwards]
01      LDR     R0, [handle, #ll_backwards]
        CMP     R0, #nullptr
        BEQ     %FT02
        LDR     R14, [handle, #w_flags - w_active_link]
        TST     R14, #wf_icon2
        ANDNE   R14, R14, #wf_grabkeys
        TEQNE   R14, #wf_grabkeys
        MOVEQ   handle, R0
        BEQ     %BT01
        ; Found such a window; send close request message
        Push    "R0"
        SUB     handle, handle, #w_active_link
        SUB     sp, sp, #4
        Rel     R2, handle
        STR     R2, [sp]
        MOV     R0, #Close_Window_Request
        MOV     R1, sp
        BL      int_sendmessage_fromwimp
        ADD     sp, sp, #4
        Pull    "handle"
        B       %BT01
02
        Pull    "R0-R4"
        B       trykeys
66
      ]

        MOV     R0,#121                 ; Scan keyboard for shift key
        MOV     R1,#&80
        SWI     XOS_Byte

        CMP     R1,#&FF

        Pull    "r0-r4",NE
        BNE     not_alt

        [ ChildWindows
        LDR     R0, [handle, #w_parent] ; don't do anything if Shift-close on child windows
        CMP     R0, #-1
        Pull    "r0-r4",NE
        BNE     trykeys
        ]
20
      [ MultiClose
        LDR     r2, mousexpos
        LDR     r4, mouseypos
      ]
        BL      sendiconisemessages
        Pull    "r0-r4"
        B       trykeys

not_alt
        [ StickyMenus
;        LDR     R0,[handle,#w_taskhandle]
;        CMP     R0,#-1
;        BNE     %FT05
        LDR     R14,menuSP
        CMP     R14,#-4
        BEQ     %FT05                           ; no menu open

        LDR     R14,menuhandles
        Abs     R14,R14
        TEQ     handle,R14

        BNE     %FT02
        BL      menusdeleted
        MOVVC   R0,#-4
        BLVC    closemenus
        B       nothing                         ; swallow it.
02
        LDR     R14,menuSP
        ADD     R14,R14,#4
        ADR     R0,menuhandles
03
        LDR     R1,[R0,R14]
        Abs     R1,R1
        TEQ     R1,handle
        BEQ     %FT04
        SUBS    R14,R14,#4
        BGE     %BT03
        B       %FT05
04
        SUB     R0,R14,#4
        BL      closemenus              ; close down to one above
        B       nothing
05
        ]
	[ BounceClose
	STR	R2,mousebuttons		; Horrible hack so apps can tell that adjust was pressed
	]
        STR     R3,[userblk]
        MOV     R0,#Close_Window_Request
        B       ExitPoll

      [ MultiClose
iconisenextwindow
        Entry   "R0-R4,handle"
        LDR     handle, nextwindowtoiconise
        BL      checkhandle             ; make sure it's still valid
        TEQVS   PC, #0                  ; \ set Z = not (V)
        TEQVC   R0, R0                  ; /
        LDREQ   R14, [handle, #w_flags]
        ANDEQ   R0, R14, #ws_open
        CMPEQ   R0, #ws_open            ; and open
        ADDEQ   handle, handle, #w_active_link
        LDRNE   handle, activewinds + lh_forwards ; if not, restart from top
01
        LDR     R0, [handle, #ll_forwards]
        CMP     R0, #nullptr             ; have we reached the header?
        MOVEQ   R0, #0
        STREQ   R0, nextwindowtoiconise
        EXIT    EQ                       ; stop if we have
        LDR     R14, [handle, #w_flags - w_active_link]
        TST     R14, #wf_backwindow
        MOVNE   R0, #0
        STRNE   R0, nextwindowtoiconise
        EXIT    NE                       ; don't re-iconise already-iconised windows!
        TST     R14, #wf_icon2
        ANDNE   R14, R14, #wf_grabkeys
        TEQNE   R14, #wf_grabkeys
        MOVEQ   handle, R0
        BEQ     %BT01
        ; Found a suitable window; work out suitable entry conditions for sendiconisemessages
        Push    "R0"
        SUB     handle, handle, #w_active_link
        MOV     R0, #iconposn_iconise
        BL      calc_w_iconposn          ; x0-y1 = button bbox
        ADD     R2, x0, x1
        ADD     R4, y0, y1
        MOV     R2, R2, LSR#1
        MOV     R4, R4, LSR#1
        MOV     R1, #0
        Rel     R3, handle
        BL      sendiconisemessages
        ; Now store the next window to process (if any)
        Pull    "handle"
        LDR     R0, [handle, #ll_forwards]
        CMP     R0, #nullptr
        MOVEQ   R0, #0
        SUBNE   R0, handle, #w_active_link
        Rel     R0, R0, NE
        STR     R0, nextwindowtoiconise
        EXIT
      ]

sendiconisemessages
; Entry: R1 = &FF => caused by click on close icon; R1 = 0 => caused by click on iconise icon
;        R3 = relative window handle to send
; and if MultiClose enabled,
;        R2 = mouse x
;        R4 = mouse y
        Entry

        [ IconiseButton
        ; Send a Message_IconizeAt (RML)
        Push    "r3"
        SUB     sp, sp, #48

        CMP     r1, #&FF                 ; If a Shift+Click on close
        MOVEQ   r0, #1                   ; then bit 0 of flags is set
        MOVNE   r0, #0
        STR     r0, [sp, #ms_data+16]

        MOV     r1, sp
      [ MultiClose
        STR     r2, [r1, #ms_data+8]
        SUB     r4, r4, #32+17
        STR     r4, [r1, #ms_data+12]
      |
        LDR     r0, mousexpos
        STR     r0, [r1,#ms_data+8]
        LDR     r0, mouseypos
        SUB     r0, r0, #(32+17)
        STR     r0, [r1,#ms_data+12]
      ]
        MOV     r0, #40
        STR     r0, [r1,#ms_size]        ; size
        MOV     r0, #0
        STR     r0, [r1, #ms_yourref]    ; your ref
        LDR     r0, =Message_IconizeAt   ; (Message number 22)
        STR     r0, [r1, #ms_action]     ; message action
        STR     r3, [r1, #ms_data+0]     ; window handle

        ; Work out task handle
      [ MultiClose
        LDR     R2, [handle, #w_taskhandle]
      |
        LDR     r2, taskhandle
      ]
        LDR     r4, [wsptr, r2]          ; add in version bits (if alive)
        TST     r4, #task_unused
        LDREQ   r4, [r4, #task_flagword]
        MOVEQ   r4, r4, LSR #flag_versionbit
        ORREQ   r2, r2, r4, LSL #flag_versionbit
        STR     r2, [r1, #ms_data+4]

        MOV     r0, #User_Message
        MOV     r2, #0
        BL      int_sendmessage_fromwimp

        ADD     sp, sp, #48
        Pull    "r3"
        ]
                                                ; Shift-quit, send Message_Iconize.
        MOV     R14,#ms_data+8+20       ; size of block (Window,Task,20 byte from title ).
        STR     R14,[sp,#-(ms_data+8+20)]!
        MOV     R0,#User_Message
        MOV     R1,sp
        STR     R3,[R1,#ms_data]

      [ MultiClose
        LDR     R2, [handle, #w_taskhandle]
        LDR     R14, [handle, #w_titleflags]
        TST     R14, #if_indirected
        Task    "R2", NE, "Iconise: reading indirected title string"
      |
        LDR     R2,taskhandle
      ]
        LDR     R14,[wsptr,R2]          ; add in version bits (if alive)
        TST     R14,#task_unused
        LDREQ   R14,[R14,#task_flagword]
        MOVEQ   R14,R14,LSR #flag_versionbit
        ORREQ   R2,R2,R14,LSL #flag_versionbit
        STR     R2,[R1,#ms_data+4]

        ADD     R2,handle,#w_title
        LDR     R14,[handle,#w_titleflags]
        TST     R14,#if_indirected
        LDRNE   R2,[R2]                     ; If indirected get real address

00      LDRB    R3, [R2]                    ; always skip leading spaces
        TEQ     R3, #&20
        ADDEQ   R2, R2, #1
        BEQ     %BT00

        MOV     R14,R2                      ; Find first space or control terminator
01      LDRB    R3,[R14]
        CMP     R3,#&20
        ADDHI   R14,R14,#1
        BHI     %BT01

02      LDRB    R3,[R14]                    ; Go back to last '.'
        CMP     R14,R2
        CMPNE   R3,#"."
        SUBNE   R14,R14,#1
        BNE     %BT02

        CMP     R3,#"."                     ; Skip '.'
        ADDEQ   R14,R14,#1

        LDRB    R3, [R14]
        CMP     R3, #&20
        MOVLS   R14, R2                     ; if we'd get a null string, then jump back to start

        MOV     R4,#ms_data+8
03      LDRB    R3,[R14],#1
        CMP     R3,#&20
        BLS     %FT04
        STRB    R3,[R1,R4]
        ADD     R4,R4,#1
        CMP     R4,#27+ms_data
        BLO     %BT03

04      MOV     R3,#0                      ; Store zero terminator.
        STRB    R3,[R1,R4]

        MOV     R2,#0
        STR     R2,[R1,#ms_yourref]
        LDR     R14,=Message_Iconize
        STR     R14,[R1,#ms_action]
        BL      int_sendmessage_fromwimp
        ADD     sp,sp,#(ms_data+8+20)     ; correct stack

        EXIT

wicon_scup
        TST     R2,#button_right
        BNE     wicon_scdown2
wicon_scup2
        MOV     R14,#32
        LDR     R1,[handle,#w_wey1]             ; can't scroll up past top
        B       scrolly

wicon_scdown
        TST     R2,#button_right
        BNE     wicon_scup2
wicon_scdown2
        MOV     R14,#-32
        LDR     R1,[handle,#w_way0]
        LDR     R2,[handle,#w_way1]
        SUB     R1,R2,R1                        ; height of window
        LDR     R2,[handle,#w_wey0]
        ADD     R1,R2,R1                        ; min y-scroll coord (-ve)

scrolly
        LDR     R0,[handle,#w_flags]
        TST     R0,#wf_userscroll
        MOVNE   R1,#0                           ; x-scroll
        MOVNE   R2,R14,ASR #5                   ; scale down to +-1
        BNE     userscroll
;
        LDR     R0,[handle,#w_scy]
        TEQ     R0,R1
        BEQ     trykeys
;
        Push    R14
        BL      int_get_window_state
        LDR     R14,[handle,#w_bhandle]         ; open at same posn in stack
        STR     R14,[userblk,#u_bhandle]
        LDR     R0,[userblk,#u_scy]
        Pull    R14
        ADD     R0,R0,R14
        STR     R0,[userblk,#u_scy]
;
        B       Exit_OpenWindow

wicon_scleft
        TST     R2,#button_right
        BNE     wicon_scright2
wicon_scleft2
        MOV     R14,#-32
        LDR     R1,[handle,#w_wex0]             ; can't scroll left past wex0
        B       scrollx

wicon_scright
        TST     R2,#button_right
        BNE     wicon_scleft2
wicon_scright2
        MOV     R14,#32
        LDR     R1,[handle,#w_wax0]
        LDR     R2,[handle,#w_wax1]
        SUB     R1,R2,R1                        ; width of window
        LDR     R2,[handle,#w_wex1]
        SUB     R1,R2,R1                        ; max x-scroll coord

scrollx
        LDR     R0,[handle,#w_flags]
        TST     R0,#wf_userscroll
        MOVNE   R1,R14,ASR #5                   ; scale down to +-1
        MOVNE   R2,#0                           ; y-scroll
        BNE     userscroll
;
        LDR     R0,[handle,#w_scx]
        TEQ     R0,R1
        BEQ     trykeys
;
        Push    R14
        BL      int_get_window_state
        LDR     R14,[handle,#w_bhandle]         ; open at same posn in stack
        STR     R14,[userblk,#u_bhandle]
        LDR     R0,[userblk,#u_scx]
        Pull    R14
        ADD     R0,R0,R14
        STR     R0,[userblk,#u_scx]

;
; Get window opened
; Entry:  handle --> window definition
;         userblk --> data (handle,x0,y0,x1,y1,scx,scy,bhandle)
;

Exit_OpenWindow
        BL      calc_w_status                   ; ensure bhandle is correct
        ADD     R14,handle,#w_wax0
        LDMIA   R14!,{cx0,cy0,cx1,cy1}
        LDMIA   userblk,{R0,x0,y0,x1,y1}

        Push    R14
        LDR     R14,dx_1
        BIC     cx0,cx0,R14
        BIC     cx1,cx1,R14
        BIC     x0,x0,R14
        BIC     x1,x1,R14

        LDR     R14,dy_1
        BIC     cy1,cy1,R14
        BIC     cy0,cy0,R14
        BIC     y0,y0,R14
        BIC     y1,y1,R14
        Pull    R14

        TEQ     cx0,x0
        TEQEQ   cy0,y0
        TEQEQ   cx1,x1
        TEQEQ   cy1,y1
        LDMEQIA R14,{cx0,cy0,cx1}               ; scx,scy,bhandle
        ADDEQ   R14,userblk,#5*4                ; skip handle,x0,y0,x1,y1
        LDMEQIA R14,{x0,y0,x1}

        Push R14
        LDR     R14,dx_1
        BIC     cx0,cx0,R14
        BIC     x0,x0,R14

        LDR     R14,dy_1
        BIC     cy0,cy0,R14
        BIC     y0,y0,R14
        Pull    R14

        TEQEQ   cx0,x0
        TEQEQ   cy0,y0
        TEQEQ   cx1,x1
      [ togglebits
        LDR     R14, [handle, #w_flags]         ; don't mask out requests during a toggle operation!
        TSTEQ   R14, #ws_toggling
      ]
        BEQ     trykeys                         ; go to next bit of polling
;
        LDR     R14,[handle,#w_taskhandle]
        CMP     R14,#0                          ; <= 0 ==> system window
   ;    Task    R14,GT,"OpenWindow"     ; force errors into the open!
        MOVGT   R0,#Open_Window_Request
        BGT     ExitPoll
;
        BL      int_open_window         ; menus are opened automatically
        B       repollwimp

userscrollpage
        MOV     R2,R0                   ; R2 = cx0, so it's in R0 instead

userscroll
        Push    "R1,R2"
        BL      int_get_window_state
        LDR     R14,[userblk,#u_handle]         ; open at same posn in stack
        STR     R14,[userblk,#u_bhandle]
        Pull    "R1,R2"
        ADD     R14,userblk,#u_scroll
        STMIA   R14,{R1,R2}
;
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"ScrollRequest"
        MOV     R0,#Scroll_Request
        B       ExitPoll

        LTORG


wicon_scvert
        BL      getvscrollcoords
        MOV     R1,#0                   ; x-scroll = 0
        LDR     R14,mouseypos
        CMP     R14,cy0
        MOVLT   R0,#-2                  ; R2 = cx0, so use R0 instead
        BLT     pagevert
        CMP     R14,cy1
        MOVGT   R0,#2
        BGT     pagevert
;
        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_icon7           ; if there is a horizontal scroll bar,
        LDRNE   R14,mousebuttons        ; and the right-hand button is used
        TSTNE   R14,#button_right       ; allow 2-way scrolling
        MOVEQ   R0,#drag_vscroll
        MOVNE   R0,#drag_scrollboth
        BL      int_drag_box
        B       trykeys

wicon_schoriz
        BL      gethscrollcoords
        MOV     R0,#0                   ; y-scroll = 0
        LDR     R14,mousexpos           ; R2 = cx0, so use R0 instead
        CMP     R14,cx0
        MOVLT   R1,#-2
        BLT     pagehoriz
        CMP     R14,cx1
        MOVGT   R1,#2
        BGT     pagehoriz
;
        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_icon5           ; if there is a vertical scroll bar,
        LDRNE   R14,mousebuttons        ; and the right-hand button is used
        TSTNE   R14,#button_right       ; allow 2-way scrolling
        MOVEQ   R0,#drag_hscroll
        MOVNE   R0,#drag_scrollboth
        BL      int_drag_box
        B       trykeys

pagevert
        LDR     R14,mousebuttons        ; reverse direction if r-button used
        TST     R14,#button_right
        RSBNE   R0,R0,#0
;
        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_userscroll
        BNE     userscrollpage
;
        Rel     R1,handle
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{cx0,cy0,cx1,cy1,x0,y0,x1}
        SUB     R14,cy1,cy0
        TEQ     R0,#0                   ; y scroll direction
        ADDPL   y0,y0,R14
        SUBMI   y0,y0,R14
        ASSERT  (u_handle=0)
        STMIA   userblk,{R1,cx0,cy0,cx1,cy1,x0,y0,x1}
        STR     R1,[userblk,#u_bhandle]         ; same posn in stack
;
        B       Exit_OpenWindow

pagehoriz
        LDR     R14,mousebuttons        ; reverse direction if r-button used
        TST     R14,#button_right
        RSBNE   R1,R1,#0
;
        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_userscroll
        BNE     userscrollpage
;
        Rel     R0,handle
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{cx0,cy0,cx1,cy1,x0,y0,x1}
        SUB     R14,cx1,cx0
        TEQ     R1,#0                   ; x scroll direction
        ADDPL   x0,x0,R14
        SUBMI   x0,x0,R14
        ASSERT  (u_handle=0)
        STMIA   userblk,{R0,cx0,cy0,cx1,cy1,x0,y0,x1}
        STR     R0,[userblk,#u_bhandle]         ; same posn in stack
;
        B       Exit_OpenWindow


;;----------------------------------------------------------------------------
;; Get_Pointer_Info - find out where the mouse pointer is
;;----------------------------------------------------------------------------

SWIWimp_GetPointerInfo
        MyEntry "GetPointerInfo"
;
        LDR     R0,mousexpos                    ; just read old values
        LDR     R1,mouseypos
        LDR     R2,mousebuttons
        MOV     R5,#wf2_shadedhelp              ; match shaded icons if this bit is set in window flags 2 byte
        BL      int_get_pointer_info

        CMP     R4, #nullptr2
        BNE     %FT01
	LDR     R14,singletaskhandle            ; if single-tasking,
	CMP     R14,#nullptr
	MOVNE   R4,#nullptr                     ; don't confuse punter!
        BLEQ    setwimpicon                     ; if new task, work out which icon it is
01
        LDR     R14,backwindowhandle
        TEQ     R3,R14
        LDRNE   R14,backwindow                  ; window -1 for either of these
        TEQNE   R3,R14
        MOVEQ   R3,#nullptr
;
        LDR     R14,iconbarhandle
        CMP     R14,#nullptr
        BEQ     %FT01
        TEQ     R3,R14
        MOVEQ   R3,#nullptr2                    ; return -2 for iconbar
01
   ;    LDR     R14,oldbuttons                  ; WAS last parameter
        STMIA   userblk,{R0,R1,R2,R3,R4}
        B       ExitWimp


;-----------------------------------------------------------------------------
; getmouseposn - takes note of mouseflags
;-----------------------------------------------------------------------------

getmouseposn
        Push    "LR"
;
        LDR     R14,mouseflags
        TST     R14,#mf_oldcoords
        BIC     R14,R14,#mf_oldcoords   ; test and reset bit
        STR     R14,mouseflags
        BEQ     newmouseposn
;
        LDR     R0,mousexpos
        LDR     R1,mouseypos
        LDR     R2,mousebuttons
        Pull    "PC"
;
newmouseposn
        LDR     R14,mousebuttons
        STR     R14,oldbuttons          ; copy only if want new buttons

        Push    "R3"

        MOV     R3,#0                   ; see if R3 is set up!

      [ mousecache

; if recacheposn is zero then no need to read the mouse values as they
; will not have been updated, instead read from our workspace and
; wait for the value to go non-zero.

        ASSERT  mouseypos = mousexpos +4
        ASSERT  mousebuttons = mouseypos +4
        ASSERT  mousetime = mousebuttons +4

        LDRB    R0,recacheposn
        TEQ     R0,#0                   ; do we have a cached posn for the mouse?
        ADREQ   R0,mousexpos            ; if they are then pick them up from our workspace
        LDMEQIA R0,{R0,R1,R2,R3}
        BEQ     %FT10

        STRB    R3,recacheposn
      ]

        SWI     XOS_Mouse
        LDR     R14,dx_1                ; R14 <- dx-1
        BIC     R0,R0,R14
        LDR     R14,dy_1                ; R14 <- dy-1
        BIC     R1,R1,R14

      [ mousecache
        ADR     R14,mousexpos
        STMIA   R14,{R0,R1,R2,R3}
      |
        STR     R0,mousexpos
        STR     R1,mouseypos
        STR     R2,mousebuttons
      ]
      [ NCErrorBox
        Push    "R0-R6"
        ASSERT  ptrsuspendflag = ptrsuspenddata + 12
        ADRL    R14, ptrsuspenddata
        LDMIA   R14, {R3-R6}

        TEQ     R6, #1                  ; waiting for keyboard-generated move?
        MOVEQ   R3, #2
        STMEQIA R14, {R0-R3}            ; store mouse state and new flag value

        TEQ     R6, #2                  ; waiting for mouse-generated move?
        BNE     %FT06
        TEQ     R0, R3
        TEQEQ   R1, R4
;        TEQEQ   R2, R5
        BEQ     %FT06                   ; no change

        ADRL    R14, ptrsuspendflag
        MOV     R0, #0
        STR     R0, [R14]               ; store new flag value
        MOV     R0, #106
        MOV     R1, #1
        SWI     XOS_Byte                ; turn on pointer
        TEQ     R1, #0
        SWINE   XOS_Byte                ; just in case it's been turned on by someone else already!
06
        Pull    "R0-R6"
      ]
10
        TEQ     R3,#0                   ; was it set up?
        BEQ     bodgetime
        STR     R3,timeblk
        Pull    "R3,PC"

bodgetime
        Push    "R0-R2"
        MOV     R0,#1
        ADR     R1,timeblk
        SWI     XOS_Word                ; make a note of the time
        Pull    "R0-R3,PC"


;-----------------------------------------------------------------------------
; int_get_pointer_info - called by Poll_Wimp
; look for a match with a window / user icon
; Entry :  R0,R1 = coords of mouse pointer
;          R5 = bits in windowflags2 that can be set to indicate shaded icons should b included
; Exit :   R3 = window handle (relative)
;          R4 = button index (or -1 if none)
;          handle --> window definition (if any)
;          If clicked in work area, [mousexyrel] = relative coordinates
;-----------------------------------------------------------------------------

int_get_pointer_info

        ADRL    R4,activewinds+lh_forwards-ll_forwards
      [ ChildWindows
        MOV     R3,#0                   ; ignore wf_inborder bit for top-level windows
      ]

; In:   R4 -> list head ( activewinds or [handle,#w_children] )
;       R3 = wf_inborder if we're to search only windows which have this bit
;       R3 = 0 if we're to search all windows in the list

int_get_pointer_info_R4                 ; for ChildWindows

      [ ChildWindows
        Push    "userblk,LR"

        MOV     userblk,R3              ; userblk = wf_inborder or 0
      |
        Push    "LR"
      ]

findwlp
        LDR     R4,[R4,#ll_forwards]
        LDR     R14,[R4,#ll_forwards]
        CMP     R14,#nullptr
        MOVEQ   R3,#nullptr
        MOVEQ   R4,#nullptr
      [ ChildWindows
        Pull    "userblk,PC",EQ
      |
        Pull    "PC",EQ
      ]
        SUB     handle,R4,#w_active_link
        Rel     R3,handle

      [ ChildWindows
        LDR     R14,[handle,#w_flags]
        AND     R14,R14,userblk
        TEQ     R14,userblk                     ; ignore window if userblk = wf_inborder and bit not
        BNE     findwlp
      ]

        ADD     R14,handle,#w_x0
        LDMIA   R14,{x0,y0,x1,y1}

        CMP     R0,x0
        CMPGE   R1,y0
        BLT     findwlp
        CMP     x1,R0
        CMPGT   y1,R1
        BLE     findwlp

;        Debug   child,"Click within outer box of",handle

; decide whether the click is inside or outside the work area

        Push    "R0-R3,R5-R9"
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{cx0,cy0,cx1,cy1,x0,y0}
;
        Push    "cx0,cy0,cx1,cy1"
        LDR     R14,dx
        SUB     cx0,cx0,R14                     ; include border!
        LDR     R14,dy
        SUB     cy0,cy0,R14
        CMP     R0,cx0                          ; check inside work area
        CMPGE   R1,cy0
        CMPGE   cx1,R0
        CMPGE   cy1,R1
        Pull    "cx0,cy0,cx1,cy1"
      [ :LNOT: ChildWindows
        BLT     noficon2
      ]

; right - a click inside the work area
; first check to see if it's inside any of the window's children

      [ ChildWindows
        Push    "cx0,cy0,cx1,cy1,x0,y0,x1,y1,handle,userblk"
        MOVLT   R3,#wf_inborder                 ; click was in border area (so only scan children that overlap the border)
        MOVGE   R3,#0                           ; click was in work area (scan all children)
        MOV     userblk,R3
        ADD     R4,handle,#w_children + lh_forwards - ll_forwards
        LDR     R5,[SP,#(10+4)*4]               ; get back original R5
        BL      int_get_pointer_info_R4         ; scan child window stack for a hit
        CMP     R3,#nullptr
        ADDNE   SP,SP,#10*4
        STRNE   R3,[SP,#3*4]
        BNE     exitp4                          ; click found in one of the children
        TEQ     userblk,#0
        Pull    "cx0,cy0,cx1,cy1,x0,y0,x1,y1,handle,userblk"
        BNE     noficon2                        ; click was in border area
      ]
;
; now scan the icon list for a hit (starting at the frontmost, ie. the highest-numbered)
;
        SUB     x1,cx0,x0                       ; coords of top-left
        SUB     y1,cy1,y0
        SUB     R0,R0,x1                        ; make mouse coords relative
        SUB     R1,R1,y1
;
        LDR     R4,[handle,#w_nicons]           ; R4 = no of icons
        LDR     R2,[handle,#w_icons]            ; R2 --> start of icons
        ADD     R2,R2,R4,ASL #i_shift           ; R3 --> end of list
        LDR     R5,[SP,#4*4]                    ; R5 on entry
ficlp
        SUBS    R4,R4,#1
        BMI     noficon                         ; no more left
        SUB     R2,R2,#i_size                   ; point to current icon
;
        LDR     R14,[R2,#i_flags]
        TST     R14,#is_deleted                 ; always ignore deleted icons
        BNE     ficlp
        TST     R14,#is_shaded                  ; only ignore shaded icons
        LDRNE   R14,[handle,#w_taskhandle]      ; if they're not in a menu
        CMPNE   R14,#nullptr                    ; -1 => menu
        BEQ     %FT01
        LDRB    R14,[handle,#w_flags2]
        TST     R14,R5                          ; do we want to match shaded icons in windows?
        BEQ     ficlp
01
        ASSERT  (i_bbx0=0)                      ; check LDMIA is OK
        ASSERT  cx1 = r4
        Push    "cx1,cy1"
        LDMIA   R2,{cx1,cy1,x0,y0}
        CMP     R0,cx1                          ; see if pointer is inside
        CMPGE   R1,cy1
        Pull    "cx1,cy1"
        BLT     ficlp
        CMP     R0,x0
        CMPLT   R1,y0
        BLT     endficon                ; got it!!!
        B       ficlp
;
noficon2
        MOV     R4,#nullptr2            ; if in border, set icon = -2
        B       endficon
noficon
        MOV     R4,#nullptr
endficon
        STR     R0,mousexrel            ; may be needed later
        STR     R1,mouseyrel
exitp4                                  ; for ChildWindows
      [ ChildWindows
        Pull    "R0-R3,R5-R9,userblk,PC" ; restore mouse coords etc.
      |
        Pull    "R0-R3,R5-R9,PC"        ; restore mouse coords etc.
      ]

        END