> Wimp.Desktop New Calls --------- Wimp_Initialise Wimp_CloseDown Service_WimpCloseDown Wimp_StartTask *Desktop Service_StartWimp Service_StartedWimp Wimp_Poll: reason code 18 (message) action 0 (global closedown) Starting/stopping a task ------------------------ Wimp_Initialise Entry: if R1 = "TASK" (low-byte = "T", high-byte = "K"): R0 = last known Wimp version number * 100 (at least 200) R2 = pointer to short description of task (used by Switcher menu) (this is a new-style Wimp task) if R1 <> "TASK", (this is an old-style Wimp task) Exit: R0 = current Wimp version number * 100 if R1 = "TASK" on entry, R1 = task handle on exit Old-style Wimp programs are recognised by the Wimp by the way in which they call Wimp_Initialise, since they won't set R1 = "TASK" (unless they are extremely unlucky!). When an old-style wimp application starts up, the Wimp will automatically open a large window with background colour 15, which covers all other windows, so that the task has the whole screen to itself. It will also prevent any other tasks from receiving any kind of event from Wimp_Poll while the old-style task is running. When new tasks start up, the Wimp will set up the mode and pointer (if this is the first task), whereas it behaves in 'compatible mode' with old tasks, not doing anything except programming the pointer shape. Wimp_CloseDown Entry: if R1 = "TASK", R0 = task handle of task to be closed down If the task handle is not specified, it is assumed to be whichever task was last active (ie. the last task that Wimp_Poll returned to). Note that the task handle should always be specified if Wimp_CloseDown is called in (eg) the 'Die' entry of a module, since the relevant task is probably not the one currently running. When a live task is killed using Wimp_CloseDown, the following service call is issued by the Wimp: Service_WimpCloseDown (&53) Entry: R0 = 0 ==> Wimp_CloseDown was called R0 > 0 ==> Wimp_Initialise was called in the same domain R2 = task handle of task being closed down Normally a task will close itself down when it has decided to finish, in which case the service call is somewhat superfluous, but there are two occasions where the task can be closed down 'automatically', as it were. The first of these happens when OS_Exit is called by the task, and Wimp_CloseDown has not yet been called. This can happen if, for example, an error is generated which is not caught by the task's own error handler - in this case the Wimp reports the error and calls OS_Exit automatically. The service call will come round with R0=0 - in this case, the task receiving the service call should tidy itself up, and pass on the service (it should NOT call Wimp_CloseDown !!!). All registers must be preserved. The second case occurs when Wimp_Initialise is called in the same task domain as a currently active task. This can occur, if, for example, the task allows the user to enter *commands, in which case it is likely that the user will try to start up another Wimp program. The Wimp will attempt to close down the first task before starting up the new one, and will send round the Service_WimpCloseDown with R0 > 0 (in fact R0=1 initially, but may be modified by the time the module receives it). If R0>0, the original task is allowed to return an error from the service call by pointing R0 at an error block (a good error message is "Wimp is currently active" (error number &103)). The service should not be claimed, so other modules can receive it, and R0 should be preserved unless an error is to be returned. If R0 > 1 on exit from the OS_ServiceCall, the Wimp will return the error to the task attempting to start up, which will prevent it from doing so. Note, however, that application space may well have been overwritten at this stage, so BASIC programs and the like cannot use this facility! Note that modules should not actually claim this service (set R1=0), since it is possible that more than one module is interested in a particular task closing down. Wimp_StartTask Entry: R0 --> *command to execute This command is used to start up a new Wimp task. It creates a new 'domain' for the task, and executes the *command in it. If and when the task calls Wimp_Initialise, and then Wimp_Poll, control will be returned to the 'parent' task (ie. the one which called Wimp_StartTask). Note that Wimp_StartTask cannot be called except from within a new-style Wimp application. Starting the desktop -------------------- Either: *Desktop Or: Wimp is the configured language, entered by the MOS automatically The *desktop command is decoded by the Desktop module, which also supports the DESKFS filing system, and allows the Desktop module to be entered as a language. The *Desktop command cancels the current EXEC file, if any, since the Desktop will exit immediately if an exec file is in operation (this is so that SHIFT-BREAK can be used to start up a boot file). When the Desktop is entered (by either of the above methods), it attempts to 'wake up' any resident modules which would like to become Wimp tasks. It does this by passing round a service call Service_StartWimp (&49), which wimp application modules should respond to as follows: * if already running as a Wimp task, do nothing * otherwise claim the service call: set R0 --> *command to start up module R1 = 0 (claim service) The Desktop, on finding that the service call has been claimed, will pass the value returned in R0 on to Wimp_StartTask, thereby invoking the *command in a new Wimp domain. The *command should be decoded by the module (by having the command in its command decoding table - see PRM 'Modules' chapter), and should cause the module to enter itself as follows: SWI OS_Module, R0=2, R1-->module title, R2 = R0 on entry to *command (R0 points to the command tail on entry to the *command). Note that it is very important that the module starts itself up as an application, for the following reasons: * Wimp_Poll can only be called from USR mode * the Wimp intercepts the Service_NewApplication call * Arthur's CAO (currently active object) pointer is set up After starting up the module, the Desktop will re-issue the service call, and will continue in this way until no-one claims the service, at which point it issues Service_StartedWimp (&4A) to say that it has finished, and exits. Any tasks that were started up during the sequence then continue running. Example startup code: Service LDR R12,[R12] ; get workspace pointer TEQ R1,#Service_StartWimp MOVNE PC,LR Push "LR" LDR R14,mytaskhandle TEQ R14,#0 ; is module running? MOVEQ R14,#-1 STREQ R14,mytaskhandle ADREQ R0,mycommand MOVEQ R1,#0 ; if not, claim service Pull "PC" mycommand DCB "foo",0 ALIGN Mycommand_Code ; command table points here Push "LR" MOV R2,R0 ; R2 --> command tail ADR R1,ModuleTitle ; R1 --> module title MOV R0,#2 ; R0 = ModHandReason_Enter SWI XOS_Module Pull "PC" Note that the module must actually enter itself in this way, rather than simply jumping to its code, since the MOS has to flatten the supervisor stack and so on, as the new module is a new application. Errors ------ A problem arises if any module fails to initialise properly, since after reporting the error to the user, the module must abort, and the Wimp will then re-issue the Service_StartWimp call, which will be caught again by the module, and so on (ie. the user is stuck in an infinite loop). It is better if any errors during initialisation are reported, followed by the rest of the modules starting up. To achieve this, the following procedure must be adopted: * When the application module is first loaded, it sets an internal variable called 'mytaskhandle' to zero (meaning 'not running') * When the module is entered, it should check to see if it is already running (mytaskhandle > 0). If it is, it should first call Wimp_CloseDown with R0 = mytaskhandle, R1 = "TASK", to avoid running as two tasks at once! Note: whenever the task closes itself down in this way, it should set mytaskhandle=0 afterwards. * When the module starts up successfully as a task, it calls Wimp_Initialise, and then sets mytaskhandle = R1 (task handle returned by Wimp). The value returned is defined to be greater than zero. * When the task terminates, the module calls Wimp_CloseDown and sets mytaskhandle to 0. * When the module is killed, it checks to see if mytaskhandle is greater than zero - if it is, it calls Wimp_CloseDown with R0 = mytaskhandle and R1 = "TASK" (this tells the Wimp exactly which task is to be closed down) * When the module receives the Service_StartWimp call, it should respond ONLY if mytaskhandle = 0. If it is, it should set mytaskhandle = -1, and return the appropriate *command from the service call. * If an error occurs during initialisation, the module will not attempt to restart, since mytaskhandle = -1. If the initialisation succeeds, mytaskhandle will be overwritten with the appropriate task handle value. * So that the user can attempt to re-run the module, the Wimp passes round another service call when it has finished starting up all dormant tasks. This is Service_StartedWimp (&4A), and when the module receives it it should set mytaskhandle to 0, if it was -1 (without claiming the service). * The module should set mytaskhandle=0 when Service_Reset is detected Closing down ------------ To make it easier for the user to exit from the Wimp environment, the switcher module provides a menu entry that allows the user to close down all tasks. This causes the switcher to issue a broadcast to all tasks, asking them to close down, which should be responded to as follows: Wimp_Poll Exit: R0 = 18 R1 --> block (as supplied to Wimp_Poll) +0 = 20 (size of block) +4 = task handle of Switcher +8 = reference number of message +12 = 0 +16 = 0 (global close down) If the task can exit immediately, it should do so (by calling Wimp_CloseDown followed by OS_Exit or Wimp_Poll). If the task has some unsaved data (eg. the user has modified a file and not saved it), it should prompt the user (in the same way as it would if the user had attempted to exit from the task explicitly). Before it does this, however, it must abort the close down sequence by acknowledging the close down message as follows: Wimp_SendMessage Entry: R0 = 19 (acknowledge message) R1 --> block + 12 = reference number of closedown message Once the user has entered a dialogue with any task in this way, therefore, he must explicitly restart the closedown sequence if he still wishes to quit from the Wimp environment.