Apart from the calls to
DDI_Load()
and
DDI_Unload()
, which are made when the OSDI is loaded and unloaded by Windows, all calls
into the OSDI are made through the function DDI_Entry() by the DP4 terminal
manager. This function has four parameters:
Not all commands use the third or fourth parameters.
The commands map into the user functions described below as follows:
| Commands | Function |
|---|---|
| 0 | DDI_InitTask() |
| 1 | DDI_Error() |
| 2 | DDI_SelectSubdevice() |
| 3 | DDI_PollInput() |
| 4 | DDI_EnableInput() |
| 4 & 7 | DDI_DisableInput() |
| 5 | DDI_DvcStatus() |
| 6 | DDI_OutputData() |
| 7 | DDI_EndTask() |
All the other values are reserved for future expansion. If you want to add custom commands into an OSDI then export another function and call that.
Details of the functions to be modified are given below:
int pascal DDI_Load(HINSTANCE hInst)
This function is called by the operating system when the OSDI is loaded by the terminal manager. (N.B. In the Win32 environment it is not called again if you create a new thread in your application). This function can be used to perform any load time initialisations your OSDI requires. Note that the OSDI will always receive a call to DDI_InitTask() as soon as the terminal manager has loaded the OSDI.
int pascal DDI_Unload(int nExitType)
This function is called immediately before the OSDI is unloaded by the operating system after the 16 bit terminal manager has freed DIPW.DLL. This function is not called in the 32 bit version. This was an unintentional omission, but does not seem to have caused any problems.
The terminal manager will load and unload the OSDI each time an application opens and closes an Initialisation POD for a device group that requires the OSDI. It has been found that this can cause problems in some cases. An easy work around for the problem is to add a call to the Windows API LoadLibrary() in the application, to load the OSDI. This will prevent the OSDI from being loaded and unloaded by the terminal manager.
void pascal DDI_InitTask(struct USER_INFO near * user_info)
This function is called from dipw.c when the terminal manager makes the initial call to the OSDI. This function should perform any initialisation necessary that was not performed by DDI_Load()
void pascal DDI_EndTask(struct USER_INFO near * user_info)
This function is called when the terminal manager is closing the OSDI device group. It should perform any clean-up necessary. (For example unsubclassing the terminal manager main window if this has been subclassed). You should remember that both this function and the previous one may be called multiple times in the life of an application, as the application may sometimes select a device group that uses an OSDI, and sometimes an ordinary printer.
int pascal DDI_Error(struct USER_INFO near * user_info)
This function is called when the DP4 terminal manager wants to know if an error has occurred. It may be called after selecting a new COD (calls to DDI_SelectSubdevice()) or calls to output data. DDI_OutputData(). It may also be called after DDI_InitTask() if the application does not immediately select a COD. The return value is the desired error code, or 0 for no error.
void pascal DDI_SelectSubdevice(struct USER_INFO near * user_info, int
new_device)
This function is called whenever the application selects a POD that has the
Requires Device Driver
flag set. The function is called whether or not the new COD that is selected
by the POD is different from the previously selected COD. You can inspect the
subdevice
member of the user_info structure to determine if the COD is different or not.
Any parameters passed in the COD application will be received by the DDI_OutputData() function described later on. Unlike for other operating systems it is the responsibility of the OSDI to determine what is parameter and what is "ordinary" output data.
int pascal DDI_EnableInput(struct USER_INFO near * user_info, int ip_device,int
already_enabled)
This function is called when the application calls dvc_set_inp_mask(). Typically applications will call dvc_set_inp_mask() before every input field for which the device_mask is not zero, and then call the function again after the input field to set the input device to 0.
The code in DIPW.C makes one call to DDI_EnableInput() for every bit that is set in the new device_mask. For each call the already_enabled parameter will be non zero if that bit was already set (which it won't be if the application has followed the convention above). The value for ip_device is the value of the bit mask for that device, and not the number of the bit: i.e. if the application calls dvc_set_inp_mask(5) then the calls to DDI_EnableInput() will have parameters 1 and 4 for ip_device.
The return value from DDI_EnableInput() is very important. DIPW.C "ors" all the return codes from the calls to DDI_EnableInput() together. If the resulting overall return code is non zero then whenever the terminal manager wants to request input from the user it uses a "polling" mode: it will continuously inspect the application message queue for keyboard and mouse messages, and whenever there are no messages it will call the DDI_PollInput() function to see if the OSDI has input for it. Using the polling method prevents the application from "idling" when it is waiting for input, and can degrade performance.
The preferred method of returning input to the application from the OSDI is as follows:
All calls to DDI_EnableInput() return 0. Now when the terminal manager wants input it will simply call the Windows API GetMessage() and wait for a message.
The OSDI must creates thread to monitor the input devices (unless they are interrupt driven or there is some kind of system hook which makes this unnecessary). It must also call RegisterWindowMessage("DP4_DDI_IN") to discover the message number to be used for communication with the terminal manager. (This could be done in DDI_Load() or DDI_Input_Task().)
If the OSDI receives input from one of the input devices it must format it into an array of short values. Each short can either be an untranslated key value, or alternatively one of the standard DP4 key codes listed in the file userdata.h. The OSDI must call PostMessage() (not SendMessage()!) specifying the following values for the parameters:
hWnd
: The handle of the application main window as held in the user_info structure
wMsg
: The number for the "DP4_DDI_IN" message as returned by the call to
RegisterWindowMessage("DP4_DDI_IN")
wParam
: The number of "characters" in the buffer to be returned.
lParam
: The address of the buffer of input characters.
Here is some information about character translation:
Having non translated keys makes it possible to insert keys whose meaning is forced: you use the file USERDATA.SYS to do this. For example you could insert GR_F(1) to insert an F1 key, which will be F1 even if F1 is normally translated to help.
The buffer returned should be for a single, complete entry field if at all possible and the first three entries in the array must specify the source of the input using the following technique:
The following example code may be be a clearer explanation of what is required:
buffer[0] = GR_NEWDVC; buffer[1] = src & 0xff; buffer[2] = (unsigned) src >> 8; |
In theory you could also return input to the application by posting a series of WM_CHAR messages, but in this case you run the risk of getting input from different sources muddled up with each other.
void pascal DDI_DisableInput(struct USER_INFO near * user_info,int ip_device)
DDI_DisableInput() is also called as a result of application calls to dvc_set_inp_mask(). It is called once for every bit that was set in the previous input device bit mask, and which is not now set. This function may also be called when the OSDI is unloaded. Just before DDI_EndTask() is called any input devices that are still enabled will be disabled by calls to this function.
int pascal DDI_PollInput(struct USER_INFO near * user_info,short far * buffer,
int trm_waiting)
This function is called when the OSDI is in polling mode and the terminal manager is requesting input. The buffer should be filled using the same format as described for the "DP4_DDI_IN"message above. The return value is the number of characters in the buffer, including the initial 3 shorts used for specifying the input device.
int pascal DDI_DvcStatus(struct USER_INFO near * user_info,char far * buffer)
This function is called when the application calls dvc_get_status(). It is envisaged that this information will be information about the status of the currently selected output device, but the implementor is free to return any information desired.
The format of the data is expected to be a null terminated string, though from C this is not mandatory. Within QAB returning data with embedded nulls will cause unpredictable results. Within QAB returning more than 128 bytes of data may cause a catastrophic error such as a protection fault, but C programs can access up to 255 bytes of data.
The return value should be the number of bytes of data copied to the buffer, not including the terminating 0.
void pascal DDI_OutputData(struct USER_INFO near * user_info,char far *
buffer,int length)
This function is called whenever the terminal manager wants to output data. It is used for both parameters and ordinary data. One technique for distinguishing between parameters and data is to set a flag whenever DDI_SelectSubdevice() is called. Until DDI_OuputData() receives the next newline,"\r\n" to be exact (assuming this is the new line sequence you defined for the device group) , any data passed to it is considered to be parameters. Subsequently any number of lines of output may be sent. Each line will be terminated with the newline sequence.
The format of the data is an array of non null characters. buffer[wParam] is 0, so the data can be processed using str() functions.
The following information applies only to the current implementation of the terminal manager: if you choose to take advantage of it you may be forced to change your code later on (though this is unlikely).
This command may be called an arbitrary number of times in order to perform output. Typically for each line of output you will see two calls - one with all the text and one with a line separator. However if data is output into a coloured map, fields will be split into sections of identical colour separated by attribute setting strings.
The terminal manager is primarily responsible for managing the keyboard and mouse in a DP4 Windows application. Nevertheless the OSDI can at least partially over-ride this using either or both of the following techniques:
It is worth referring to the following pages in the non-Windows part of this section:
However, the latter page does have some information which is not relevant to the Windows OSDI because of its different construction.