OS/2 Presentation Drivers in a Nutshell
Written by Dave Raymer
Historically, the general population has held the belief that computers are inherently difficult to use. One of the outgrowths of this popularly held belief has been the Graphical User Interface, or more commonly the GUI. The designers of OS/2, both on the Microsoft, and IBM side, strove to provide not only an easy to use interface, but an easy to program interface. This ease is measured by the robustness of the application programming interfaces (API's) that OS/2 supports, as well as the high degree of device independence that is provided by the display and printer subsystems.
The OS/2 display and printer subsystems are built around the OS/2 Graphics Engine, and a set of executable programs collectively termed "Presentation Drivers." Presentation Drivers (PD's) are a special form of dynamic link library (DLL) that provide a predefined interface for the OS/2 Graphics Engine (PMGRE) to use in rendering. The display driver is one form of a presentation driver, and the printer/plotter driver is another. The primary difference between the two is that the display driver must provide support for a pointing device, but on the whole, the functionality of the two is essentially the same.
In general, Joe and Suzi User interact with OS/2 through the WorkPlace Shell, which is an application that makes extensive use of two DLLs, PMWIN.DLL and PMGPI.DLL. These two DLLs are responsible for providing the API interface that application programmers use. In turn, PMWIN and PMGPI, use the PMGRE in a manner very similar to the shell's use of PMWIN and PMGPI. The PMGRE in turn uses the PD to produce the physical output that the user sees and interacts with, whether this output is on the video display, or a piece of paper. Figure 1 provides an annotated stack model of this simplified view of the OS/2 user interface.
A Brief Overview Of The Printer Subsystem
The OS/2 Print Subsystem consists of three primary components, the print manager, the print spooler, and the presentation driver. Under OS/2 version 2.x the print manager is implemented through WorkPlace Shell printer objects. The WPS printer object provides the user the ability to manage print jobs on a per printer basis, but is not the focus of this article, so little more will be said in regards to it.
OS/2 provides two print spoolers, PMPRINT and PMPLOT, which differ in the fact that PMPLOT supplies a reverse vector clipping engine developed by Steve Matthews for Micrografx, Incorporated, and reimplemented by Wes Bell, also of Micrografx, for IBM, enabling HPGL plotters to produce correct output in what is primarily a raster oriented environment. The purpose of the print spooler is to build a meta-data file of an appli- cations request for hardcopy output, allowing the application to continue processing without without the need to wait for hardcopy output rendering to complete.
The OS/2 print spoolers provide two types of meta-data files, the first is a result of opening the printer OD_QUEUED with the PM_Q_STD standard option. In this mode, the spooler will produce an intermediate data file that contains boundary information as well as the application supplied arguments to the rendering functions. The second format occurs when the printer is opened OD_QUEUED with the PM_Q_RAW option. This combination will produce an intermediate data file that is the raw printer data stream. However, the overall data path through the printer will remain the same.
When used in conjunction with the spooler, the data is passed through the presentation driver twice. In the PM_Q_STD mode, the first pass is used to accumulate bounds and do basic error checking on the appli- ation supplied arguments, while the second pass, initiated by the spooler, will generate the hardcopy output. In the PM_Q_RAW format, the meta-data file created contains raw printer data stream, and will pass through the presentation driver unmodified on the second pass.
The Presentation Driver
The OS/2 presentation driver is built around the concept of the dispatch table. The dispatch table belongs to the OS/2 graphics engine, and is passed to the driver at a specified point during the enable processing, so that the presentaion driver can override the default processing of certain key rendering routines. The enable processing for a presentation driver is handled through a procedure that is aliased to or called OS2_PM_DRV_ENABLE. The following snipet from a presentation driver module definition file's export section aliases a procedure "Enable" to OS2_PM_DRV_ENABLE, and places it at ordinal 200 in the driver. Placing the OS2_PM_DRV_ENABLE procedure at ordinal 200 is required. OS2_PM_DRV_ENABLE = Enable @200
The prototype for the Enable procedure, for the CSet/2(tm) compiler package, is as follows. ULONG _System Enable(ULONG SubFunc,ULONG Param1,ULONG Param2);
The returned value from the enable procedure varies from subfunction to subfunction.
The following table lists the subfunctions.
The handling of each of these subfunctions is covered in some detail in the OS/2 Toolkit white books ( specifically the I/O Subsystem Presentation Driver reference), available from IBM, therefore only a brief summary of each will be provided here.
Producing Hardcopy Output
Once all ENABLE processing has been completed, the presentation driver is ready to begin producing output. In the handling of the Fill_Logical_Device Enable() subfunction, the PD hooks out a copy of the the PMGRE dispatch table. The heart of the OS/2 graphics engine is a dispatch routine that is used to access presentation drivers and internal PMGRE entry points via vectors. The dispatch table is an array of 32bit entries, each representing the addresses of entry points into the graphics engine. Logically, the presentation driver perform one of four actions on a given entry in the dispatch table.
The following macros are taken from my presentation driver template source code, and provide implementations of options two through four.
#define COPY(Fun) \ da##Fun = (PFNL)*(pDispatchTable + \ (NGre##Fun & 0xffL)); #define HOOK(Fun) \ *(pDispatchTable + \ (NGre##Fun & 0xffL)) = (ULONG)Fun; #define SWAP(Fun) \ da##Fun = (PFNL)*(pDispatchTable + \ (NGre##Fun & 0xffL)); \ *(pDispatchTable + (NGre##Fun & 0xffL)) \ = (ULONG)Fun;
Note that the above macros assume that the presentation driver source code provides local storage for SWAP'd and COPY'd vectors in local address space. In my presentation driver template source code, this storage is provided by variables with the prefix "da" .
The following are functions which a driver does NOT need to hook, but should save the PMGRE vector. By calling the vector directly, the overhead of the PMGRE dispatch mechanism is avoided. These entry points are COPY'd.
COPY( Convert ); COPY( SelectClipPath ); COPY( ConvertWithMatrix);
The following entry points should be HOOK'd by the PD and processed completely. None of these involve PMGRE call-back, ie, your presentation driver must do all the work.
HOOK( AccumulateBounds ); HOOK( ImageData ); HOOK( Bitblt ); HOOK( CreateLogColorTable ); HOOK( DeviceCreateBitmap ); HOOK( DeviceDeleteBitmap ); HOOK( DeviceGetAttributes ); HOOK( DeviceQueryFonts ); HOOK( DeviceSelectBitmap ); HOOK( DeviceSetAttributes ); HOOK( DeviceSetDCOrigin ); HOOK( DeviceSetGlobalAttribute ); HOOK( DrawLinesInPath ); HOOK( ErasePS ); HOOK( Escape ); HOOK( GetBitmapBits ); HOOK( GetBoundsData ); HOOK( GetCodePage ); HOOK( GetCurrentPosition ); HOOK( GetDCOrigin ); HOOK( GetLineOrigin ); HOOK( GetPairKerningTable ); HOOK( GetPel ); HOOK( GetStyleRatio ); HOOK( LockDevice ); HOOK( PolyScanline ); HOOK( PolyShortLine ); HOOK( QueryColorData ); HOOK( QueryColorIndex ); HOOK( QueryDeviceBitmaps ); HOOK( QueryDeviceCaps ); HOOK( QueryHardcopyCaps ); HOOK( QueryLogColorTable ); HOOK( QueryNearestColor ); HOOK( QueryRealColors ); HOOK( QueryRGBColor ); HOOK( RealizeColorTable ); HOOK( RealizeFont ); HOOK( ResetBounds ); HOOK( SetBitmapBits ); HOOK( SetCodePage ); HOOK( SetLineOrigin ); HOOK( SetPel ); HOOK( SetStyleRatio ); HOOK( UnlockDevice ); HOOK( UnrealizeColorTable );
The following entry PMGRE entry points should also be supported by the presentation driver, but may also be safely passed back to the PMGRE supplied entry points if the processing involves complex clipping, non-device fonts, etc. These entry points may be safely SWAP'd by a printer presentation driver.
SWAP( Arc ); SWAP( BeginArea ); SWAP( BoxBoth ); SWAP( BoxBoundary ); SWAP( BoxInterior ); SWAP( CharString ); SWAP( CharStringPos ); SWAP( DeviceQueryFontAttributes ); SWAP( EndArea ); SWAP( FillPath ); SWAP( FullArcBoth ); SWAP( SetCurrentPosition ); SWAP( FullArcBoundary ); SWAP( FullArcInterior ); SWAP( NotifyClipChange ); SWAP( NotifyTransformChange ); SWAP( PartialArc ); SWAP( PolyLine ); SWAP( PolyMarker ); SWAP( QueryCharPositions ); SWAP( QueryTextBox ); SWAP( QueryWidthTable ); SWAP( SetArcParameters );
The internals of each of the PD supplied entry points is dependent on the physical device type (printer or display, raster or vector), the algorithms and data structures chosen, and of course the developer. It is therefore not within the scope of this article to discuss them further. However, each of the external entry points to the presentation driver, and for that matter, the PMGRE have a common parameter, that is worth discussing. The last parameter to all entry points is a 32 bit unsigned value which contains the entry point id in the low 16 bits and a set of flags in the high order 16 bits. These bits should be checked at the beginning of each entry point procedure, as they contain additional control information governing exactly what the PD should do. For example, on the first pass through the driver, the COM_DRAW bit will be clear, while the COM_BOUND bit will be set. In this case, the PD need only accumulate bounds for the operation, and need not produce any physical output. Other bits of interest are used to indicate whether or not the driver is currently within a path or area bracket.
A Few Final Notes
For a display PD, there are several additional entry points, related to the support of mouse and text cursors. Under V2.0+ of OS/2, the display driver also has the additional overhead of the Virtual Device Driver necessary to support the multiple virtual DOS machines (MVDMs) and WINOS2. Overall, display drivers are more difficult to write than printer drivers due to performance considerations. If you are interested in creating a display Presentation Driver, I would highly recommend contacting IBM through the Developers Assistance Program (the local IBM branch office should be able to help.)
OS/2 Presentation Drivers are not overly complex, neither are they simple. To successfully create a PD requires careful thought and design as well as a strong knowledge of computer graphics in general. The use of modular and structured programming techniques, along with object oriented concepts (one need not use an object language to write object oriented code) will make the development cycle far less frustating, and much more rewarding. Remember that a little extra effort in the design phase can save a great deal of recoding in the testing phase.