Writing Windows CE Display Drivers
by Jeff Spurgat



Listing One
NewGPE::NewGPE()
{
    // Flags for hardware features
    m_bIsVGADevice = TRUE;      // default to VGA device
    m_bHWCursor = FALSE;        // default to software cursor
    m_b555Mode = FALSE;         // default to 5-6-5 mode for 16Bpp

    // NOTE: The following data members are modified to their final values
    // by the default implementation of SetMode and shouldn't need to
    // modified here or in ModeInit.
    // 
    m_pPrimarySurface = NULL;   // pointer to primary display surface
    m_nScreenWidth = 0;         // display width
    m_nScreenHeight = 0;        // display height
    m_pMode = NULL;             // pointer to information on current mode
    m_p2DVideoMemory = NULL;    // pointer to video memory manager
    m_pLAW = NULL;              // pointer to linear access window
    memset(&m_ulBitMasks[0], 0, sizeof(m_ulBitMasks)); // bit masks

    // NOTE: The following data members MUST be modified to their final
    // values by the display hardware specific function ModeInit.
    // 
    m_nLAWPhysical = 0;     // the physical address of the linear access
                            // window for accessing the frame buffer
    m_nLAWSize = 0;         // size of linear access window
    m_nVideoMemorySize = 0; // size of video memory, which can be different
                            // than the linear access window
    m_nVideoMemoryStart = 0;// offset within the linear access window to the
                            // start of video memory (usually is 0)
    m_nScreenStride = 0;    // number of bytes per display line
}


Listing Two
int NewGPE::NumModes()
{
    // count the number of entries in the mode table
    BOOL bDone = FALSE;
    int nIndex = 0;
    while (!bDone) {
        if (m_gpeModeTable[nIndex].Bpp==0) {
            // no more entries in the table
            bDone = TRUE;
        }
        else {
            // count entry and go to next entry
            nIndex++;
        }
    }
    return nIndex;
}
SCODE NewGPE::GetModeInfo(GPEMode *pMode, int modeNo )
{
    // make sure that the mode is valid (index is zero based)
    if ((modeNo<0) || (modeNo>=NumModes()))
        return E_INVALIDARG;
    // get data from mode table
    *pMode = m_gpeModeTable[modeNo];
    return S_OK;
}


Listing Three
NewGPE::SetMode(int modeId,HPALETTE *pPaletteHandle )
{
    int nModeNum = 0;
    GPEMode gpeMode;
    // get mode entry that matches modeId
    BOOL bDone = FALSE;
    int nNumModes = NumModes();
    while (!bDone) {
        if (nModeNum>=nNumModes) {
            // failed to find matching mode entry
            bDone = TRUE;
        }
    else if ((GetModeInfo(&gpeMode, nModeNum)==S_OK) &&
        (gpeMode.modeId==modeId)) {
            // found matching mode entry
            bDone = TRUE;
        }
        else {
            // check next entry
            nModeNum++;
        }
    }
    // check if mode number is valid
    if (nModeNum>=nNumModes) {
        return E_INVALIDARG;
    }
    // pass mode info to the hardware specific initialization function
    SCODE scInit = ModeInit(&m_gpeModeTable[nModeNum]);
    if (scInit!=S_OK) {
        // something failed here, don't go any further
        return scInit;
    }
    // verify parameters ModeInit is required to initialize
    if ((m_nLAWPhysical==0) || (m_nLAWSize==0) || (m_nVideoMemorySize==0) ||
        (m_nScreenStride==0)) {
        // ERROR: ModeInit failed to properly initialize some data
        //        members that are required. These data members need
        //        to be initialized for this function to work.
return E_FAIL;
    }
    // continue with hardware independent initialization
    // Using the following values initialized by ModeInit:
    // * m_nLAWPhysical
    // * m_nLAWSize
    // * m_nVideoMemorySize
    // * m_nVideoMemoryStart
    // * m_nScreenStride
    // * m_bHWCursor (optional)
    // * m_b555Mode (optional)
    // Initialize the remaining values:
    // * m_pMode
    // * m_nScreenWidth
    // * m_nScreenHeight
    // * m_pLAW
    // * m_p2DVideoMemory
    // * m_pPrimanrySurface
    // * m_ulBitMasks
    // And don't forget to initialize the palette parameter pPaletteHandle.
    // initialize remaining values
    //
    m_pMode = &m_gpeModeTable[nModeNum];    // current mode
    m_nScreenWidth = m_pMode->width;    // current display width
    m_nScreenHeight = m_pMode->height;  // current display height

    // generate pointer to linear access window (can't address physical 
    // memory directly, but need to create and map a pointer)
    m_pLAW = (unsigned char *) VirtualAlloc(NULL, m_nLAWSize, 
                                               MEM_RESERVE, PAGE_NOACCESS);
    BOOL bCreateLAW;
    if (m_nLAWPhysical<0x20000000) { // handle <512MB address differently
        bCreateLAW = VirtualCopy(m_pLAW, (LPVOID) (m_nLAWPhysical|0x80000000), m_nLAWSize,
            PAGE_READWRITE|PAGE_NOCACHE);
    }
    else {
        bCreateLAW = VirtualCopy(m_pLAW, 
                      (LPVOID) (m_nLAWPhysical>>8), m_nLAWSize,
                        PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);
    }
    // check for error creating LAW pointer
    if (!bCreateLAW) {
        return E_FAIL;
    }
    // create Node2D instance (for managing video memory)
    m_p2DVideoMemory  = new Node2D(m_nScreenWidth, 
                         m_nVideoMemorySize/m_nScreenStride,
                           0, 0, 32/m_pMode->Bpp);
    // check for error creating Node2D instance
    if (m_p2DVideoMemory==NULL) {
        return E_FAIL;
    }
    // create primary surface
    SCODE scAlloc = AllocSurface( &m_pPrimarySurface, m_nScreenWidth,
               m_nScreenHeight, m_pMode->format, GPE_REQUIRE_VIDEO_MEMORY);
    // check for error allocating primary surface
    if (scAlloc!=S_OK) {
        return E_FAIL;
    }
    // initialize bitmasks based on current bits per pixel
    memset(&m_ulBitMasks[0], 0, sizeof(m_ulBitMasks));
    if (m_pMode->Bpp==16) {
        // 16Bpp has two mask options
        if (m_b555Mode) {   // 5-5-5 mode
            m_ulBitMasks[0] = 0x7c00; // red
            m_ulBitMasks[1] = 0x03e0; // green
            m_ulBitMasks[2] = 0x001f; // blue
        }
        else {              // 5-6-5 mode
            m_ulBitMasks[0] = 0xf800; // red
            m_ulBitMasks[1] = 0x07e0; // green
            m_ulBitMasks[2] = 0x001f; // blue
        }
    }
    // create a default palette, if necessary
    if (pPaletteHandle!=NULL) {
        // create palette
        switch (m_pMode->Bpp) {
        case 24:
        case 32:
            *pPaletteHandle = EngCreatePalette (PAL_BGR, 0, NULL, 0, 0, 0);
            break;
        case 16:
            *pPaletteHandle = EngCreatePalette(PAL_BITFIELDS, 0, NULL, 
                         m_ulBitMasks[0], m_ulBitMasks[1], m_ulBitMasks[2]);
            break;
        case 8:
            *pPaletteHandle = EngCreatePalette(PAL_INDEXED,256,
                                        (ULONG *)m_rgbIdentityPal,0,0,0);
            SetPalette(m_rgbIdentityPal, 0, 256); 
                                        // palette needs to be set here
            break;
        case 4:
            *pPaletteHandle = EngCreatePalette(PAL_INDEXED,16,
                                        (ULONG *)m_rgbIdentityPal16,0,0,0);
            SetPalette(m_rgbIdentityPal16, 0, 16); // palette needs to be set
            break;
        default:
            RETAILMSG(1,(TEXT("NewGPE::SetMode Failed to create 
                                               unknown palette type.\r\n")));
            break;
        }
    }
    return S_OK;
}


Listing Four
NewGPESurf::NewGPESurf( int width, int height, void *pBits, int stride,
                EGPEFormat format, int offset, Node2D *pNode)
{
    // call general GPESurf initialization
    Init(width, height, pBits, stride, format);
    m_pNode2D = pNode;
    if (pNode!=NULL) {
        // surface in video memory, set flags and parameters
        m_fInVideoMemory = TRUE;
    }
}
NewGPESurf::~NewGPESurf()
{
    // free video memory if applicable
    if (m_fInVideoMemory && (m_pNode2D!=NULL)) {
        delete m_pNode2D;
    }
}

Listing Five
SCODE NewGPE::AllocSurface( GPESurf **ppSurf, int width, int height,
                EGPEFormat format, int surfaceFlags )
{
    SCODE scRet = S_OK;
    *ppSurf = NULL;

    // check parameters are valid:
    //   video memory surface must have same pixel format as display
    if (surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY) {
        // video memory surface must have same pixel format as display
        if (format!=m_pMode->format)
           return E_INVALIDARG;
    }
    // check if video memory surface requested
    if ((surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY) ||
        ((surfaceFlags & GPE_PREFER_VIDEO_MEMORY) &&
        (format==m_pMode->format))) {
        // try allocating out of video memory
        Node2D *pNode = m_p2DVideoMemory->Alloc(width, height);
        if (pNode!=NULL) {
            // get offset into video memory
            DWORD dwOffset = (m_nScreenStride * pNode->Top()) +
                ((pNode->Left() * EGPEFormatToBpp[format]) / 8);
            // now create a surface for the allocated video memory
            *ppSurf = new NewGPESurf( width, height, m_pLAW + dwOffset,
                m_nScreenStride, format, dwOffset, pNode);
            if (*ppSurf!=NULL) {
                // video memory surface allocated successfully
                return S_OK;
            }
            // failed creating surface for video memory
            delete pNode; // free video memory
        }

        // if we got here then unable to allocate out of video memory
        if (surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY)
            return E_OUTOFMEMORY; // fail since surface requires video memory
    }
    // Allocate surface from system memory if not in video memory
    *ppSurf = new GPESurf(width, height, format);
    if (*ppSurf!=NULL) {
        // make sure system memory allocated
        if ((*ppSurf)->Buffer()!=NULL) {
            // system memory surface allocated successfully
            return S_OK;
        }
        // system memory surface allocation failed
        delete *ppSurf;
        // just fall through to fail
    }
    // if we got here then something failed
    return E_OUTOFMEMORY;
}

Listing Six
SCODE NewGPE::BltPrepare( GPEBltParms *pBltParms )
{
    // use the default blit function
   pBltParms->pBlt = EmulatedBlt;
    return S_OK;
}
SCODE NewGPE::BltComplete( GPEBltParms *pBltParms )
{
    // don't need to do anything here
    return S_OK;
}


Listing Seven
SCODE NewGPE::Line(GPELineParms *pLineParms,EGPEPhase phase /* = gpeSingle */)
{
    // use the default line function
    pLineParms->pLine = EmulatedLine;
    return S_OK;
}





6


