PDRREF:Introduction to OS/2 Presentation Drivers

OS/2* operates within the confines of a four-ring structure, Ring 0 through Ring 3. As shown in the following figure, Ring 0 is the most-protected ring and contains the OS/2 kernel, which is the main engine that drives the operating system. Ring 3 is the least-protected ring and contains applications and system services. Ring 1 is not used by OS/2. Ring 2 is similar to Ring 0 in that it provides access to physical devices such as printers and displays. Ring 2 is similar to Ring 3 in that it is prohibited from modifying kernel data structures.

The following figure shows the ring structure



Throughout the ring structure, functions can be called from the same ring level or any numerically lower ring level. Conversely, data can be accessed from the same ring level or any numerically higher ring level.

For the sake of compatibility between 16-bit and 32-bit code, all 32-bit code is treated as "Ring-2-Conforming". This means that 32-bit code runs at the ring level of its caller. For example, if the code is called from Ring 3, it runs at Ring 3. If the code is called from Ring 2, it runs at Ring 2. This allows easier access to functions and data, and eliminates many of the costly ring transitions encountered in the previous 16-bit system. To be Ring-2-conforming (rather than strictly Ring 2 or Ring 3), a function cannot call any strictly Ring 3 code or directly access the hardware. In other words, it must abide by the restrictions of both ring levels.

Many of the system services that exist at Ring 3 in dynamic link libraries (DLLs) access another DLL called PMGRE.DLL, which is the graphics engine or kernel of the OS/2 graphics subsystem. The graphics engine is composed entirely of Ring-2-conforming code that interfaces with the presentation drivers, which interface directly with the physical device drivers (at Ring 0) and the hardware. These DLLs are identified as presentation drivers for hardcopy devices (hardcopy drivers) by the file-name extension .DRV, and as presentation drivers for display devices (display drivers) by the file-name extension .DLL. The following figure shows a conceptual view of presentation drivers.



Conceptual view of presentation drivers in the flow of control from an application program to the hardcopy device and the display screen.

The graphics engine loads and enables the presentation drivers, and then dispatches calls to them through dispatch tables as Ring 2, Ring 3, or Ring-2-conforming code. For optimal system performance, write all of the functions in 32-bit presentation drivers be written as Ring-2-conforming. This eliminates the need for ring transitions from system services at Ring 3 and from 16-bit presentation drivers at Ring 2.

By exporting a table named OS2_PM_DRV_RING_LEVELS in 32-bit presentation drivers, the ring level of each function in the dispatch table can be selected.

Note: If this table is not exported, all 32-bit functions will be dispatched as Ring-2-Conforming.

The presentation driver has certain responsibilities to the graphics engine. Specifically, a number of entry points exist within the graphics engine that the presentation driver is required to hook (a mechanism by which procedures are called) and support. Many of these functions (as they currently exist in the graphics engine) are not truly functional, and if calls were made to these entry points, nothing would happen. In many cases, they simply return when called.

There are other entry points in the graphics engine that can be optionally hooked by the presentation driver (for example, where only light processing is required, it might be preferable to use the presentation driver). These entry points can be called by the presentation driver for special processing.

The graphics engine calls the entry points within the presentation driver by means of a dispatch table. The dispatch table is a block of memory allocated by the graphics engine for the containment of entry points and is assigned for use by a presentation driver. Each presentation driver loaded by the system is given its own separate dispatch table by the graphics engine. When a presentation driver device context is enabled, the graphics engine allocates a dispatch table for that presentation driver and fills the dispatch table with pointers. Each entry in the table is a 32-bit pointer that points back to a specific routine existing in the graphics engine.

Because many of these routines must be hooked by the presentation driver, the graphics engine refers to the dispatch table to find the appropriate pointer any time that it calls a function in the presentation driver. At this point, however, all of the pointers in the dispatch table point back to routines in the graphics engine. The presentation driver must go into the dispatch table itself and replace some of the pointers with new pointers that point to corresponding routines within the presentation driver. This is mandatory for some routines, optional for others. The hooking of pointer entries in the dispatch table occurs the first time the presentation driver is called at its OS2_PM_DRV_ENABLE entry point for the first subfunction (see FillLogicalDeviceBlock). The following figure shows how the Graphics Function Dispatch Table works.



The exported entry point OS2_PM_DRV_ENABLE (see OS2_PM_DRV_ENABLE) has ten subfunctions. Four of these subfunctions are used in the enable process of a device context, three are used in the disable process, one is used to save the device context, one is used to restore the device context, and one is used to reset the device context.

When Enable Subfunction 01H - FillLogicalDeviceBlock is called, the graphics engine passes to it a pointer to the dispatch table that it allocated for the presentation driver. FillLogicalDeviceBlock then must substitute all of the mandatory pointers (and perhaps some optional routines) in the dispatch table with pointers to corresponding routines within the presentation driver itself.

The Developer's Toolkit for OS/2 provides support for writing source code in either C or assembler language. This support consists of a set of header files that define the functions, structures, and constants used in the internal interface to the presentation driver. Presentation drivers might also need to include OS2.H or OS2.INC, which define system functions, structures, and constants.

As with other components of OS/2, the presentation driver architecture ensures that: Note that although many of the functions must be hooked by the presentation driver, some might also be passed back to the graphics engine for processing when called. Save the original pointer values stored in the dispatch table by the graphics engine before hooking them. Having access to the original pointers to the graphics engine simulation routines allows the presentation driver to optionally make calls back to the graphics engine (that is, to have the graphics engine do the processing instead). This technique has often proven to be a tremendous time saver in developing OS/2 presentation drivers, especially when calling back to the graphics engine to perform clipping routines.
 * Once loaded, the presentation driver can be enabled for use by multiple applications or processes.
 * Once enabled, the presentation driver can support multiple instances of a device context for the owning application or process.

When the Presentation Manager* interface is initialized, the presentation driver for the attached display is loaded and enabled. This driver has direct access to the video hardware. The function calls passed to the presentation driver are processed and then passed to the adapter interface.

Hardcopy Devices (Printers and Plotters)
For hardcopy drivers, the presentation driver is loaded in response to an application or process calling DevOpenDC. Upon receipt of this routine, the Presentation Manager interface looks to see if the required presentation driver is loaded, and if it is able to handle the new device context (DC). If the presentation driver is loaded and can handle the DC, the driver is enabled for the new DC. If either of these conditions is not met, the required driver is loaded and then enabled.

Note: The amount of time required by the system to load a driver can be reduced by supplying Extended Attribute information in the .DRV file. See Extended Attributes for further information.

Calling Conventions
Presentation drivers interact with both the external API and the internal interface to the graphics engine and dispatch table. Parameters are passed as 32-bit values on the stack by using the C convention. These parameters are pushed to the stack in reverse order of how they appear in the statement. The hardware architecture locates the conceptual top of the stack at the high-order address of the stack space. When data is pushed to the stack, the stack pointer is decremented so that upon completion, the pointer addresses the first item of data.

At entry to the handling routine, the stack contains a frame of 32-bit parameters and a return address as shown in the following figure.



Function Number and Command Flags
The first doubleword (DWORD) pulled from the stack contains two fields: The flags in this 16-bit field tell the presentation driver which operations need to be done while it processes the function:
 * COM_DRAW : Bit 0. If this flag is set, the presentation driver must draw the output of the function at the device. When not set, the driver does not draw the output. The function must still be processed to update internal data, such as current position and, if specified, boundary calculations.
 * COM_BOUND: Bit 1. If set, the presentation driver must calculate the bounding rectangle for the output of the specified function. Upon completion, the driver calls its own GreAccumulateBounds routine to accumulate the bounding rectangle (GPI_BOUNDS in model space coordinates).
 * Note: All presentation drivers must be able to calculate bounds on any figure they can draw.


 * COM_CORR: Bit 2. This flag is significant only for display drivers. If set, the display driver must determine whether the output of the specified function intersects the pick window. The result, TRUE or FALSE, is passed back to the caller in the return code for the called function. For details, see the function descriptions in Mandatory Functions for All Drivers and Simulated Functions.
 * COM_ALT_BOUND: Bit 3. This flag is significant only for display drivers. It indicates that the display driver should accumulate USER_BOUNDS in screen coordinates. Notice that user bounds are those used by the Window Manager. Hardcopy drivers do not accumulate user bounds.
 * COM_AREA: Bit 4. If set, the function is part of an area. Calls to functions that define an area (for example, GreSetCurrentPosition and GrePolyLine), or that are invalid in an area definition, are passed back to the graphics engine for processing by the default handler.
 * If all functions in the area component have been hooked by the presentation driver, it is not necessary to pass back functions received with COM_AREA set.


 * COM_PATH: Bit 5. This flag is similar to the COM_AREA flag. Calls to functions that define a path (for example, GrePolyLine and GreSetCurrentPosition), or that are invalid in a path definition, are passed back to the graphics engine for processing by the default handler.
 * If all functions in the path component have been hooked by the presentation driver, it is not necessary to pass back functions received with COM_PATH set.


 * COM_TRANSFORM: Bit 6. This flag is set by the graphics engine. If set, the engine is working in world coordinates and the presentation driver should return world coordinates for queries, and convert to device space (using GreConvert) for drawing functions. If this bit is not set, the graphics engine is working in device space and the presentation driver should return device coordinates for queries and should not convert to device space for drawing functions.
 * COM_RECORDING: Bit 7. This flag is ignored.
 * COM_DEVICE: Bit 8. If set, the presentation driver must process the function. The driver should not pass the function to the graphics engine.
 * COM_PRECLIP: Bit 10. If set, the graphics engine has passed the function's data already clipped in screen coordinates.

The COM_PRECLIP flag will only be used and set by the Graphics Engine when a presentation driver reports CAPS_CLIP_FILLS in the CAPS_ADDITIONAL_GRAPHICS capability field of the mandatory entry point GreQueryDeviceCaps.

Note: This flag is supported if the Graphics Engine is Version 202 or greater.

Note: COM_DRAW, COM_BOUND, COM_CORR, COM_ALT_BOUND, COM_AREA, and COM_PATH apply only to drawing functions. They are ignored by all other functions. The remaining bits of the Command Flags field are not defined and their values are ignored.

Device Context
Device contexts provide the mechanism that the application program uses to write output data to devices. The application, or one of its processes, opens a device context with DevOpenDC, associates a presentation space to the DC, and writes or draws in that space. Each DevOpenDC creates an instance of a DC. That instance is eliminated when the application closes the device context. The created DC is seen internally as a dispatch table. Calls from the application program to the DC are passed, as one or more internal Grexxx routines, through the dispatch table to the handling routines in the presentation driver for the DC, or are passed back to the Grexxx simulation routines.

Each instance of a device context has:
 * Device context types
 * Data types (for only OD_QUEUED device context type)
 * Instance data
 * Program stack

Device Context Types
For presentation drivers that support the device context type, OD_QUEUED, the driver must support the PM_Q_STD and PM_Q_RAW data types as defined by the Presentation Manager interface. Support for other data types is optional.

The concept of data types only applies when the device context type is OD_QUEUED. For all other device context types (OD_DIRECT, OD_MEMORY, OD_METAFILE, OD_METAFILE_NOQUERY, and OD_INFO), the pszDataType field in the DEVOPENSTRUC structure has no meaning. See FillPhysicalDeviceBlock for details on when the DEVOPENSTRUC structure is passed to the presentation driver. However, a hardcopy driver is never requested to open an OD_METAFILE or OD_METAFILE_NOQUERY device context type because these types are handled by GPI. See Graphics Engine Hardcopy Drivers for details on how the hardcopy driver creates the spool file.

The basic differences between data types, PM_Q_STD and PM_Q_RAW, are described below:
 * PM_Q_STD:The hardcopy driver uses the spooler to create a device-independent spool file using the SplStdxxx and SplQmxxx interfaces.
 * PM_Q_RAW:The hardcopy driver processes the Grexxx functions to generate device-specific output data. This data is written using the spooler SplQmxxx interface to a spool file.

Data Types (Hardcopy Drivers Only)
For presentation drivers that support the device context type, OD_QUEUED, the driver must support the PM_Q_STD and PM_Q_RAW data types as defined by the Presentation Manager interface. Support for other data types is optional.

The concept of data types only applies when the device context type is OD_QUEUED. For all other device context types (OD_DIRECT, OD_MEMORY, OD_METAFILE, OD_METAFILE_NOQUERY, and OD_INFO), the pszDataType field in the DEVOPENSTRUC structure has no meaning. See FillPhysicalDeviceBlock for details on when the DEVOPENSTRUC structure is passed to the presentation driver. However, a hardcopy driver is never requested to open an OD_METAFILE or OD_METAFILE_NOQUERY device context type because these types are handled by GPI. See Graphics Engine Hardcopy Drivers for details on how the hardcopy driver creates the spool file.

The basic differences between data types, PM_Q_STD and PM_Q_RAW, are described below:
 * PM_Q_STD:The hardcopy driver uses the spooler to create a device-independent spool file using the SplStdxxx and SplQmxxx interfaces.
 * PM_Q_RAW:The hardcopy driver processes the Grexxx functions to generate device-specific output data. This data is written using the spooler SplQmxxx interface to a spool file.

Instance Data
For every instance of a device context, the system has a doubleword that is reserved for use by the presentation driver. Typically, this doubleword is used by the presentation driver to hold a pointer to information about the current state of the device context. This pointer is returned to the system by the driver when the device context is enabled. On subsequent calls through the dispatch table, the pointer is passed back to the presentation driver as a parameter (pInstance) on the program stack. For more information, see EnableDeviceContext.

Program Stack
A stack of 4KB is available for use when a function is passed to the driver at Ring 2. If it needs more than 4KB, the presentation driver should allocate its own stack space, switch to that stack on entry, and switch back to the original stack on exit. At Ring 3, the presentation driver will use the application's stack when a function is passed to the driver.

Calls to the presentation driver use C language calling conventions. Parameters are pushed to the stack in the opposite order as they are in the call statement.

Dynamic Link Library Functions
Functions exported and imported by a dynamic link library are identified in the library module definition file. These provide links between libraries and subsystems. For example, the components of the Presentation Manager interface must call an enable entry point in the presentation driver. The presentation driver needs access to the simulated functions in the graphics engine. For information on how to develop a dynamic link library (DLL), refer to the OS/2 Programming Guide and OS/2 Application Design Guide.

Note: The initialization routine for a dynamic link library, including presentation drivers, must be compiled to run at Ring 3 (privilege level 3).

Exported Functions
There are two types of exported functions used by OS/2:
 * Presentation drivers
 * Graphic engine functions

Presentation Drivers
Dynamic link libraries for presentation drivers must export the appropriate set of the following entry points:  EXPORTS MoveCursorBlock   @103           /* Display drivers only  */ OS2_PM_DRV_QUERYSCREENRESOLUTIONS /* Optional             */ OS2_PM_DRV_DEVMODE               /* Hardcopy drivers only */ OS2_PM_DRV_DEVICENAMES           /* Hardcopy drivers only */ DrvInstall                       /* Optional              */ DrvRemove                        /* Optional              */ OS2_PM_DRV_RING_LEVELS           /* All drivers           */ OS2_PM_DRV_ENABLE_LEVELS         /* All drivers           */ OS2_PM_DRV_ENABLE                /* All drivers           */  In addition to the entry points listed above, hardcopy drivers should export entry points for the routines that handle dialogs with the user. See Exported Entry Points (Cursor); also see Exported Entry Points and Exported Driver Function Reference.

Graphics Engine Functions
The graphics engine exports its own set of entry points. Those that are significant to the presentation driver are: EXPORTS GETDRIVERINFO   @30 SETDRIVERINFO   @31
 * GETDRIVERINFO:Used by the presentation driver to get the instance pointer (pInstance) for a specified device context, or to get a pointer to the bit-map header for a specified bit map. See System Functions for more information.
 * SETDRIVERINFO:Used by the presentation driver to set a specified value in the instance pointer of a specified device context.

Note: Instance pointers (pInstance) and data are discussed under EnableDeviceContext.

Imported Functions
To call a Grexxx function that is supported as a simulation or internal function in the graphics engine, call the imported entry point with the Grexxx function parameters. The pInstance parameter should be NULL. For example, to call GreCreateJournalFile with the name assigned in the module definition file, use: result = GreCreateJournal (pszFileName, flOption, cSize, 0L, NGreCreateJournalFile);

Note: NGreCreateJournalFile is defined in the header file. See the description of GreCreateJournalFile.

Simulations (functions for the presentation driver interface that are supported by handling routines in the graphics engine) can also be called at the addresses given in the default dispatch table. Use the addresses contained in the dispatch table that is passed to the presentation driver at Enable time.

Presentation Driver Interface
The internal Presentation Driver interface is comprised of a set of graphics engine (Grexxx) functions that are called through a dispatch table. A dispatch table is an array of pointers to function-handling routines. The low-order byte of the function number identifies the member of the array that contains the pointer for the function. The functions called through the dispatch table fall into three main groups: The first instance of a loaded presentation driver is given a copy of the default dispatch table. The Enable routine in the presentation driver modifies this copy so that, for those functions supported in the driver, the pointers address the function-handling routines of the presentation driver.
 * Functions that all presentation drivers must support. See Mandatory Functions for All Drivers.
 * Functions that must be supported by display drivers. See Mandatory Functions for Display Drivers.
 * Functions that are supported by simulations in the graphics engine. See Simulated Functions.

When the function is called a second time (or any time thereafter) for the same presentation driver, a NULL dispatch table pointer can be given because the graphics engine already has the table correctly initialized. It is therefore not necessary to reinitialize the table.