RISC World

Modules for Beginners

Part 4: The Wimp Filtering System - Brian Pickard

All multitasking RISC OS applications rely on the WIMP SWI's. These give the programmer a reasonably straight forward way of designing the Windows, icons and controlling the application via mouse clicks etc.

The whole application is based round a loop which contains the SWI Wimp_Poll. This SWI polls the WIMP to see if certain events have occurred (e.g. a mouse click or key press or the window needs redrawing etc).

The loop is only exited when the application is quitting or an unrecoverable error occurs.

Modifying Wimp Applications

Sometimes applications do not quite meet the users needs or a more friendly user interface could have been designed. An example of this is DrawWorks Millenium which uses the Draw application but adds a great user interface as well as extra facilities. It would be nice if such applications could have their actions modified. There is a way of doing this using what is known as the Wimp filtering system.

Note the applications code is not altered in any way so this method does not break any copyright.

The Wimp filtering system is usually a hidden feature that is left dormant. It is a way of 'looking into' any multitasking applications events before or just after they occur. So using this method mouse clicks, for example, can be intercepted and under certain conditions may be made to do something different.

In fact this filter can be applied to ALL multitasking applications using just one module, so the same event(s) can be modified in ALL tasks. But less of the boring details lets get down to a useful example.

The RISC OS printing system has one flaw, (up to version 1.53 anyway) if the printer has not been switched on or the connection has got broken then trying to print results in the computer hanging. It would be nice if a warning occurred and the printing was not allowed to start.

To do this requires the Print key press, the print menu selection and the mouse click on an print start icon to be stopped from reaching the application. The tricky bit is to recognise this mouse click! (See later).

Setting up Wimp filters

There are two sorts of wimp filter.

  • Pre-Filter: This is called whenever a task is about to call Wimp_Poll. The task is not yet paged in memory at this stage so all the routine can do is change the event mask that the task has setup.
  • Post-Filter: This is called whenever the Wimp is about to return from Wimp_Poll to the task. The task therefore is already paged in memory. This means the tasks control block is readable, the wimp event code may be changed or intercepted.

Personally I have found Post Filters are more useful than Pre Filters. To set up a filter the SWI's Filter_RegisterPreFilter and Filter_RegisterPostFilter are used. NOT Wimp_RegisterFilter! In our example the following code is used in the modules initialisation.

    ADR R0,fname      ;R0 points to filter name
    ADR R1,routine    ;R1 points to filter routine
    LDR R2,[R12]      ;R2 start of module workspace
    MOV R3,#0         ;R3 is zero since we wish this filter all tasks
    LDR R4,mask%      ;R4 is Wimp mask for which events required
    SWI ”XFilter_RegisterPostFilter• ;register the filter

The filter name can be anything up to a max of 12 chrs and null terminated.

Note that R2 should point to a convenient area of workspace. I use the modules workspace previously setup. So R2 will contain the start address of the modules workspace. When the filter routine is entered this address will be set up in R12 by RISC OS.

R3 normally has the filtered tasks Wimp handle but for all tasks to be filtered we use zero.

R4 should contain a mask value for which events you do not wish the filter to be called. The value is worked out by a bit pattern. In our example we want mouse clicks (event 6), key presses (event 8) and menu selections (event 9).

The value should be &FFFFFFFF - (1<<6) - (1<<8) - (1<<9).

To de-register filters the same data is required with the SWI "XFilter_DeRegisterPostFilter" the code being placed in the modules finalisation. You can check your filter has been registered by typing *Filters at a task window prompt. This lists all the known filters.

The Modules Action

For any task when the wimp is about to return from Wimp_Poll to the task our filter routine will be called. So the following registers are set:

    R0 will contain the event code
    R1 will contain the control block relevant for the event
    R2 will have the tasks wimp handle.
    R12 will point to the start of the workspace.
    R13 is the usual stack pointer
    R14 contains the return address

The processing mode will be SVC (more about this later).

We must preserve R1 and R2 (obvious?!) but we can alter any data in the control block and we can change R0 to produce a different event code. Making R0 = -1 stops the event from reaching the task. The complete module source code is on the CD it is called Modsrc1 and the assembled module is called PrtChkMod1. The filter routine first checks to see which event has occurred. The simplest is the key press since checking for the print key number is all that is needed to decide if the printer connection needs checking.

For mouse click and menu selection the routine needs to check what icon and window the mouse pointer is in. Since this is quite involved the routine checks the printer connection first. If a printer driver is not present there is no need to intercept the event. The module checks this by finding out if the system variable Printer$Path exists. If it does then it reads the first few bytes and decides if the printer is printing to a file or a LanMan98 PC network. (You cannot easily trap these conditions so the program assumes the connection is ok).

If the printer is printing via the parallel port then check the status bits of the port. If the connection is ok then get out of the routine, allowing the event to be passed onto the task. If the routine finds the printer connection is blocked then check if the event will start up the printing process. In Wimp tasks a user starts printing by clicking on a Print icon so the routine needs to check if this event is about to do this.

Checking the window and icon properties is not so straight forward. Here is the outline.

Check the text in the icon. If text = Print then intercept the event (the task will not receive it). Check window title. If title = Print or print and icon text = OK or Ok then intercept event If the printer cannot be used then call SWI 256+7 (VDU7 make beep).

Here is the code as used in the module

  STMFD R13!,{R0-R6,R14}             ;store registers to stack
  .the Printer$path and parallel port connection check code 
  .is here (see module source code)
  ADD R1,R12,#12:SWI&600CF           ;use Wimp_GetPointerInfo
  LDR R0,[R1,#16]:CMP R0,#0:BLT no%  ;to see if in a icon or window
  LDR R6,[R1,#12]:CMP R6,#0:BLE no%  ;if not then exit routine
  STR R6,[R1]:STR R0,[R1,#4]:SWI&600CE ;get icon data
  LDR R0,[R1,#24]:MOV R2,#(1<<8) ;and check to see if the
  ADD R2,R2,#1:AND R0,R0,R2          ;text is re-directed or not
  CMP R0,R2:LDREQ R5,[R1,#28]
  ADDNE R5,R1,#28                    ;set up R5 to point to text (if any)
  ORR R1,R1,#1:STR R6,[R1]:SWI&600CC ;get window data
  LDR R0,[R1,#59]:MOV R2,#(1<<8)
  ADD R2,R2,#1                      ;check to see if title is re-directed
  AND R0,R0,R2:CMP R0,R2:LDREQ R4,[R1,#75]
  ADDNE R4,R1,#75                    ;or not. Set up R4 to point to title
  LDRB R3,[R4]:ORR R3,R3,#96
  CMP R3,#ASC(”p•):BNE printic%      ;check title of window
  LDRB R3,[R4,#1]:CMP R3,#ASC(”r•)
  BNE printic%                       ;if it is not the word
  LDRB R3,[R4,#2]:CMP R3,#ASC(”i•)
  BNE printic%                       ;Print or print
  LDRB R3,[R4,#3]:CMP R3,#ASC(”n•)
  BNE printic%                       ;then go and check the
  LDRB R3,[R4,#4]:CMP R3,#ASC(”t•)
  BNE printic%                       ;text in the icon
  LDRB R3,[R4,#5]:CMP R3,#32
  BGT printic%                       ;that was clicked on
  LDRB R3,[R5]:CMP R3,#ASC(”O•)
  BNE printic%                       ;check if the text in icon
  LDRB R3,[R5,#1]:ORR R3,R3,#96
  CMP R3,#ASC(”k•):BEQ intercept%    ;is Ok or OK if so then correct

  LDRB R3,[R5]:CMP R3,#ASC(”P•)
  BNE no%                            ;if text not Ok or OK then
  LDRB R3,[R5,#1]:CMP R3,#ASC(”r•)
  BNE no%                            ;check if text is Print
  LDRB R3,[R5,#2]:CMP R3,#ASC(”i•)
  BNE no%                            ;if not then this cannot be
  LDRB R3,[R5,#3]:CMP R3,#ASC(”n•)
  BNE no%                            ;a print window and print icon
  LDRB R3,[R5,#4]:CMP R3,#ASC(”t•)
  BNE no%                            ;so not correct
  LDRB R3,[R5,#5]:CMP R3,#32:BGT no% ;(might be the word Printer etc.)
  ;the next 3 lines of code does the required intercepting
  ;of event and makes the bleep
  MVN R0,#0                        ;make R0 = -1
  STR R0,[R12]                     ;store value at start of workspace
  SWI 256+7                        ;make the beep

  LDMFD R13!,{r0-r6 ,R14}          ;return all Registers trick
  LDR R0,[R12]                     ;R0 now contains required event code
  MOVS PC,R14                      ;return to application

You will have noticed I have not mentioned the Menu selection event. Menus are quite similar to windows and icons at this level. When the menu selection event is about to occur using the SWI Wimp_GetPointerInfo returns the Menu handle and the icon handle of the selected menu option. So the same code can be used as the mouse click event.

Special Features

All the above requires the use of several Wimp SWI's. This means the return address in R14 needs to be saved on the stack before calling any SWI's. The module also sets up two star commands called prtfilteron and prtfilteroff.

These call the modules initialise and finalise routines, checking if the module is active or not, and then re-registering or de-registering the filter.

Thats it for this part. I hope the module is useful. You can of course try out some ideas of your own. Make a beep when the pointer is leaving a window, or making the iconbar menu appear when the pointer moves on to the respective tasks iconbar icon.

Next time I will extend the ideas of interception in RISC OS with vectored routines.

Brian Pickard