#include "precomp.h" // // PM.CPP // Palette Manager // // Copyright(c) Microsoft 1997- // #define MLZ_FILE_ZONE ZONE_CORE // // // PALETTE MANAGER (PM) OVERVIEW // // Palette Manager is responsible for sending palette packets. A palette // packet: // // (1) indicates the colors being used on the host machine - and therefore // specifies which colors the remote machine should use if it can choose // (e.g. by selecting and realizing the given colors into the display // hardware). [A palette packet may not contain the exact colors being // used on the host if the protocol bpp is different from the host bpp]. // // (2) specifies the colors which correspond to the values in bitmap // (screen) data i.e. the values in 4bpp and 8bpp bitmap data are indices // into the table of colors sent in the palette packet. // // // (1) affects order replay and (2) affects screen data replay, so a // correct palette packet must be sent (by calling // PM_MaybeSendPalettePacket) before a batch of updates are sent. // // Palette Manager also handles incoming palette packets from other parties // in the conference and creates corresponding local palettes which the // Update Receiver can query and use when processing updates. // // When a new palette packet is sent (e.g. due to the System Palette // changing), all shared areas of the screen will be retransmitted in due // course. A receiving Palette Manager therefore does not have to (and // should not attempt to) convert any updates/bitmaps that have been // received prior to the arrival of the new palette packet. // // // // PM strategy when network packets cannot be allocated. // // PM_MaybeSendPalettePacket returns a boolean indicating whether it has // succesfully sent a palette packet. The USR will only send updates if // the corresponding palette packet is successfully sent. // // const COLORREF s_apmGreyRGB[PM_GREY_COUNT] = { PM_GREY1, PM_GREY2, PM_GREY3, PM_GREY4, PM_GREY5 }; // // PM_PartyLeftShare() // void ASShare::PM_PartyLeftShare(ASPerson * pasPerson) { DebugEntry(ASShare::PM_PartyLeftShare); ValidatePerson(pasPerson); // This should be cleared already! ASSERT(!pasPerson->pmcColorTable); ASSERT(!pasPerson->apmColorTable); ASSERT(!pasPerson->pmPalette); DebugExitVOID(ASShare::PM_PartyLeftShare); } // // PM_RecalcCaps() // // This calculates the PM hosting caps when // * we start to host // * we're hosting and somebody joins the share // * we're hosting and somebody leaves the share // // This can GO AWAY WHEN 2.x COMPAT IS GONE -- no more min() of cache size // void ASShare::PM_RecalcCaps(BOOL fJoiner) { ASPerson * pasT; DebugEntry(ASShare::PM_RecalcCaps); if (!m_pHost || !fJoiner) { // // Nothing to do if we're not hosting. And also, if somebody has // left, no recalculation -- 2.x didn't. // DC_QUIT; } ValidatePerson(m_pasLocal); // // NOTE: // The default size is 6 palettes cached. The result is going to be // <= that number. There's no point in recreating the cache, it's // so small. // m_pHost->m_pmNumTxCacheEntries = m_pasLocal->cpcCaps.palette.capsColorTableCacheSize; DC_EXIT_POINT: DebugExitVOID(ASShare::PM_Recalccaps); } // // PM_HostStarting() // // Called when we start to host; sets up color palette stuff and creates // outgoing palette cache // BOOL ASHost::PM_HostStarting(void) { BOOL rc = FALSE; TSHR_COLOR localPalColors[PM_NUM_8BPP_PAL_ENTRIES]; DebugEntry(ASHost::PM_HostStarting); // // Get palette caps. NOTE PM_RecalcCaps must be called AFTER // USR_RecalcCaps(), because that updates m_usrSendingBPP. // if (g_usrPalettized) { ASSERT(g_usrScreenBPP <= 8); ZeroMemory(localPalColors, sizeof(localPalColors)); // // Now create the Local Palette. // if (!m_pShare->PM_CreatePalette(COLORS_FOR_BPP(g_usrScreenBPP), localPalColors, &m_pmTxPalette)) { ERROR_OUT(( "Failed to create Local Palette")); DC_QUIT; } } else { m_pmTxPalette = (HPALETTE)GetStockObject(DEFAULT_PALETTE); PMGetGrays(); } // // With NM 3.0, why not just create a receive cache the size that // the host specifies in his caps? // // So I did that. For back compat, OUTGOING caches use the min size. // When we only have to be compatible with NM 3.0 and up, we won't // have to do this min stuff. // // Note similar code in CM, SSI, and SBC // // Figure out how many outgoing entries we can actually use m_pShare->PM_RecalcCaps(TRUE); // // Create the PM color table cache with a single eviction // category. // if (!CH_CreateCache(&m_pmTxCacheHandle, TSHR_PM_CACHE_ENTRIES, 1, 0, PMCacheCallback)) { ERROR_OUT(("Could not create PM cache")); DC_QUIT; } rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASHost::PM_HostStarting, rc); return(rc); } // // PM_HostEnded() // // We free resources created when we started to host // void ASHost::PM_HostEnded(void) { DebugEntry(ASHost::PM_HostEnded); if (m_pmTxPalette) { m_pShare->PM_DeletePalette(m_pmTxPalette); m_pmTxPalette = NULL; } if (m_pmTxCacheHandle) { CH_DestroyCache(m_pmTxCacheHandle); m_pmTxCacheHandle = 0; m_pmNumTxCacheEntries = 0; } DebugExitVOID(ASHost::PM_HostEnded); } // // PM_ViewStarting() // // For 3.0 nodes, we create the PM cache each time they start hosting // For 2.x nodes, we create the PM cache once and use it until they leave // the share. // BOOL ASShare::PM_ViewStarting(ASPerson * pasPerson) { BOOL rc = FALSE; DebugEntry(ASShare::PM_ViewStarting); ValidatePerson(pasPerson); // // In normal operation, we will receive a palette packet from the host // before any updates, which we use to create the correct palette for // this host. // // However, in some back-level calls we may not receive a palette // packet before the first updates, so we initialize this host's // palette to the default palette to allow us to generate some sort // of output. // pasPerson->pmPalette = (HPALETTE)GetStockObject(DEFAULT_PALETTE); // // Allocate color table cache memory based on the negotiated options // Space needed is (n)x256xRGBQUAD where n is the number of color // tables the conference supports. // pasPerson->pmcColorTable = pasPerson->cpcCaps.palette.capsColorTableCacheSize; if (!pasPerson->pmcColorTable) { WARNING_OUT(("PM_ViewStarting: person [%d] has no palette cache size", pasPerson->cpcCaps.palette.capsColorTableCacheSize)); rc = TRUE; DC_QUIT; } pasPerson->apmColorTable = new COLORTABLECACHE[pasPerson->pmcColorTable]; if (!pasPerson->apmColorTable) { ERROR_OUT(( "Failed to get memory for PM color table cache")); DC_QUIT; } ZeroMemory(pasPerson->apmColorTable, pasPerson->pmcColorTable * sizeof(COLORTABLECACHE)); rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASShare::PM_ViewStarting, rc); return(rc); } // // PM_ViewEnded() // void ASShare::PM_ViewEnded(ASPerson * pasPerson) { DebugEntry(ASShare::PM_ViewEnded); ValidatePerson(pasPerson); PMFreeIncoming(pasPerson); DebugExitVOID(ASShare::PM_PartyViewEnded); } // // PMFreeIncoming() // void ASShare::PMFreeIncoming(ASPerson * pasPerson) { DebugEntry(ASShare::PMFreeIncoming); // // Free the color table cache // pasPerson->pmcColorTable = 0; if (pasPerson->apmColorTable) { delete[] pasPerson->apmColorTable; pasPerson->apmColorTable = NULL; } if (pasPerson->pmPalette != NULL) { // // Free this host's palette. and set it to NULL so that we can tell // that this host has left the share. // PM_DeletePalette(pasPerson->pmPalette); pasPerson->pmPalette = NULL; } DebugExitVOID(ASShare::PMFreeIncoming); } // // PM_MaybeSendPalettePacket() // BOOL ASHost::PM_MaybeSendPalettePacket(void) { BOOL rc = TRUE; DebugEntry(ASHost::PM_MaybeSendPalettePacket); if (m_pmMustSendPalette) { ASSERT(m_usrSendingBPP <= 8); // // Ensure that our palette colors are up to date before we send the // palette packet. // if (g_usrPalettized) { PMUpdateSystemPaletteColors(); } PMUpdateTxPaletteColors(); } else if (g_usrPalettized) { ASSERT(m_usrSendingBPP <= 8); // // If the System Palette has changed then we may need to send // another palette packet. // if (PMUpdateSystemPaletteColors()) { // // The System Palette has changed, but we only need to send // another palette packet if the palette colors have changed. // TRACE_OUT(( "System Palette changed")); if (PMUpdateTxPaletteColors()) { TRACE_OUT(( "Tx Palette changed")); m_pmMustSendPalette = TRUE; } } } if (m_pmMustSendPalette) { ASSERT(m_usrSendingBPP <= 8); TRACE_OUT(( "Send palette packet")); rc = PMSendPalettePacket(m_apmTxPaletteColors, COLORS_FOR_BPP(m_usrSendingBPP)); if (rc) { m_pmMustSendPalette = FALSE; } } DebugExitBOOL(ASHost::PM_MaybeSendPalettePacket, rc); return(rc); } // // PM_ReceivedPacket // void ASShare::PM_ReceivedPacket ( ASPerson * pasPerson, PS20DATAPACKET pPacket ) { PPMPACKET pPMPacket; HPALETTE newPalette = NULL; DebugEntry(ASShare::PM_ReceivedPacket); ValidateView(pasPerson); pPMPacket = (PPMPACKET)pPacket; // // Create a new palette from the received packet. // // We cannot just update the current palette colors (using // SetPaletteEntries) because Windows does not handle the repainting // of other local Palette Manager apps correctly (it does not // broadcast the WM_PALETTE.. messages as the palette mapping does // not change). // if (PM_CreatePalette(pPMPacket->numColors, pPMPacket->aColors, &newPalette)) { PM_DeletePalette(pasPerson->pmPalette); pasPerson->pmPalette = newPalette; TRACE_OUT(( "Created new palette 0x%08x from packet", newPalette)); } else { WARNING_OUT(( "Failed to create palette. person(%u) numColors(%u)", pasPerson, pPMPacket->numColors)); } DebugExitVOID(ASShare::PM_ReceivedPacket); } // // PM_SyncOutgoing() // void ASHost::PM_SyncOutgoing(void) { DebugEntry(ASHost::PM_SyncOutgoing); // // Ensure we send a palette to the remote PM next time we are called. // if (m_usrSendingBPP <= 8) { m_pmMustSendPalette = TRUE; // // The sync discards any as-yet-unsent accumulated orders. Since these // orders may include color table cache orders, clear the cache. // ASSERT(m_pmTxCacheHandle); CH_ClearCache(m_pmTxCacheHandle); } DebugExitVOID(ASHost::PM_SyncOutgoing); } // // PM_CacheTxColorTable // BOOL ASHost::PM_CacheTxColorTable ( LPUINT pIndex, LPBOOL pCacheChanged, UINT cColors, LPTSHR_RGBQUAD pColors ) { BOOL rc = FALSE; UINT cacheIndex = 0; UINT i = 0; PCOLORTABLECACHE pEntry = NULL; COLORTABLECACHE newEntry = { 0 }; DebugEntry(ASHost::PM_CacheTxColorTable); ASSERT(m_usrSendingBPP <= 8); ASSERT(m_pmTxCacheHandle); TRACE_OUT(( "Caching table of %u colors", cColors)); // // Create the data we want to cache. It may be that there is already // an entry in the cache for this set of colors, but we still need to // create a cache entry in local memory so we can search the cache to // find out. // ZeroMemory(&newEntry, sizeof(COLORTABLECACHE)); newEntry.inUse = TRUE; newEntry.cColors = cColors; memcpy(&newEntry.colors, pColors, cColors * sizeof(TSHR_RGBQUAD)); // // Check to see if the table is already cached. (No hint or eviction // category.) // if (CH_SearchCache(m_pmTxCacheHandle, (LPBYTE)(&newEntry), sizeof(COLORTABLECACHE), 0, &cacheIndex )) { TRACE_OUT(( "Found existing entry at %u",cacheIndex)); *pIndex = cacheIndex; *pCacheChanged = FALSE; rc = TRUE; DC_QUIT; } // // Find a free cache entry // // We arrange that our transmit cache is always one greater than the // negotiated cache size so that we should never fail to find a free // array entry. Once we have fully populated our Tx cache we will // always find the free entry as the one last given back to us by CH. // Note the scan to <= m_pmNumTxCacheEntries is NOT a mistake. // if (m_pmNextTxCacheEntry != NULL) { pEntry = m_pmNextTxCacheEntry; m_pmNextTxCacheEntry = NULL; } else { for (i = 0; i <= m_pmNumTxCacheEntries; i++) { if (!m_apmTxCache[i].inUse) { break; } } // // We should never run out of free entries, but cope with it // if (i > m_pmNumTxCacheEntries) { ERROR_OUT(( "All PM cache entries in use")); rc = FALSE; DC_QUIT; } pEntry = m_apmTxCache + i; } // // Set up the color table in the free entry we just found // memcpy(pEntry, &newEntry, sizeof(COLORTABLECACHE)); // // Add the new entry to the cache // We do not use hints or eviction so set to 0 // cacheIndex = CH_CacheData(m_pmTxCacheHandle, (LPBYTE)pEntry, sizeof(COLORTABLECACHE), 0 ); TRACE_OUT(( "Color table 0x%08x cached at index %u", pEntry, cacheIndex)); *pIndex = cacheIndex; *pCacheChanged = TRUE; rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASHost::PM_CacheTxColorTable, rc); return(rc); } // // PM_CacheRxColorTable // BOOL ASShare::PM_CacheRxColorTable ( ASPerson * pasPerson, UINT index, UINT cColors, LPTSHR_RGBQUAD pColors ) { BOOL rc = FALSE; PCOLORTABLECACHE pColorTable; DebugEntry(ASShare::PM_CacheRxColorTable); ValidatePerson(pasPerson); pColorTable = pasPerson->apmColorTable; TRACE_OUT(( "Person [%d] color table rx cache 0x%08x cache %u, %u colors", pasPerson->mcsID, pColorTable, index, cColors)); if (pColorTable == NULL) { ERROR_OUT(( "Asked to cache when no cache allocated")); DC_QUIT; } // // The index must be within the currently negotiated cache limits // if (index > pasPerson->pmcColorTable) { ERROR_OUT(( "Invalid color table index %u",index)); DC_QUIT; } // // Set up the color table entry // pColorTable[index].inUse = TRUE; pColorTable[index].cColors = cColors; memcpy(pColorTable[index].colors, pColors, cColors * sizeof(TSHR_RGBQUAD)); rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASShare::PM_CacheRxColorTable, rc); return(rc); } // // PMSendPalettePacket // // DESCRIPTION: // // Sends a palette packet containing the given colors. // // PARAMETERS: // // pColorTable - pointer to an array of TSHR_RGBQUAD colors to be sent in the // palette packet. // // numColors - the number of entries in the TSHR_RGBQUAD array // // RETURNS: TRUE if the palette packet is sent, FALSE otherwise // // BOOL ASHost::PMSendPalettePacket ( LPTSHR_RGBQUAD pColorTable, UINT numColors ) { PPMPACKET pPMPacket; UINT sizePkt; UINT i; BOOL rc = FALSE; #ifdef _DEBUG UINT sentSize; #endif // _DEBUG DebugEntry(ASHost::PMSendPalettePacket); // // Send a palette packet. // // First calculate the packet size. // sizePkt = sizeof(PMPACKET) + (numColors - 1) * sizeof(TSHR_COLOR); pPMPacket = (PPMPACKET)m_pShare->SC_AllocPkt(PROT_STR_UPDATES, g_s20BroadcastID, sizePkt); if (!pPMPacket) { WARNING_OUT(("Failed to alloc PM packet, size %u", sizePkt)); DC_QUIT; } // // Fill in the packet contents. // pPMPacket->header.header.data.dataType = DT_UP; pPMPacket->header.updateType = UPD_PALETTE; // // Convert the TSHR_RGBQUADs in the color table to TSHR_COLORs as we copy // them into the packet. // pPMPacket->numColors = numColors; for (i = 0; i < numColors; i++) { // // Convert each RGBQuad entry in the color table to a DCColor. // TSHR_RGBQUAD_TO_TSHR_COLOR(pColorTable[i], pPMPacket->aColors[i]); } // // Now send the packet to the remote application. // if (m_pShare->m_scfViewSelf) m_pShare->PM_ReceivedPacket(m_pShare->m_pasLocal, &(pPMPacket->header.header)); #ifdef _DEBUG sentSize = #endif // _DEBUG m_pShare->DCS_CompressAndSendPacket(PROT_STR_UPDATES, g_s20BroadcastID, &(pPMPacket->header.header), sizePkt); TRACE_OUT(("PM packet size: %08d, sent %08d", sizePkt, sentSize)); rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASHost::PMSendPalettePacket, rc); return(rc); } // // FUNCTION: PMCacheCallback // // DESCRIPTION: // // Cursor Manager's Cache Manager callback function. Called whenever an // entry is removed from the cache to allow us to free up the object. // // PARAMETERS: // // hCache - cache handle // // event - the cache event that has occured // // iCacheEntry - index of the cache entry that the event is affecting // // pData - pointer to the cache data associated with the given cache entry // // cbDataSize - size in bytes of the cached data // // RETURNS: Nothing // // void PMCacheCallback ( ASHost * pHost, PCHCACHE pCache, UINT iCacheEntry, LPBYTE pData ) { DebugEntry(PMCacheCallback); // // Release the cache entry for reuse // TRACE_OUT(( "Releasing cache entry %d at 0x%08x", iCacheEntry, pData)); pHost->m_pmNextTxCacheEntry = (PCOLORTABLECACHE)pData; pHost->m_pmNextTxCacheEntry->inUse = FALSE; // // Let SBC know that the cache entry has been released // pHost->SBC_PMCacheEntryRemoved(iCacheEntry); DebugExitVOID(PMCacheCallback); } // // PM_GetSystemPaletteEntries // void ASHost::PM_GetSystemPaletteEntries(LPTSHR_RGBQUAD pColors) { UINT i; DebugEntry(ASHost::PM_GetSystemPaletteEntries); PMUpdateSystemPaletteColors(); for (i = 0; i < PM_NUM_8BPP_PAL_ENTRIES; i++) { pColors[i].rgbRed = m_apmCurrentSystemPaletteEntries[i].peRed; pColors[i].rgbGreen = m_apmCurrentSystemPaletteEntries[i].peGreen; pColors[i].rgbBlue = m_apmCurrentSystemPaletteEntries[i].peBlue; pColors[i].rgbReserved = 0; } // // This function in its current form always returns TRUE - it is always // able to obtain the system colors. // DebugExitVOID(ASHost::PM_GetSystemPaletteEntries); } // // PM_GetLocalPalette() // HPALETTE ASHost::PM_GetLocalPalette(void) { // // Ensure the palette is up to date // if (g_usrPalettized) { PMUpdateSystemPaletteColors(); } // // Return the handle to the Local Palette. // return(m_pmTxPalette); } // // PM_GetColorTable // void ASShare::PM_GetColorTable ( ASPerson * pasPerson, UINT index, LPUINT pcColors, LPTSHR_RGBQUAD pColors ) { PCOLORTABLECACHE pColorTable; DebugEntry(ASShare::PM_GetColorTable); ValidatePerson(pasPerson); ASSERT(pasPerson->apmColorTable); pColorTable = &(pasPerson->apmColorTable[index]); TRACE_OUT(( "Color table requested for [%d], table ptr 0x%08x index %d", pasPerson->mcsID, pColorTable,index)); if (!pColorTable->inUse) { ERROR_OUT(( "Asked for PM cache entry %hu when cache not yet in use", index)); DC_QUIT; } // // Copy the colors into the structure we have been passed // *pcColors = pColorTable->cColors; memcpy( pColors, pColorTable->colors, sizeof(TSHR_RGBQUAD) * pColorTable->cColors ); TRACE_OUT(( "Returning %u colors",*pcColors)); DC_EXIT_POINT: DebugExitVOID(ASShare::PM_GetColorTable); } // // PMADJUSTBUGGEDCOLOR() // // Macro used to tweak an 8 bit palette entry that the Win95 16 bit // driver returns incorrectly // #define PMADJUSTBUGGEDCOLOR(pColor) \ if ( ((pColor)->rgbBlue != 0x00) && \ ((pColor)->rgbBlue != 0xFF) ) \ { \ (pColor)->rgbBlue += 0x40; \ } \ \ if ( ((pColor)->rgbGreen != 0x00) && \ ((pColor)->rgbGreen != 0xFF) ) \ { \ (pColor)->rgbGreen += 0x20; \ } \ \ if ( ((pColor)->rgbRed != 0x00) && \ ((pColor)->rgbRed != 0xFF) ) \ { \ (pColor)->rgbRed += 0x20; \ } // // PMGetGrays() // // Gets display driver specific versions of gray RGBs // void ASHost::PMGetGrays(void) { HBITMAP hOldBitmap = NULL; BITMAPINFO_ours bitmapInfo; BYTE bitmapBuffer[16]; UINT i; DebugEntry(ASHost::PMGetGrays); // // Initialise the bitmapinfo local structure header contents. This // structure will be used in the GetDIBits calls. // m_pShare->USR_InitDIBitmapHeader((BITMAPINFOHEADER *)&bitmapInfo, 8); bitmapInfo.bmiHeader.biWidth = 16; bitmapInfo.bmiHeader.biHeight = 1; // // Select the bitmap into the work DC // hOldBitmap = SelectBitmap(m_usrWorkDC, m_pShare->m_usrBmp16); if (hOldBitmap == NULL) { ERROR_OUT(( "Failed to select bitmap. hp(%08lX) hbmp(%08lX)", m_usrWorkDC, m_pShare->m_usrBmp16 )); DC_QUIT; } // // Use the real GDI to set each bit to each supplied color. // for (i = PM_GREY_COUNT; i-- != 0; ) { SetPixel(m_usrWorkDC, i, 0, s_apmGreyRGB[i]); } // // Because this function is only used for true color scenarios we do // not need to select a palette into our compatible DC. We just need // to get the bits. // if (!GetDIBits(m_usrWorkDC, m_pShare->m_usrBmp16, 0, 1, &bitmapBuffer, (BITMAPINFO *)&bitmapInfo, DIB_RGB_COLORS )) { ERROR_OUT(( "GetDIBits failed. hp(%x) hbmp(%x)", m_usrWorkDC, m_pShare->m_usrBmp16)); DC_QUIT; } // // Check if we need to adjust the palette colors for the 16 bit driver // bug. // m_pmBuggedDriver = ((g_usrScreenBPP > 8) && (bitmapInfo.bmiColors[1].rgbRed == 0) && (bitmapInfo.bmiColors[1].rgbGreen == 0) && (bitmapInfo.bmiColors[1].rgbBlue == 0x40)); // // Extract the RGBs returned by the display driver with the sending bpp // DIB. // for (i = PM_GREY_COUNT; i-- != 0; ) { // // Extract the RGB from the color table // m_apmDDGreyRGB[i] = *((LPTSHR_RGBQUAD)(&bitmapInfo.bmiColors[bitmapBuffer[i]])); // // Adjust the palette colors for the 16 bit driver bug, if needed. // if (m_pmBuggedDriver) { TRACE_OUT(( "Adjusting for bugged driver")); PMADJUSTBUGGEDCOLOR(&m_apmDDGreyRGB[i]); } } DC_EXIT_POINT: // // clean up // if (hOldBitmap != NULL) { SelectBitmap(m_usrWorkDC, hOldBitmap); } DebugExitVOID(ASHost::PMGetGrays); } // // FUNCTION: PMUpdateSystemPaletteColors // // DESCRIPTION: // // Determines whether the colors in the System Palette have changed since // the last time this function was called and if so, updates the supplied // palette so that it contains the same colors as the System Palette. // // The first time that this function is called after PM_Init the System // Palette colors will be returned and the function will return TRUE. // // PARAMETERS: // // shadowSystemPalette - handle of the palette to be updated with the // current System Palette colors // // RETURNS: TRUE if the System Palette has changed since the last call, // FALSE otherwise. // // BOOL ASHost::PMUpdateSystemPaletteColors(void) { BOOL rc = FALSE; PALETTEENTRY systemPaletteEntries[PM_NUM_8BPP_PAL_ENTRIES]; HDC hdcScreen = NULL; UINT cbSystemPaletteEntries; int irgb, crgb, crgbFixed; DebugEntry(ASHost::PMUpdateSystemPaletteColors); ASSERT(g_usrPalettized); ASSERT(g_usrScreenBPP <= 8); ASSERT(m_usrSendingBPP <= 8); // // Don't bother with all this stuff if the system palette has not // changed at all. We track notifications to our UI to detect // palette changes. // if (!g_asSharedMemory->pmPaletteChanged) { DC_QUIT; } hdcScreen = GetDC(NULL); if (!hdcScreen) { WARNING_OUT(( "GetDC failed")); DC_QUIT; } if (GetSystemPaletteEntries(hdcScreen, 0, COLORS_FOR_BPP(g_usrScreenBPP), systemPaletteEntries) != (UINT)COLORS_FOR_BPP(g_usrScreenBPP)) { WARNING_OUT(( "GetSystemPaletteEntries failed")); DC_QUIT; } // // Now that we have succesfully queried the system palette, we can // reset our flag. // g_asSharedMemory->pmPaletteChanged = FALSE; cbSystemPaletteEntries = COLORS_FOR_BPP(g_usrScreenBPP) * sizeof(PALETTEENTRY); // // See if the System Palette has changed from the last time we queried. // if (!memcmp(systemPaletteEntries, m_apmCurrentSystemPaletteEntries, cbSystemPaletteEntries )) { // // The System Palette has not changed // TRACE_OUT(( "System palette has NOT changed")); rc = TRUE; DC_QUIT; } // // Take a copy of the new System Palette. // memcpy(m_apmCurrentSystemPaletteEntries, systemPaletteEntries, cbSystemPaletteEntries ); // // Update the current local paleete. // // NOTE FOR WIN95: // We need to add PC_NOCOLLAPSE to non-system palette entries. // if (g_asWin95) { if (GetSystemPaletteUse(hdcScreen) == SYSPAL_STATIC) crgbFixed = GetDeviceCaps(hdcScreen, NUMRESERVED) / 2; else crgbFixed = 1; crgb = COLORS_FOR_BPP(g_usrScreenBPP) - crgbFixed; for (irgb = crgbFixed; irgb < crgb; irgb++) { systemPaletteEntries[irgb].peFlags = PC_NOCOLLAPSE; } } SetPaletteEntries(m_pmTxPalette, 0, COLORS_FOR_BPP(g_usrScreenBPP), systemPaletteEntries ); m_pmMustSendPalette = TRUE; // // SFR0407: The system palette has changed so re-fetch our set of RGBs // which the driver returns on an 8-bit GetDIBits for greys. // PMGetGrays(); rc = TRUE; DC_EXIT_POINT: if (hdcScreen) { ReleaseDC(NULL, hdcScreen); } DebugExitBOOL(ASHost::PMUpdateSystemPaletteColors, rc); return(rc); } // // FUNCTION: PMUpdateTxPaletteColors // // DESCRIPTION: // // Returns the colors that make up the current Tx Palette (the palette that // is SENT from the local machine). These are not necessarily the colors // in the local machine's palette, because the local machine's bpp and the // protocol bpp may be different (e.g. on an 8bpp machine talking at 4bpp // the Tx Palette has 16 entries). // // PARAMETERS: // // pColorTable - pointer to an array of RGBQUADs which is filled with the // colors that make up the current Tx Palette. // // RETURNS: TRUE if successful, FALSE otherwise. // // BOOL ASHost::PMUpdateTxPaletteColors(void) { UINT i; UINT j; BOOL rc = FALSE; HDC hdcMem = NULL; HBITMAP hbmpDummy = NULL; HPALETTE hpalOld = NULL; BITMAPINFO_ours pmBitmapInfo; DebugEntry(ASHost::PMUpdateTxPaletteColors); // // Returns the values returned by a GetDIBits call with the // m_pmTxPalette selected. // ASSERT(m_usrSendingBPP <= 8); // // If we are at 8bpp locally, and sending at 8bpp, then the TxPalette // is simply the system palette. // if ((g_usrScreenBPP == 8) && (m_usrSendingBPP == 8)) { PM_GetSystemPaletteEntries(pmBitmapInfo.bmiColors); } else { hdcMem = CreateCompatibleDC(NULL); if (!hdcMem) { ERROR_OUT(("PMUpdateTxPaletteColors: couldn't create memory DC")); DC_QUIT; } hpalOld = SelectPalette(hdcMem, m_pmTxPalette, TRUE); RealizePalette(hdcMem); #define DUMMY_WIDTH 8 #define DUMMY_HEIGHT 8 hbmpDummy = CreateBitmap(DUMMY_WIDTH, DUMMY_HEIGHT, 1, g_usrScreenBPP, NULL); if (hbmpDummy == NULL) { ERROR_OUT(( "Failed to create bitmap")); DC_QUIT; } // // Set up the structure required by GetDIBits. // pmBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pmBitmapInfo.bmiHeader.biWidth = DUMMY_WIDTH; pmBitmapInfo.bmiHeader.biHeight = DUMMY_HEIGHT; pmBitmapInfo.bmiHeader.biPlanes = 1; pmBitmapInfo.bmiHeader.biBitCount = (WORD)m_usrSendingBPP; pmBitmapInfo.bmiHeader.biCompression = BI_RGB; pmBitmapInfo.bmiHeader.biSizeImage = 0; pmBitmapInfo.bmiHeader.biXPelsPerMeter = 10000; pmBitmapInfo.bmiHeader.biYPelsPerMeter = 10000; pmBitmapInfo.bmiHeader.biClrUsed = 0; pmBitmapInfo.bmiHeader.biClrImportant = 0; if (0 == GetDIBits( hdcMem, hbmpDummy, 0, DUMMY_HEIGHT, NULL, (LPBITMAPINFO)&pmBitmapInfo.bmiHeader, DIB_RGB_COLORS )) { WARNING_OUT(( "GetDIBits failed hdc(%x) hbmp(%x)", HandleToUlong(hdcMem), HandleToUlong(hbmpDummy))); DC_QUIT; } SelectPalette(hdcMem, hpalOld, TRUE); PM_AdjustColorsForBuggedDisplayDrivers( (LPTSHR_RGBQUAD)pmBitmapInfo.bmiColors, COLORS_FOR_BPP(m_usrSendingBPP)); // // This doesn't work for VGA. // if (g_usrScreenBPP > 4) { // // Check the new color table for any occurrences of the dodgy-grey // RGBs which the display driver returns (getDIBits at 8bpp can // return RGBs with unequal R, G and B for a supplied RGB with // equal components, causing poor quality output). // for (i = COLORS_FOR_BPP(m_usrSendingBPP); i-- != 0;) { for ( j = 0; j < PM_GREY_COUNT; j++ ) { if (!memcmp(&pmBitmapInfo.bmiColors[i], &m_apmDDGreyRGB[j], sizeof(pmBitmapInfo.bmiColors[i])) ) { // // Found a dodgy grey in the color table, so replace // with a "good" grey, ie one with equal R, G and B. // pmBitmapInfo.bmiColors[i].rgbRed = GetRValue(s_apmGreyRGB[j]); pmBitmapInfo.bmiColors[i].rgbGreen = GetGValue(s_apmGreyRGB[j]); pmBitmapInfo.bmiColors[i].rgbBlue = GetBValue(s_apmGreyRGB[j]); TRACE_OUT(( "match our grey %#x", s_apmGreyRGB[j])); break; } } } } } // // If the colors have changed then return TRUE and copy the new color // table back, else return FALSE. // if (!memcmp(m_apmTxPaletteColors, pmBitmapInfo.bmiColors, COLORS_FOR_BPP(m_usrSendingBPP) * sizeof(RGBQUAD) )) { rc = FALSE; } else { memcpy(m_apmTxPaletteColors, pmBitmapInfo.bmiColors, COLORS_FOR_BPP(m_usrSendingBPP) * sizeof(RGBQUAD) ); rc = TRUE; } DC_EXIT_POINT: if (hbmpDummy != NULL) { DeleteBitmap(hbmpDummy); } if (hdcMem != NULL) { DeleteDC(hdcMem); } DebugExitDWORD(ASHost::PMUpdateTxPaletteColors, rc); return(rc); } // // FUNCTION: PMCreatePalette // // DESCRIPTION: // // Creates a new palette using the given colors. // // PARAMETERS: // // cEntries - number of entries in the pNewEntries array // // pNewEntries - pointer to a TSHR_COLOR array containing the new palette // entries // // phPal - pointer to a HPALETTE variable that receives the new palette // handle. // // // RETURNS - TRUE if successful, FALSE otherwise. // // BOOL ASShare::PM_CreatePalette ( UINT cEntries, LPTSHR_COLOR pNewEntries, HPALETTE * phPal ) { UINT i; BYTE pmLogPaletteBuffer[sizeof(LOGPALETTE) + (PM_NUM_8BPP_PAL_ENTRIES-1)*sizeof(PALETTEENTRY)]; LPLOGPALETTE pLogPalette; BOOL rc = FALSE; DebugEntry(ASShare::PM_CreatePalette); ASSERT(cEntries <= PM_NUM_8BPP_PAL_ENTRIES); // // Set up a palette structure. // pLogPalette = (LPLOGPALETTE)pmLogPaletteBuffer; // This is a random windows constant pLogPalette->palVersion = 0x300; pLogPalette->palNumEntries = (WORD)cEntries; // // This palette packet contains an array of TSHR_COLOR structures which // contains 3 fields (RGB). We have to convert each of these // structures to a PALETTEENTRY structure which has the same 3 fields // (RGB) plus some flags. // for (i = 0; i < cEntries; i++) { TSHR_COLOR_TO_PALETTEENTRY( pNewEntries[i], pLogPalette->palPalEntry[i] ); } // // Create the palette. // *phPal = CreatePalette(pLogPalette); // // Return TRUE if the palette was created. // rc = (*phPal != NULL); DebugExitDWORD(ASShare::PM_CreatePalette, rc); return(rc); } // // FUNCTION: PM_AdjustColorsForBuggedDisplayDrivers // // DESCRIPTION: // // Adjusts the supplied color table if necessary to take account of display // driver bugs. // // PARAMETERS: // // pColors - pointer to the color table (an array of RGBQUADs) // // cColors - number of colors in the supplied color table // // RETURNS: Nothing. // // // NOTE: There is similar code in NormalizeRGB below (although not similar // enough to macro it.) If you change this code you should probably do // the same there.) // void ASHost::PM_AdjustColorsForBuggedDisplayDrivers ( LPTSHR_RGBQUAD pColors, UINT cColors ) { LPTSHR_RGBQUAD pColor; UINT i; DebugEntry(ASHost::PM_AdjustColorsForBuggedDisplayDrivers); // // The Win95 16bpp display drivers return wrong colors when querying at // 8bpp. The palette depends on the driver itself (5-6-5, 6-5-5, 5-6-5, // or 5-5-5). Only when R, G, and B have the same # of bits are we // going to end up with an even distribution. // // Detect this case and try to adjust the colors. // m_pmBuggedDriver = ((g_usrScreenBPP > 8) && (pColors[1].rgbRed == 0) && (pColors[1].rgbGreen == 0) && (pColors[1].rgbBlue == 0x40)); if (m_pmBuggedDriver) { TRACE_OUT(( "Adjusting for bugged driver")); pColor = pColors; for (i = 0; i < cColors; i++) { PMADJUSTBUGGEDCOLOR(pColor); pColor++; } } DebugExitVOID(ASHost::PM_AdjustColorsForBuggedDisplayDrivers); } // // FUNCTION: PM_DeletePalette // // DESCRIPTION: // // Deletes the given palette, if it is not the default palette. // // PARAMETERS: // // palette - palette to be deleted // // RETURNS: Nothing. // // void ASShare::PM_DeletePalette(HPALETTE palette) { DebugEntry(ASShare::PM_DeletePalette); if ((palette != NULL) && (palette != (HPALETTE)GetStockObject(DEFAULT_PALETTE))) { DeletePalette(palette); } DebugExitVOID(ASShare::PM_DeletePalette); }