Multiple printer drivers (versions 3.00 and above)

Problems:

Version 3.00 and above of the printer drivers have been upgraded to allow more than one of them on the iconbar at any one time. This would be an easy change except for a two points:

  1. Printer driver modules.
    Version 2.4 of the printer drivers had one module per printer driver type. I.e. !PrinterDM had a graphics printing module, !PrinterPS had a graphics printing module, etc. All these modules were called 'PDriver', so only one of them could be loaded at any one time. For multiple printer drivers we needed many of these graphics printing modules, one for each printer type resident on the iconbar.
    All the printer modules used the same SWI numbers. If you want many printer modules loaded at once, however, you cannot let them use the same SWI numbers - it just doesn't work. There needed to be one central manager module which used the old SWIs, routeing them to the correct printer module.
  2. 'Printer:'.
    Most or all of the current applications available for the Archimedes print to a graphics printer by opening the file "printer:" and then calling the printer module. To allow each printer driver to be printing at the same time the applications would have to be able to open the file "printer:" more than once, but this is not permitted by the kernel. This problem has only been partialy resolved. In addition to these problems, a lot of changes have had to be made to the BASIC front-ends for the printer drivers, including the design of a complex message protocol.

    The solution:

    The chosen concept for multiple printer drivers is: Allow many printer driver icons on the iconbar, but make at most one of them selected. When the user drags a file icon to a particuler printer, that printer will print it. If the user performs a Misc=>Print type menu option (e.g. from !Draw) then it will print to the currently highlighted printer.

    This leads to the following solutions to the two problems mentioned above:

    1. Printer manager module.
              A new printer manager module was created (called 'PDriver' and using
      
      the same SWI chunk as the old printer drivers). The old printer driver modules were slightly upgraded by:
                Changing their names to 'PDriverDM', 'PDriverIX' etc.
                Removing their standard SWI handling. Instead, the modules
      
      register themselves with the 'PDriver' printer manager module. This module can then call them when it needs to route a message.
              The printer manager module takes over control of the printer driver
      
      SWIs, and introduces two of its own. The first of these new ones is PDriver_DeclarePrinter:

      SWI PDriver_DeclarePrinter
      On entry:

              R0 => old SWI handling code
              R1 =  R12 for calls through the branch table
              R2 =  printer number
      
      On exit:
              All preserved
      
              The printer manager notes all printer drivers and their numbers
      
      registered with it, so that any printer may be selected and then used in the normal way. To select a printer driver the manager provides SWI PDriver_SelectPrinter:

      SWI PDriver_SelectPrinter
      On entry:

              R0 =  printer number
      
      On exit:
              R0 =  previously selected printer number, -1 for none.
              New printer selected, or error if that printer number is not known.
      
              The printer manager routes all PDriver SWIs sent to it to a sensible
      
      printer driver. A 'sensible' printer driver could be the currently selected printer driver, the printer driver which started a job, or ALL the printer drivers. The routeing is done by imitating a SWI call - the SWI handling code is called with R11 being the normal SWI number in the SWI chunk. This means no change has to be made to the printer driver SWI handling code. The actual rerouteing of the calls is as follows:

      PDriver_Info, PDriver_SetInfo, PDriver_CheckFeatures, PDriver_PageSize, PDriver_SetPageSize, PDriver_ScreenDump, PDriver_SetPrinter. These are all passed to the currently selected printer driver, as it must be the printer module which the caller expects to talk to.

      PDriver_SelectJob, PDriver_SelectIllustration. These calls either expect to select a completely new job, in which case they are passed to the selected printer module, or they are used to select an old job, in which case the call must be passed to the printer module which owns that job, or to deselect all jobs, in which case it must be sent to the printer module which owns the current job. Note that to do all this the printer manager must keep track of which modules own which jobs.

      PDriver_CurrentJob, PDriver_EnumerateJobs. These calls can all be handled by the printer manager itself, as it knows which module owns which jobs, hence which jobs are active and which is the current job.

      PDriver_EndJob, PDriver_AbortJob, PDriver_CancelJob, PDriver_CancelJobWithError. These calls are passed to the owner of the job specified in the call. EndJob and AbortJob also involve deactivating the job in the printer manager's internal structures.

      PDriver_Reset. This must call all the printer drivers with PDriver_Reset, and then clear the active job table.

      PDriver_FontSWI, PDriver_GiveRectangle, PDriver_DrawPage, PDriver_GetRectangle, PDriver_InsertIllustration. These must be passed to the owner of the current job.

              There are actually quite a few more SWIs implemented by the printer
      
      manager module. These are 'ForDriver' SWIs, to enable printer applications to access their module without doing a SelectDriver, call, SelectDriver to reselect the previous driver. The ForDriver calls are available for all those SWIs which use the currently selected pdriver module. The printer number of the module for the call is passed in R7. So, there are SWIs PDriver_SetPrinterForDriver, PDriver_SelectJobForDriver, PDriver_InfoForDriver, and so on.

      Currently allocated printer numbers (apply to Acorn):

        Number         Driver
          0      Acorn PostScript printer driver
          1      Acorn FX80 type printer driver
          2      Acorn HP LaserJet printer driver
          3      Acorn Integrex printer driver
          4      Computer Concepts Fax modem printer driver
          5      Computer Concepts Dumb laser engine printer
          6      Computer Concepts Canon LBP4 printer driver
          7      Acorn Unified dot matrix printer driver
      

      2) 'Printer:'.

              There is no way to stop the current applications from opening
      
      'printer:' to print. This means that the currently highlighted printer must always have the correct printer driver module and the correct destination for 'printer:' selected. Since 'printer:' can only be opened once at any one time their will obviously be a limit of 1 on the number of graphics print jobs which can be performed at any time. This is not much of a restriction as the printer driver modules are so slow that you cannot afford the processor power to attempt more than one print.
              Text printing is a completely different matter. All the text
      
      printing is done by the printer driver front-ends themselves. To print to the serial port the printer driver only needs to open 'serial:', and this can be open at the same time as another printer driver is printing text to 'netprint#sepia:' or whatever. Indeed, if a printer driver is printing text to 'serial:' there is nothing to stop an application from opening 'printer:' and doing a graphics dump!
              There is one complication. If the user drags a file out of the save
      
      box of an application to an unhighlighted printer then that printer must select itself (set up the correct destination for 'printer:', select its module, send the 'setup' data to its module etc.) before the application opens 'printer:' to start printing. This leads to a very complicated message protocol outlined below.
              Finally, note that any printing to the parallel port has to go
      
      through 'printer:' - or more precisely 'printer#centronics:'. This means that if the user is text-printing to a parallel printer, he or she will not be able to perform a graphics dump to another printer because 'printer:' is already open. This is unfortunate, but unavoidable.

      The message protocol:

      There are three messages in the protocol:

      Message_SelectPermanentPrinter
      Block:

            data + 20  =  task handle of printer to become permanently selected
      
      Use:
              This message is broadcast when the user clicks on the 'Select' menu
      
      option by that printer driver, with its own task handle in the data block. It is also broadcast when a printer driver first starts up, to ensure that it is selected. Both times the message is sent recorded. If the message bounces then there is no currently selected printer, so the printer driver sends itself the corrent Message_SelectPrinterOK message (see below). This message is also sent when a temporary printer selection completes.

      Message_SelectTemporaryPrinter
      Block:

            data + 20  =  task handle of printer to become temporarily selected
      
      Use:
              This message is broadcast when the user drags a file onto a
      
      non-highlighted printer driver. The printer driver must force itself to be selected for a short while so that it can select its printer module, select the correct destination for 'printer:' etc. ready for an application to open 'printer:' and start printing, when the printer driver can reselect the permanent printer driver. The message is broadcast recorded delivery, and if it bounces then the printer driver is free to permanently select itself using a Message_SelectPrinterOK.

      Message_SelectPrinterOK
      Block:

            data + 20  =  task handle of printer which should select itself
            data + 24  =  0 for permanent selection
                          1 for temporary selection
                          2 for message protocol died
      
      Use:
            a)
              This message is broadcast in reply to a
      
      Message_SelectPermanentPrinter by the currently highlighted printer. This printer then deselects itself. This allows a central printer manager task to keep track of which is the currently highlighted printer.
            b)
              This message is broadcast in reply to a
      
      Message_SelectTemporaryPrinter by the currently highlighted printer. This tells the printer which wants to become temporarily selected for the duration of a save-drag which printer it must permanently select after it has finished.
            c)
              This message is broadcast when one of the other messages bounces.
      
      The bounce can only occur if their is no currently selected printer, and so the task which sent the message can freely send itself a Message_SelectPrinterOK.
            d)
              This message is broadcast if a printer tries to become temporarily
      
      selected, the currently highlighted printer responds with this message (as in b), but the temporary printer fails to claim its requirements for selection (e.g. opening 'printer:' to see if it can set the printer destination). This time the message will contain a 'message protocol died' code.

      When a printer driver receives a SelectPrinterOK it must select itself as the current printer. Then it decides if it is a temporary selection, in which case it must record the task handle of the printer driver which sent the message, so it can reply with a Message_SelectPermanentPrinter when the temporary selection is finished with.

      Example Scenario 1:

      Two printer drivers on the iconbar. Drop !Email letter onto currently selected printer driver. This creates a Printer$Scrap file, which is DataLoaded to the printer driver by !Email. The printer driver can then open a file to print to (not printer:, but serial:, printer#centronics:, filename or whatever) and do a multitasking print from that Printer$Scrap file.

                    
      
      [ first driver not printing to printer#centronics: ]

      Now drop a !Draw file on the other icon. This printer driver: Broadcasts a message Message_SelectTemporaryPrinter. Receives Message_SelectPrinterOK with temporary code. Opens printer: and closes printer: to find out that no-one is using it. It then sets up its information.
      Finally it replies to !Draw with a message PrintFile.

      !Draw picks up the PrintFile message, opens printer: and prints the file. !Draw fails to send back the correct Print_WillPrint message. On the next non-message event the printer driver notices it is temporarily selected, and sends Message_SelectPermanentPrinter to the printer which sent it the previous SelectPrinterOK message.

      Note that if the first printer were printing to the parallel port (i.e. printer#centronics:) then the following would happen:

      [ first driver is printing to printer#centronics: ]

      Drop a !Draw file on the other icon. This printer driver: Opens printer: and closes printer: to find out that the first driver is using it. Issue an error 'Printer: in use'.