Share via


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