Multiple Screen Driver Support (Windows Embedded CE 6.0)
1/6/2010
The OS helps connect multiple screens to a Windows Embedded CE-based device. Multiple screens allow applications to use multiple display devices at the same time.
You can use multiple screens as one large combined screen. This extra space can be useful when you need to maximize your onscreen workspace, or when performing tasks in desktop publishing, Web development, or video editing.
The following code examples show how to implement a driver that supports multiple screens.
The code examples are based on the sample ATI driver. This driver is the only sample driver provided that supports multiple screens. The source code for the sample driver is in the %_WINCEROOT%\Public\Common\OAK\Drivers\Display\ATI\ATI.cpp directory.
To implement a driver that supports multiple monitors, implement the GetGPEPerCard function in your driver. GetGPEPerCard is a new function specific to multiple screen implementations. The parameter iCard, which is being passed, is the index of the card. Zero-based indexing is used.
For example, if you have two cards, the OS passes the integer 1 to indicate the second card. The function returns a pointer to the structure for that card. The OS needs the pointer to correctly draw on that particular screen.
For the OS to recognize the pointer to the function for referencing a particular card, be sure that when you implement DrvEnableDriver, you implement it as shown in the following code sample and pass the correct card pointer.
BOOL APIENTRY DrvEnableDriver(ULONG iEngineVersion,
ULONG cj,
DRVENABLEDATA *pded,
PENGCALLBACKS pEngCallbacks)
{
pfnGetGPEPerCard = GetGPEPerCard;
return GPEEnableDriver(iEngineVersion, cj, pded, pEngCallbacks);
}
Because you have multiple cards, you must create an array of GPE structures, one for each card.
The following code example creates an array of GPE structures of MONITORS_MAX at the beginning of the %_WINCEROOT%\Public\Common\OAK\Drivers\Display\ATI\Ati.cpp directory. MONITORS_MAX cannot be greater than 4.
static GPE *pGPE = (GPE *)NULL;
//create a GPE array
static GPE *pATI[MONITORS_MAX] = {NULL};
//variable that states the total monitors in the system
static int cMonitors = 0;
Next, enumerate all display devices on the system that this driver supports. In the example, the driver checks the registry for instance information to determine how many displays are present. For each display the driver finds, the driver must initial and assign the pointer to the GPE data structure.
The following code example shows the code that appears in the ATI example.
// look for all the monitors we are supposed to support
for(gdwMonitors = 0; gdwMonitors < gdwMonitorsExpected && fOk == TRUE; gdwMonitors++) {
HKEY hkInstance;
DDKWINDOWINFO dwi;
DDKPCIINFO dpi;
#define INSTANCE_LEN 256
TCHAR szInstance[INSTANCE_LEN];
// read the registry to get our PCI instance information
_sntprintf(szInstance, INSTANCE_LEN, _T("%s%u"), gszBaseInstance, gdwMonitors + 1);
szInstance[INSTANCE_LEN-1] = _T('\0'); // Guarantee null-termination
dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szInstance, 0, 0, &hkInstance);
if(dwStatus == ERROR_SUCCESS) {
dwi.cbSize = sizeof(dwi);
dwStatus = DDKReg_GetWindowInfo(hkInstance, &dwi);
if(dwStatus == ERROR_SUCCESS) {
dpi.cbSize = sizeof(dpi);
dwStatus = DDKReg_GetPciInfo(hkInstance, &dpi);
}
RegCloseKey(hkInstance);
}
// check the registry information
if(dwStatus == ERROR_SUCCESS) {
if((dpi.dwWhichIds & (PCIIDM_VENDORID | PCIIDM_CLASS | PCIIDM_SUBCLASS)) != (PCIIDM_VENDORID | PCIIDM_CLASS | PCIIDM_SUBCLASS)) {
dwStatus = ERROR_INVALID_DATA;
} else if(dpi.idVals[PCIID_VENDORID] != PCI_VENDOR_ATI
|| dpi.idVals[PCIID_CLASS] != PCI_CLASS_DISPLAY
|| (dpi.idVals[PCIID_SUBCLASS] != PCI_SUBCLASS_DISPLAY && dpi.idVals[PCIID_SUBCLASS] != 0x80)) {
dwStatus = ERROR_INVALID_DATA;
} else if(dwi.dwNumMemWindows != 2) {
dwStatus = ERROR_INVALID_DATA;
}
}
// did we find the device?
if (dwStatus != ERROR_SUCCESS) {
// Couldn't find an MQ200 card, what to do now?
RETAILMSG (1, ((L"ATI card instance %d not found at '%s'\r\n"), gdwMonitors + 1, szInstance));
fOk = FALSE;
} else {
#define RESSTRING_LEN 16
TCHAR szResolution[RESSTRING_LEN],szResString[RESSTRING_LEN];
_sntprintf(szResString, RESSTRING_LEN, TEXT("%s%i"), TEXT("RESOLUTION"), gdwMonitors);
szResString[RESSTRING_LEN-1] = TEXT('\0'); // Guarantee null-termination
dwSize = sizeof(szResolution);
dwStatus = RegQueryValueEx(hkGDI, szResString, NULL, &dwType, (LPBYTE)szResolution, &dwSize);
szResolution[RESSTRING_LEN-1] = TEXT('\0'); // Guarantee null-termination
if(dwStatus == ERROR_SUCCESS && dwType == REG_SZ) {
pATI[gdwMonitors] = new ATI(&dwi,Bpp,szResolution);
} else {
pATI[gdwMonitors] = new ATI(&dwi,Bpp, NULL);
}
}
}
The following code example shows how to modify a function to support single and multiple monitors. Use this function for single monitor support. You must implement this function for any Windows Embedded CE–based display driver.
// Main entry point for a GPE-compliant driver
GPE *GetGPE()
{
if (!pGPE)
{
if (!EnableAllCard())
return NULL;
pGPE = (GPE *)pATI[0];
}
return pGPE;
}
See Also
Concepts
Display Driver Extensions
Display Driver Development Concepts
Display Driver Samples