#include "precomp.h" // // CM.CPP // Cursor Manager // // Copyright(c) Microsoft 1997- // #define MLZ_FILE_ZONE ZONE_CORE // // CM_ShareStarting() // Creates resources used by the share // BOOL ASShare::CM_ShareStarting(void) { BOOL rc = FALSE; HBITMAP hbmpT; ICONINFO cursorInfo; char szTmp[MAX_CURSOR_TAG_FONT_NAME_LENGTH]; DebugEntry(ASShare::CM_ShareStarting); // // Create the hatching brush we will use to make shadow cursors // distinguishable from real cursors. // hbmpT = LoadBitmap(g_asInstance, MAKEINTRESOURCE(IDB_HATCH32X32) ); m_cmHatchBrush = CreatePatternBrush(hbmpT); DeleteBitmap(hbmpT); if (!m_cmHatchBrush) { ERROR_OUT(("CM_ShareStarting: Failed to created hatched brush")); DC_QUIT; } m_cmArrowCursor = LoadCursor(NULL, IDC_ARROW); if (!m_cmArrowCursor) { ERROR_OUT(("CM_ShareStarting: Failed to load cursors")); DC_QUIT; } // Get the arrow hotspot GetIconInfo(m_cmArrowCursor, &cursorInfo); m_cmArrowCursorHotSpot.x = cursorInfo.xHotspot; m_cmArrowCursorHotSpot.y = cursorInfo.yHotspot; DeleteBitmap(cursorInfo.hbmMask); if (cursorInfo.hbmColor) DeleteBitmap(cursorInfo.hbmColor); // // Get the size of the cursor on this system. (Cursor bitmaps are word // padded 1bpp). // m_cmCursorWidth = GetSystemMetrics(SM_CXCURSOR); m_cmCursorHeight = GetSystemMetrics(SM_CYCURSOR); // // Load the name of the font which will be used for creating cursor // tags. It makes sense to have this in a resource, so it can be // localized. // LoadString(g_asInstance, IDS_FONT_CURSORTAG, szTmp, sizeof(szTmp)); m_cmCursorTagFont = CreateFont(CURSOR_TAG_FONT_HEIGHT, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, szTmp); if (!m_cmCursorTagFont) { ERROR_OUT(("CM_ShareStarting: couldn't create cursor tag font")); DC_QUIT; } rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASShare::CM_ShareStarting, rc); return(rc); } // // CM_ShareEnded() // Frees resources used by the share // void ASShare::CM_ShareEnded(void) { DebugEntry(ASShare::CM_ShareEnded); // // Free cursor tag font // if (m_cmCursorTagFont != NULL) { DeleteFont(m_cmCursorTagFont); m_cmCursorTagFont = NULL; } // // Free shadow cursor dither brush // if (m_cmHatchBrush != NULL) { DeleteBrush(m_cmHatchBrush); m_cmHatchBrush = NULL; } DebugExitVOID(ASShare::CM_ShareEnded); } // // CM_PartyJoiningShare() // BOOL ASShare::CM_PartyJoiningShare(ASPerson * pasPerson) { BOOL rc = FALSE; DebugEntry(ASShare::CM_PartyJoiningShare); ValidatePerson(pasPerson); // // For 2.x nodes, create cursor cache now // For 3.0 nodes, create it when they start to host // if (pasPerson->cpcCaps.general.version < CAPS_VERSION_30) { if (!CMCreateIncoming(pasPerson)) { ERROR_OUT(("CM_PartyJoiningShare: can't create cursor cache")); DC_QUIT; } } pasPerson->cmhRemoteCursor = m_cmArrowCursor; pasPerson->cmHotSpot = m_cmArrowCursorHotSpot; ASSERT(pasPerson->cmPos.x == 0); ASSERT(pasPerson->cmPos.y == 0); rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASShare::CM_PartyJoiningShare, rc); return(rc); } // // CM_PartyLeftShare() // // See cm.h for description. // void ASShare::CM_PartyLeftShare(ASPerson * pasPerson) { DebugEntry(ASShare::CM_PartyLeftShare); ValidatePerson(pasPerson); // // Clear the incoming (receive) cursor cache info // if (pasPerson->cpcCaps.general.version < CAPS_VERSION_30) { TRACE_OUT(("CM_PartyLeftShare: freeing 2.x cursor cache for [%d]", pasPerson->mcsID)); CMFreeIncoming(pasPerson); } else { ASSERT(!pasPerson->ccmRxCache); ASSERT(!pasPerson->acmRxCache); } DebugExitVOID(ASShare::CM_PartyLeftShare); } // // CM_HostStarting() // // Called when we start to host. Creates the outgoing cursor cache // BOOL ASHost::CM_HostStarting(void) { BOOL rc = FALSE; DebugEntry(ASHost::CM_HostStarting); // // Calculate actual size of cache we will use -- if 3.0 share, it's // what we advertise in our caps, but if 2.x share, it's <= to that // amount, being the min of everybody in the share. // // We however create the cache the size we want, knowing that in a 2.x // share we'll use some subset of it. That's cool. // m_pShare->CM_RecalcCaps(TRUE); if (!CH_CreateCache(&m_cmTxCacheHandle, TSHR_CM_CACHE_ENTRIES, 1, 0, NULL)) { ERROR_OUT(("Could not create CM cache")); DC_QUIT; } rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASHost::CM_HostStarting, rc); return(rc); } // // CM_HostEnded() // // Called when we stop hosting, so we can free cursor data // void ASHost::CM_HostEnded(void) { DebugEntry(ASHost::CM_HostEnded); // // Destroy the outgoing cursor cache // if (m_cmTxCacheHandle) { CH_DestroyCache(m_cmTxCacheHandle); m_cmTxCacheHandle = 0; m_cmNumTxCacheEntries = 0; } DebugExitVOID(ASHost::CM_HostEnded); } // // CM_ViewStarting() // // Called when somebody we're viewing starts to host. We create // the incoming cursor cache (well, we create it if they are 3.0; 2.x // nodes populated it even when not hosting). // BOOL ASShare::CM_ViewStarting(ASPerson * pasPerson) { BOOL rc = FALSE; DebugEntry(ASShare::CM_ViewStarting); ValidatePerson(pasPerson); if (pasPerson->cpcCaps.general.version < CAPS_VERSION_30) { // Reuse created cache ASSERT(pasPerson->acmRxCache); TRACE_OUT(("CM_ViewStarting: reusing cursor cache for 2.x node [%d]", pasPerson->mcsID)); } else { if (!CMCreateIncoming(pasPerson)) { ERROR_OUT(("CM_ViewStarting: can't create cursor cache for [%d]", pasPerson->mcsID)); DC_QUIT; } } rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASShare::CM_ViewStarting, rc); return(rc); } // // CM_ViewEnded() // // Called when somebody we are viewing has stopped hosting. We free up // cursor data needed to handle what they send us (well, for 3.0 dudes we // do; for 2.x dudes we keep it as long as they are in a share). // void ASShare::CM_ViewEnded(ASPerson * pasPerson) { DebugEntry(ASShare::CM_ViewEnded); ValidatePerson(pasPerson); if (pasPerson->cpcCaps.general.version >= CAPS_VERSION_30) { // Free cursor cache CMFreeIncoming(pasPerson); } else { TRACE_OUT(("CM_ViewEnded: keeping cursor cache for 2.x node [%d]", pasPerson->mcsID)); } DebugExitVOID(ASShare::CM_ViewEnded); } // // CMCreateIncoming() // Creates cursor cache for person. // If 3.0 node, we create it when they start to host // If 2.x node, we create it when they join the share // BOOL ASShare::CMCreateIncoming(ASPerson * pasPerson) { BOOL rc = FALSE; DebugEntry(ASShare::CMCreateIncoming); if (!pasPerson->cpcCaps.cursor.capsCursorCacheSize) { // // This person has no cursor cache; don't create one. // WARNING_OUT(("CMCreateIncoming: person [%d] has no cursor cache size", pasPerson->mcsID)); rc = TRUE; DC_QUIT; } pasPerson->ccmRxCache = pasPerson->cpcCaps.cursor.capsCursorCacheSize; pasPerson->acmRxCache = new CACHEDCURSOR[pasPerson->ccmRxCache]; if (!pasPerson->acmRxCache) { ERROR_OUT(("CMCreateIncoming: can't create cursor cache for node [%d]", pasPerson->mcsID)); DC_QUIT; } ZeroMemory(pasPerson->acmRxCache, sizeof(CACHEDCURSOR) * pasPerson->ccmRxCache); rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASShare::CMCreateIncoming, rc); return(rc); } // // CMFreeIncoming() // Frees cursor cache for person. // If 3.0 node, we free it when they stop hosting // If 2.x node, we free it when they leave the share // void ASShare::CMFreeIncoming(ASPerson * pasPerson) { UINT irx; POINT cursorPos; HWND hwnd; HCURSOR hCurCursor; DebugEntry(ASShare::CMFreeIncoming); hCurCursor = ::GetCursor(); if (pasPerson->acmRxCache) { for (irx = 0; irx < pasPerson->ccmRxCache; irx++) { if (pasPerson->acmRxCache[irx].hCursor != NULL) { if (pasPerson->acmRxCache[irx].hCursor == hCurCursor) { // // We're about to destroy the current cursor. Reset it. // Note that this can only happen when there's an active // frame for this host. And that frame must be about // to go away, in which case USER will jiggle the cursor // anyway. So we don't need to do more than this. // ::SetCursor(m_cmArrowCursor); } if (pasPerson->acmRxCache[irx].hCursor == pasPerson->cmhRemoteCursor) { pasPerson->cmhRemoteCursor = NULL; } ::DestroyCursor(pasPerson->acmRxCache[irx].hCursor); pasPerson->acmRxCache[irx].hCursor = NULL; } } pasPerson->ccmRxCache = 0; delete[] pasPerson->acmRxCache; pasPerson->acmRxCache = NULL; } DebugExitVOID(ASShare::CMFreeIncoming); } // // CM_Periodic() // void ASHost::CM_Periodic(void) { HWND hwnd; DebugEntry(ASHost::CM_Periodic); CM_MaybeSendCursorMovedPacket(); // // Find out which window is currently controlling the cursor // appearance. // hwnd = CMGetControllingWindow(); if (hwnd) { UINT cursorType; CURSORDESCRIPTION desiredCursor; UINT idDelta; // // Send a cursor shape update for the controlling window if necessary // if (m_pShare->HET_WindowIsHosted(hwnd)) cursorType = CM_CT_DISPLAYEDCURSOR; else cursorType = CM_CT_DEFAULTCURSOR; switch (cursorType) { case CM_CT_DEFAULTCURSOR: if ((m_cmLastCursorShape.type == CM_CD_SYSTEMCURSOR) && (m_cmLastCursorShape.id == CM_IDC_ARROW) ) { // // No change. // DC_QUIT; } desiredCursor.type = CM_CD_SYSTEMCURSOR; desiredCursor.id = CM_IDC_ARROW; break; case CM_CT_DISPLAYEDCURSOR: CMGetCurrentCursor(&desiredCursor); if (desiredCursor.type == m_cmLastCursorShape.type) { switch (desiredCursor.type) { case CM_CD_SYSTEMCURSOR: if (desiredCursor.id == m_cmLastCursorShape.id) { // // Same cursor as last time. // DC_QUIT; } break; case CM_CD_BITMAPCURSOR: // // If the cursor has already been used, ignore it. // Check if stamp is less than or equal to the last // one - assume any sufficiently large difference // is due to overflow. // idDelta = (UINT) (desiredCursor.id - m_cmLastCursorShape.id); if (((idDelta == 0) || (idDelta > 0x10000000)) && ((g_asSharedMemory->cmCursorHidden != FALSE) == (m_cmfCursorHidden != FALSE))) { TRACE_OUT(( "No change in cursor")); DC_QUIT; } break; default: ERROR_OUT(("Invalid cursor definition")); break; } } break; default: ERROR_OUT(("cursorType invalid")); DC_QUIT; } if (desiredCursor.type == CM_CD_SYSTEMCURSOR) { if (!CMSendSystemCursor(desiredCursor.id)) { // // We failed to send the system cursor, so we just exit without // updating m_cmLastCursorShape. We will attempt to send it again // on the next call to CM_Periodic. // DC_QUIT; } m_cmLastCursorShape.type = desiredCursor.type; m_cmLastCursorShape.id = desiredCursor.id; } else { // // Save the 'hidden' state. // m_cmfCursorHidden = (g_asSharedMemory->cmCursorHidden != FALSE); if (!CMSendBitmapCursor()) { // // We failed to send the bitmap cursor, so we just exit without // updating m_cmLastCursorShape. We will attempt to send it again // on the next call to CM_Periodic. // DC_QUIT; } m_cmLastCursorShape.type = desiredCursor.type; m_cmLastCursorShape.id = desiredCursor.id; } } DC_EXIT_POINT: DebugExitVOID(ASHost::CM_Periodic); } // // CM_SyncOutgoing() // Forces a send of the current cursor shape/pos when we start to host or // somebody new joins the conference // void ASHost::CM_SyncOutgoing(void) { DebugEntry(ASHost::CM_SyncOutgoing); // // Mark the last cursor as unknown. On next timer tick we'll send the // current one. // m_cmLastCursorShape.type = CM_CD_UNKNOWN; m_cmLastCursorPos.x = -1; m_cmLastCursorPos.y = -1; // // Clear the cursor cache. // if (m_cmTxCacheHandle != 0) { CH_ClearCache(m_cmTxCacheHandle); } DebugExitVOID(ASHost::CM_SyncOutgoing); } // // CM_DrawShadowCursor(..) // void ASShare::CM_DrawShadowCursor(ASPerson * pasHost, HDC hdc) { HBRUSH hbrOld; HDC hdcMem; HBITMAP hbmp; HBITMAP hbmpOld; HPALETTE hpalScreen = NULL; HPALETTE hpalOldDIB = NULL; POINT ptFrame; DebugEntry(ASShare::CM_DrawShadowCursor); ValidateView(pasHost); // // Draw the shadow cursor if there is one. // if (pasHost->cmShadowOff || !pasHost->cmhRemoteCursor) { TRACE_OUT(("CM_DrawShadowCursor: no cursor to draw")); DC_QUIT; } // // The cursor position is always kept in the host's screen coordinates. // When we paint our view frame, we adjust the DC so that painting // in host coordinates works right, even though the view frame may // be scrolled over. // ptFrame.x = pasHost->cmPos.x - pasHost->cmHotSpot.x - pasHost->m_pView->m_viewPos.x; ptFrame.y = pasHost->cmPos.y - pasHost->cmHotSpot.y - pasHost->m_pView->m_viewPos.y; // // We draw a greyed cursor using the following steps. // - copy the destination window rectangle to a memory bitmap. // - draw the cursor into the memory bitmap // // [the memory bitmap now contains the window background + a non-greyed // cursor] // // - blt the window bitmap back to the memory using a 3-way ROP and a // hatched pattern bitmap. The ROP is chosen such that the 0s and 1s // in the pattern bitmap select either a bitmap pel or a destination // pel for the final result. The pattern bitmap is such that most // of the bitmap pels are copied, but a few destination pels are // left unchanged, giving a greying effect. // // - copy the resulting bitmap back into the window. // // The last two steps are done so that the cursor does not appear to // change shape as it is moved. If the 3 way blt is done back to the // screen at stage 3, the pattern stays relative to the screen coords // and hence as the cursor moves, it will lose different pels each // time and appear to deform. // // The ROP is calculated to copy the source pel where the pattern is 1 // and to leave the destination pel unchanged where the pattern is 0: // // P S D R // // 0 0 0 0 // 0 0 1 1 // 0 1 0 0 // 0 1 1 1 // 1 0 0 0 // 1 0 1 0 // 1 1 0 1 // 1 1 1 1 // // ^ // Read upwards -> 0xCA // // From the table in the SDK, this gives a full ROP value of 0x00CA0749 // // #define GREY_ROP 0x00CA0749 if (NULL == (hdcMem = CreateCompatibleDC(hdc))) { WARNING_OUT(( "Failed to create memory DC")); DC_QUIT; } if (NULL == (hbmp = CreateCompatibleBitmap(hdc, CM_MAX_CURSOR_WIDTH, CM_MAX_CURSOR_HEIGHT))) { WARNING_OUT(( "Failed to create bitmap")); DeleteDC(hdcMem); DC_QUIT; } if (NULL == (hbmpOld = SelectBitmap(hdcMem, hbmp))) { WARNING_OUT(( "Failed to select bitmap")); DeleteBitmap(hbmp); DeleteDC(hdcMem); DC_QUIT; } hbrOld = SelectBrush(hdcMem, m_cmHatchBrush); // // // We need to make sure that we have the same logical palette selected // into both DCs otherwise we will corrupt the background color info // when we do the blitting. // // hpalScreen = SelectPalette(hdc, (HPALETTE)GetStockObject(DEFAULT_PALETTE), FALSE ); SelectPalette( hdc, hpalScreen, FALSE ); hpalOldDIB = SelectPalette( hdcMem, hpalScreen, FALSE ); RealizePalette(hdcMem); BitBlt( hdcMem, 0, 0, CM_MAX_CURSOR_WIDTH, CM_MAX_CURSOR_HEIGHT, hdc, ptFrame.x, ptFrame.y, SRCCOPY ); DrawIcon(hdcMem, 0, 0, pasHost->cmhRemoteCursor); CMDrawCursorTag(pasHost, hdcMem); BitBlt( hdcMem, 0, 0, CM_MAX_CURSOR_WIDTH, CM_MAX_CURSOR_HEIGHT, hdc, ptFrame.x, ptFrame.y, GREY_ROP ); BitBlt( hdc, ptFrame.x, ptFrame.y, CM_MAX_CURSOR_WIDTH, CM_MAX_CURSOR_HEIGHT, hdcMem, 0, 0, SRCCOPY ); SelectBrush(hdcMem, hbrOld); SelectBitmap(hdcMem, hbmpOld); DeleteBitmap(hbmp); if (hpalOldDIB != NULL) { SelectPalette(hdcMem, hpalOldDIB, FALSE); } DeleteDC(hdcMem); DC_EXIT_POINT: DebugExitVOID(ASShare::CM_DrawShadowCursor); } // // CM_ReceivedPacket(..) // void ASShare::CM_ReceivedPacket ( ASPerson * pasPerson, PS20DATAPACKET pPacket ) { PCMPACKETHEADER pCMPacket; DebugEntry(ASShare::CM_ReceivedPacket); ValidatePerson(pasPerson); pCMPacket = (PCMPACKETHEADER)pPacket; // // Switch on the packet type // switch (pCMPacket->type) { case CM_CURSOR_ID: case CM_CURSOR_MONO_BITMAP: case CM_CURSOR_COLOR_BITMAP: case CM_CURSOR_COLOR_CACHE: CMReceivedCursorShapePacket(pasPerson, pCMPacket); break; case CM_CURSOR_MOVE: CMReceivedCursorMovedPacket(pasPerson, pCMPacket); break; default: ERROR_OUT(("Invalid CM data packet from [%d] of type %d", pasPerson->mcsID, pCMPacket->type)); break; } DebugExitVOID(ASShare::CM_ReceivedPacket); } // // CM_ApplicationMovedCursor(..) // void ASHost::CM_ApplicationMovedCursor(void) { DebugEntry(ASHost::CM_ApplicationMovedCursor); WARNING_OUT(("CM host: cursor moved by app, tell viewers")); m_cmfSyncPos = TRUE; CM_MaybeSendCursorMovedPacket(); DebugExitVOID(ASHost::CM_ApplicationMovedCursor); } // // CM_RecalcCaps() // // This calculates the CM 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::CM_RecalcCaps(BOOL fJoiner) { ASPerson * pasT; DebugEntry(ASShare::CM_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); m_pHost->m_cmNumTxCacheEntries = m_pasLocal->cpcCaps.cursor.capsCursorCacheSize; m_pHost->m_cmfUseColorCursorProtocol = (m_pasLocal->cpcCaps.cursor.capsSupportsColorCursors == CAPS_SUPPORTED); // // Now with 3.0, viewers just create caches which are the size // of the host's send caps. No more min, no more receive caps // if (m_scShareVersion < CAPS_VERSION_30) { TRACE_OUT(("In share with 2.x nodes, must recalc CM caps")); for (pasT = m_pasLocal->pasNext; pasT != NULL; pasT = pasT->pasNext) { m_pHost->m_cmNumTxCacheEntries = min(m_pHost->m_cmNumTxCacheEntries, pasT->cpcCaps.cursor.capsCursorCacheSize); if (pasT->cpcCaps.cursor.capsSupportsColorCursors != CAPS_SUPPORTED) { m_pHost->m_cmfUseColorCursorProtocol = FALSE; } } TRACE_OUT(("Recalced CM caps: Tx Cache size %d, color cursors %d", m_pHost->m_cmNumTxCacheEntries, (m_pHost->m_cmfUseColorCursorProtocol != FALSE))); } DC_EXIT_POINT: DebugExitVOID(ASShare::CM_RecalcCaps); } // // FUNCTION: CMReceivedCursorShapePacket // // DESCRIPTION: // // Processes a received cursor shape packet. // // PARAMETERS: // // personID - ID of the packet sender // // pCMPacket - pointer to the received cursor shape packet // // RETURNS: Nothing // // void ASShare::CMReceivedCursorShapePacket ( ASPerson * pasPerson, PCMPACKETHEADER pCMPacket ) { BOOL fSetCursorToNULL = FALSE; HCURSOR hNewCursor; HCURSOR hOldCursor = NULL; POINT newHotSpot; UINT cacheID; DebugEntry(ASShare::CMReceivedCursorShapePacket); ValidatePerson(pasPerson); // // Now create or load the new cursor. // switch (pCMPacket->type) { case CM_CURSOR_ID: CMProcessCursorIDPacket((PCMPACKETID)pCMPacket, &hNewCursor, &newHotSpot); break; case CM_CURSOR_MONO_BITMAP: case CM_CURSOR_COLOR_BITMAP: if (pCMPacket->type == CM_CURSOR_MONO_BITMAP) { cacheID = CMProcessMonoCursorPacket((PCMPACKETMONOBITMAP)pCMPacket, &hNewCursor, &newHotSpot); } else { cacheID = CMProcessColorCursorPacket((PCMPACKETCOLORBITMAP)pCMPacket, &hNewCursor, &newHotSpot ); } ASSERT(pasPerson->acmRxCache); ASSERT(cacheID < pasPerson->ccmRxCache); hOldCursor = pasPerson->acmRxCache[cacheID].hCursor; if (hNewCursor != NULL) { TRACE_OUT(("Cursor using cache %u", cacheID)); pasPerson->acmRxCache[cacheID].hCursor = hNewCursor; pasPerson->acmRxCache[cacheID].hotSpot = newHotSpot; } else { // // use default cursor. // TRACE_OUT(( "color cursor failed so use arrow")); pasPerson->acmRxCache[cacheID].hCursor = NULL; pasPerson->acmRxCache[cacheID].hotSpot.x = 0; pasPerson->acmRxCache[cacheID].hotSpot.y = 0; hNewCursor = m_cmArrowCursor; newHotSpot = m_cmArrowCursorHotSpot; } break; case CM_CURSOR_COLOR_CACHE: cacheID = ((PCMPACKETCOLORCACHE)pCMPacket)->cacheIndex; ASSERT(pasPerson->acmRxCache); ASSERT(cacheID < pasPerson->ccmRxCache); // // If the caching failed last time then use the default arrow // cursor. // if (pasPerson->acmRxCache[cacheID].hCursor == NULL) { TRACE_OUT(( "cache empty so use arrow")); hNewCursor = m_cmArrowCursor; newHotSpot = m_cmArrowCursorHotSpot; } else { hNewCursor = pasPerson->acmRxCache[cacheID].hCursor; newHotSpot = pasPerson->acmRxCache[cacheID].hotSpot; } break; default: WARNING_OUT(( "Unknown cursor type: %u", pCMPacket->type)); DC_QUIT; } // // Destroy the old cursor. Note that for bitmap cursor packets, // we will set the cursor to the new image twice. // if (hOldCursor) { if (hOldCursor == ::GetCursor()) { ::SetCursor(hNewCursor); } ::DestroyCursor(hOldCursor); } pasPerson->cmhRemoteCursor = hNewCursor; // // Decide what to do with the new cursor... // if (!pasPerson->cmShadowOff) { // // The shadow cursor is enabled so update it. It won't change state // or move, it will just repaint with the new image and/or hotspot. // TRACE_OUT(("Update shadow cursor")); CM_UpdateShadowCursor(pasPerson, pasPerson->cmShadowOff, pasPerson->cmPos.x, pasPerson->cmPos.y, newHotSpot.x, newHotSpot.y); } else { HWND hwnd; // Update the hotspot. pasPerson->cmHotSpot = newHotSpot; // Refresh if no old cursor ASSERT(pasPerson->m_pView); hwnd = CMGetControllingWindow(); if (hwnd == pasPerson->m_pView->m_viewClient) { SendMessage(hwnd, WM_SETCURSOR, (WPARAM)hwnd, MAKELONG(HTCLIENT, 0)); } } DC_EXIT_POINT: DebugExitVOID(ASShare::CMReceivedCursorShapePacket); } // // FUNCTION: CMProcessMonoCursorPacket // // DESCRIPTION: // // Processes a received mono cursor packet. // // PARAMETERS: // // pCMPacket - pointer to the received cursor ID packet // // phNewCursor - pointer to a HCURSOR variable that receives the handle // of a cursor that corresponds to the received packet // // pNewHotSpot - pointer to a POINT variable that receives the hot-spot // of the new cursor // // RETURNS: Nothing // // UINT ASShare::CMProcessMonoCursorPacket ( PCMPACKETMONOBITMAP pCMPacket, HCURSOR* phNewCursor, LPPOINT pNewHotSpot ) { UINT cbReceivedMaskBytes; LPBYTE pANDMask; LPBYTE pXORMask; DebugEntry(ASShare::CMProcessMonoCursorPacket); // // Work out the size (in bytes) of the two bitmap masks we have just // received. (Cursor bitmaps are 1bpp and word padded). // cbReceivedMaskBytes = pCMPacket->height * CM_BYTES_FROM_WIDTH(pCMPacket->width); // // NOTE: Compressed cursors are an R.11 remnant. NM 1.0 and 2.0 never // sent them specially compressed. Therefore the code to handle // decompression should be unnecessary. Let's find out! // ASSERT(pCMPacket->header.type == CM_CURSOR_MONO_BITMAP); // // Get the XOR and AND masks // pXORMask = pCMPacket->aBits; pANDMask = pXORMask + cbReceivedMaskBytes; // // Create a cursor from the definition supplied in the packet. // *phNewCursor = CMCreateMonoCursor(pCMPacket->xHotSpot, pCMPacket->yHotSpot, pCMPacket->width, pCMPacket->height, pANDMask, pXORMask); if (*phNewCursor == NULL) { WARNING_OUT(( "Failed to create hRemoteCursor")); DC_QUIT; } // // Return the hot spot. // pNewHotSpot->x = pCMPacket->xHotSpot; pNewHotSpot->y = pCMPacket->yHotSpot; DC_EXIT_POINT: DebugExitDWORD(ASShare::CMProcessMonoCursorPacket, 0); return(0); } // // FUNCTION: CMProcessColorCursorPacket // // DESCRIPTION: // // Processes a received color cursor packet. // // PARAMETERS: // // pCMPacket - pointer to the received cursor ID packet // // phNewCursor - pointer to a HCURSOR variable that receives the handle // of a cursor that corresponds to the received packet // // pNewHotSpot - pointer to a POINT variable that receives the hot-spot // of the new cursor // // RETURNS: Nothing // // UINT ASShare::CMProcessColorCursorPacket ( PCMPACKETCOLORBITMAP pCMPacket, HCURSOR* phNewCursor, LPPOINT pNewHotSpot ) { LPBYTE pXORBitmap; LPBYTE pANDMask; DebugEntry(ASShare::CMProcessColorCursorPacket); // // Calculate the pointers to the XOR bitmap and the AND mask within the // color cursor data. // pXORBitmap = pCMPacket->aBits; pANDMask = pXORBitmap + pCMPacket->cbXORBitmap; // // Create a cursor from the definition supplied in the packet. // *phNewCursor = CMCreateColorCursor(pCMPacket->xHotSpot, pCMPacket->yHotSpot, pCMPacket->cxWidth, pCMPacket->cyHeight, pANDMask, pXORBitmap, pCMPacket->cbANDMask, pCMPacket->cbXORBitmap); if (*phNewCursor == NULL) { WARNING_OUT(( "Failed to create color cursor")); DC_QUIT; } // // Return the hot spot. // pNewHotSpot->x = pCMPacket->xHotSpot; pNewHotSpot->y = pCMPacket->yHotSpot; DC_EXIT_POINT: DebugExitDWORD(ASShare::CMProcessColorCursorPacket, pCMPacket->cacheIndex); return(pCMPacket->cacheIndex); } // // FUNCTION: CMReceivedCursorMovedPacket // // DESCRIPTION: // // Processes a received cursor movement packet. // // PARAMETERS: // // personID - ID of the sender of this packet // // pCMPacket - pointer to the received cursor movement packet // // RETURNS: Nothing // // void ASShare::CMReceivedCursorMovedPacket ( ASPerson * pasFrom, PCMPACKETHEADER pCMHeader ) { ASPerson * pasControlling; PCMPACKETMOVE pCMPacket = (PCMPACKETMOVE)pCMHeader; DebugEntry(ASShare::CMReceivedCursorMovedPacket); // // Handle an incoming cursor moved packet. // ValidatePerson(pasFrom); TRACE_OUT(("Received cursor move packet from [%d] to pos (%d,%d)", pasFrom->mcsID, pCMPacket->xPos, pCMPacket->yPos)); CM_UpdateShadowCursor(pasFrom, pasFrom->cmShadowOff, pCMPacket->xPos, pCMPacket->yPos, pasFrom->cmHotSpot.x, pasFrom->cmHotSpot.y); // // If we're in control of this person and it's a sync, we need to // move our cursor too, to reflect where the app really stuck it. // if ((pasFrom->m_caControlledBy == m_pasLocal) && !pasFrom->m_caControlPaused && (pCMPacket->header.flags & CM_SYNC_CURSORPOS)) { // // If our mouse is over this host's client area, // autoscroll to pos or move our cursor // WARNING_OUT(("CM SYNC pos to {%04d, %04d}", pCMPacket->xPos, pCMPacket->yPos)); VIEW_SyncCursorPos(pasFrom, pCMPacket->xPos, pCMPacket->yPos); } DebugExitVOID(ASShare::CMReceivedCursorMovedPacket); } // // CM_UpdateShadowCursor() // // This repaints the host's shadow cursor in the view frame we have for him. // It is used when // * the cursor image has changed // * the cursor tag has changed (due to control changes) // * the cursor hotspot has changed // * the cursor state is changing between on and off // * the cursor has moved // void ASShare::CM_UpdateShadowCursor ( ASPerson * pasPerson, BOOL cmShadowOff, int xNewPos, int yNewPos, int xNewHot, int yNewHot ) { RECT rcInval; DebugEntry(ASShare::CM_UpdateShadowCursor); // // Is the remote cursor currently on? // if (!pasPerson->cmShadowOff) { if (pasPerson->m_pView) { // // We need to invalidate the old rectangle where the cursor // was. We need to adjust for the hotspot. Also, adjust for // any scrolling we may have done in the view frame. // rcInval.left = pasPerson->cmPos.x - pasPerson->cmHotSpot.x; rcInval.top = pasPerson->cmPos.y - pasPerson->cmHotSpot.y; rcInval.right = rcInval.left + m_cmCursorWidth; rcInval.bottom = rcInval.top + m_cmCursorHeight; VIEW_InvalidateRect(pasPerson, &rcInval); } } // Update the state, position, and hotspot pasPerson->cmShadowOff = cmShadowOff; pasPerson->cmPos.x = xNewPos; pasPerson->cmPos.y = yNewPos; pasPerson->cmHotSpot.x = xNewHot; pasPerson->cmHotSpot.y = yNewHot; if (!pasPerson->cmShadowOff) { if (pasPerson->m_pView) { // // We need to invalidate the new rectangle where the cursor is // moving to. Again, we need to adjust for the hotspot, and any // scrolling done in the view frame. // rcInval.left = pasPerson->cmPos.x - pasPerson->cmHotSpot.x; rcInval.top = pasPerson->cmPos.y - pasPerson->cmHotSpot.y; rcInval.right = rcInval.left + m_cmCursorWidth; rcInval.bottom = rcInval.top + m_cmCursorHeight; VIEW_InvalidateRect(pasPerson, &rcInval); } } DebugExitVOID(ASShare::CM_UpdateShadowCursor); } void ASHost::CM_MaybeSendCursorMovedPacket(void) { PCMPACKETMOVE pCMPacket; POINT cursorPos; #ifdef _DEBUG UINT sentSize; #endif DebugEntry(ASHost::CM_MaybeSendCursorMovedPacket); // // Get the cursor position. // if(!GetCursorPos(&cursorPos)) { WARNING_OUT(("Unable to get cursor position. Error=%d", GetLastError())); goto DC_EXIT_POINT; } // // Has it changed? // if (m_cmfSyncPos || (cursorPos.x != m_cmLastCursorPos.x) || (cursorPos.y != m_cmLastCursorPos.y)) { // // Try to allocate a packet. // pCMPacket = (PCMPACKETMOVE)m_pShare->SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, sizeof(*pCMPacket)); if (!pCMPacket) { WARNING_OUT(("Failed to alloc CM move packet")); DC_QUIT; } TRACE_OUT(("Sending cursor moved packet to pos (%d, %d)", cursorPos.x, cursorPos.y)); // // Fill in the fields // pCMPacket->header.header.data.dataType = DT_CM; pCMPacket->header.type = CM_CURSOR_MOVE; pCMPacket->header.flags = 0; if (m_cmfSyncPos) { pCMPacket->header.flags |= CM_SYNC_CURSORPOS; } pCMPacket->xPos = (TSHR_UINT16)cursorPos.x; pCMPacket->yPos = (TSHR_UINT16)cursorPos.y; // // Compress and send the packet. // if (m_pShare->m_scfViewSelf) m_pShare->CM_ReceivedPacket(m_pShare->m_pasLocal, &(pCMPacket->header.header)); #ifdef _DEBUG sentSize = #endif // _DEBUG m_pShare->DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID, &(pCMPacket->header.header), sizeof(*pCMPacket)); TRACE_OUT(("CM MOVE packet size: %08d, sent %08d", sizeof(*pCMPacket), sentSize)); m_cmfSyncPos = FALSE; m_cmLastCursorPos = cursorPos; } DC_EXIT_POINT: DebugExitVOID(ASHost::CM_MaybeSendCursorMovedPacket); } // // FUNCTION: CMSendCursorShape // // DESCRIPTION: // // Sends a packet containing the given cursor shape (bitmap). If the // same shape is located in the cache then a cached cursor packet is sent. // // PARAMETERS: // // pCursorShape - pointer to the cursor shape // // cbCursorDataSize - pointer to the cursor data size // // RETURNS: TRUE if successful, FALSE otherwise. // // BOOL ASHost::CMSendCursorShape ( LPCM_SHAPE pCursorShape, UINT cbCursorDataSize ) { BOOL rc = FALSE; BOOL fInCache; LPCM_SHAPE pCacheData; UINT iCacheEntry; DebugEntry(ASHost::CMSendCursorShape); fInCache = CH_SearchCache(m_cmTxCacheHandle, (LPBYTE)pCursorShape, cbCursorDataSize, 0, &iCacheEntry ); if (!fInCache) { pCacheData = (LPCM_SHAPE)new BYTE[cbCursorDataSize]; if (pCacheData == NULL) { WARNING_OUT(("Failed to alloc CM_SHAPE data")); DC_QUIT; } memcpy(pCacheData, pCursorShape, cbCursorDataSize); iCacheEntry = CH_CacheData(m_cmTxCacheHandle, (LPBYTE)pCacheData, cbCursorDataSize, 0); TRACE_OUT(( "Cache new cursor: pShape 0x%p, iEntry %u", pCursorShape, iCacheEntry)); if (!CMSendColorBitmapCursor(pCacheData, iCacheEntry )) { CH_RemoveCacheEntry(m_cmTxCacheHandle, iCacheEntry); DC_QUIT; } } else { TRACE_OUT(("Cursor in cache: pShape 0x%p, iEntry %u", pCursorShape, iCacheEntry)); if (!CMSendCachedCursor(iCacheEntry)) { DC_QUIT; } } // // Return success. // rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASHost::CMSendCursorShape, rc); return(rc); } // // FUNCTION: CMCopy1bppTo1bpp // // DESCRIPTION: // // Color conversion utility function to copy 1bpp cursor data to 1bpp (no // conversion required). // // Data is assumed to be padded to word boundaries, and that the // destination buffer is big enough to receive the 1bpp cursor data. // // PARAMETERS: // // pSrc - pointer to source data // // pDst - pointer to destination buffer // // cx - width of cursor in pixels // // cy - height of cursor in pixels // // RETURNS: Nothing // // void CMCopy1bppTo1bpp( LPBYTE pSrc, LPBYTE pDst, UINT cx, UINT cy ) { UINT cbRowWidth; DebugEntry(CMCopy1bppTo1bpp); cbRowWidth = ((cx + 15)/16) * 2; memcpy(pDst, pSrc, (cbRowWidth * cy)); DebugExitVOID(CMCopy1bppTo1bpp); } // // FUNCTION: CMCopy4bppTo1bpp // // DESCRIPTION: // // Color conversion utility function to copy 4bpp cursor data to 1bpp. // // Data is assumed to be padded to word boundaries, and that the // destination buffer is big enough to receive the 1bpp cursor data. // // PARAMETERS: // // pSrc - pointer to source data // // pDst - pointer to destination buffer // // cx - width of cursor in pixels // // cy - height of cursor in pixels // // RETURNS: Nothing // // void CMCopy4bppTo1bpp( LPBYTE pSrc, LPBYTE pDst, UINT cx, UINT cy ) { UINT x; UINT y; UINT cbDstRowWidth; UINT cbSrcRowWidth; UINT cbUnpaddedDstRowWidth; BOOL fPadByteNeeded; BYTE Mask; DebugEntry(CMCopy4bppTo1bpp); cbDstRowWidth = ((cx + 15)/16) * 2; cbUnpaddedDstRowWidth = (cx + 7) / 8; cbSrcRowWidth = (cx + 1) / 2; fPadByteNeeded = ((cbDstRowWidth - cbUnpaddedDstRowWidth) > 0); for (y = 0; y < cy; y++) { *pDst = 0; Mask = 0x80; for (x = 0; x < cbSrcRowWidth; x++) { if (Mask == 0) { Mask = 0x80; pDst++; *pDst = 0; } if ((*pSrc & 0xF0) != 0) { *pDst |= Mask; } if ((*pSrc & 0x0F) != 0) { *pDst |= (Mask >> 1); } Mask >>= 2; pSrc++; } if (fPadByteNeeded) { pDst++; *pDst = 0; } pDst++; } DebugExitVOID(CMCopy4bppTo1bpp); } // // FUNCTION: CMCopy8bppTo1bpp // // DESCRIPTION: // // Color conversion utility function to copy 8bpp cursor data to 1bpp. // // Data is assumed to be padded to word boundaries, and that the // destination buffer is big enough to receive the 1bpp cursor data. // // PARAMETERS: // // pSrc - pointer to source data // // pDst - pointer to destination buffer // // cx - width of cursor in pixels // // cy - height of cursor in pixels // // RETURNS: Nothing // // void CMCopy8bppTo1bpp( LPBYTE pSrc, LPBYTE pDst, UINT cx, UINT cy ) { UINT x; UINT y; UINT cbDstRowWidth; UINT cbSrcRowWidth; UINT cbUnpaddedDstRowWidth; BOOL fPadByteNeeded; BYTE Mask; DebugEntry(CMCopy8bppTo1bpp); cbDstRowWidth = ((cx + 15)/16) * 2; cbUnpaddedDstRowWidth = (cx + 7) / 8; cbSrcRowWidth = cx; fPadByteNeeded = ((cbDstRowWidth - cbUnpaddedDstRowWidth) > 0); for (y = 0; y < cy; y++) { *pDst = 0; Mask = 0x80; for (x = 0; x < cbSrcRowWidth; x++) { if (Mask == 0x00) { Mask = 0x80; pDst++; *pDst = 0; } if (*pSrc != 0) { *pDst |= Mask; } Mask >>= 1; pSrc++; } if (fPadByteNeeded) { pDst++; *pDst = 0; } pDst++; } DebugExitVOID(CMCopy8bppTo1bpp); } // // FUNCTION: CMCopy16bppTo1bpp // // DESCRIPTION: // // Color conversion utility function to copy 16bpp cursor data to 1bpp. // // Data is assumed to be padded to word boundaries, and that the // destination buffer is big enough to receive the 1bpp cursor data. // // PARAMETERS: // // pSrc - pointer to source data // // pDst - pointer to destination buffer // // cx - width of cursor in pixels // // cy - height of cursor in pixels // // RETURNS: Nothing // // void CMCopy16bppTo1bpp( LPBYTE pSrc, LPBYTE pDst, UINT cx, UINT cy ) { UINT x; UINT y; UINT cbDstRowWidth; UINT cbUnpaddedDstRowWidth; BOOL fPadByteNeeded; BYTE Mask; DebugEntry(CMCopy16bppTo1bpp); cbDstRowWidth = ((cx + 15)/16) * 2; cbUnpaddedDstRowWidth = (cx + 7) / 8; fPadByteNeeded = ((cbDstRowWidth - cbUnpaddedDstRowWidth) > 0); for (y = 0; y < cy; y++) { *pDst = 0; Mask = 0x80; for (x = 0; x < cx; x++) { if (Mask == 0) { Mask = 0x80; pDst++; *pDst = 0; } if (*(LPTSHR_UINT16)pSrc != 0) { *pDst |= Mask; } Mask >>= 1; pSrc += 2; } if (fPadByteNeeded) { pDst++; *pDst = 0; } pDst++; } DebugExitVOID(CMCopy16bppTo1bpp); } // // FUNCTION: CMCopy24bppTo1bpp // // DESCRIPTION: // // Color conversion utility function to copy 24bpp cursor data to 1bpp. // // Data is assumed to be padded to word boundaries, and that the // destination buffer is big enough to receive the 1bpp cursor data. // // PARAMETERS: // // pSrc - pointer to source data // // pDst - pointer to destination buffer // // cx - width of cursor in pixels // // cy - height of cursor in pixels // // RETURNS: Nothing // // void CMCopy24bppTo1bpp( LPBYTE pSrc, LPBYTE pDst, UINT cx, UINT cy ) { UINT x; UINT y; UINT cbDstRowWidth; UINT cbUnpaddedDstRowWidth; BOOL fPadByteNeeded; BYTE Mask; UINT intensity; DebugEntry(CMCopy24bppTo1bpp); cbDstRowWidth = ((cx + 15)/16) * 2; cbUnpaddedDstRowWidth = (cx + 7) / 8; fPadByteNeeded = ((cbDstRowWidth - cbUnpaddedDstRowWidth) > 0); for (y = 0; y < cy; y++) { *pDst = 0; Mask = 0x80; for (x = 0; x < cx; x++) { if (Mask == 0) { Mask = 0x80; pDst++; *pDst = 0; } // // Work out the intensity of the RGB value. There are three // possible results // 1) intensity <=CM_BLACK_THRESHOLD // -- we leave the dest as blck // 2) intensity > CM_WHITE_THRESHOLD // -- we definitely map to white // 3) otherwise // -- we map to white in a grid hatching fashion // intensity = ((UINT)pSrc[0]*(UINT)pSrc[0]) + ((UINT)pSrc[1]*(UINT)pSrc[1]) + ((UINT)pSrc[2]*(UINT)pSrc[2]); if ( (intensity > CM_WHITE_THRESHOLD) || ((intensity > CM_BLACK_THRESHOLD) && (((x ^ y) & 1) == 1))) { *pDst |= Mask; } Mask >>= 1; pSrc += 3; } if (fPadByteNeeded) { pDst++; *pDst = 0; } pDst++; } DebugExitVOID(CMCopy24bppTo1bpp); } // // FUNCTION: CMSendCachedCursor // // DESCRIPTION: // // Sends a packet containing the given cache entry id. // // PARAMETERS: // // iCacheEntry - cache index // // RETURNS: TRUE if packet sent, FALSE otherwise. // // BOOL ASHost::CMSendCachedCursor(UINT iCacheEntry) { BOOL rc = FALSE; PCMPACKETCOLORCACHE pCMPacket; #ifdef _DEBUG UINT sentSize; #endif // _DEBUG DebugEntry(ASHost::CMSendCachedCursor); TRACE_OUT(( "Send cached cursor(%u)", iCacheEntry)); pCMPacket = (PCMPACKETCOLORCACHE)m_pShare->SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, sizeof(*pCMPacket)); if (!pCMPacket) { WARNING_OUT(("Failed to alloc CM cached image packet")); DC_QUIT; } // // Fill in the packet. // pCMPacket->header.header.data.dataType = DT_CM; pCMPacket->header.type = CM_CURSOR_COLOR_CACHE; pCMPacket->cacheIndex = (TSHR_UINT16)iCacheEntry; // // Send it // if (m_pShare->m_scfViewSelf) m_pShare->CM_ReceivedPacket(m_pShare->m_pasLocal, &(pCMPacket->header.header)); #ifdef _DEBUG sentSize = #endif // _DEBUG m_pShare->DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID, &(pCMPacket->header.header), sizeof(*pCMPacket)); TRACE_OUT(("CM COLOR CACHE packet size: %08d, sent %08d", sizeof(*pCMPacket), sentSize)); rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASHost::CMSendCachedCursor, rc); return(rc); } // // FUNCTION: CMGetControllingWindow // // DESCRIPTION: // // Determines the window that is controlling the cursor's current shape. // // PARAMETERS: None // // RETURNS: the window that is controlling the cursor's current shape. // // HWND CMGetControllingWindow(void) { POINT cursorPos; HWND hwnd; DebugEntry(CMGetControllingWindow); // // If a SysErrPopup Window (which is always System Modal) is present // then WindowFromPoint enters a infinite recursion loop, trashing the // stack and crashing the whole system. // If there is a SysModal window Window ensure WindowFromPoint is not // executed. // // The window controlling the cursor appearance is: // // - the local window that has the mouse capture (if any) // - the window that is under the current mouse position // // hwnd = GetCapture(); if (!hwnd) { // // Get the current mouse position. // GetCursorPos(&cursorPos); hwnd = WindowFromPoint(cursorPos); } DebugExitDWORD(CMGetControllingWindow, HandleToUlong(hwnd)); return(hwnd); } // // FUNCTION: CMGetCurrentCursor // // DESCRIPTION: // // Returns a description of the current cursor // // PARAMETERS: // // pCursor - pointer to a CURSORDESCRIPTION variable that receives details // of the current cursor // // RETURNS: Nothing // // void CMGetCurrentCursor(LPCURSORDESCRIPTION pCursor) { LPCM_FAST_DATA lpcmShared; DebugEntry(CMGetCurrentCursor); lpcmShared = CM_SHM_START_READING; pCursor->type = CM_CD_BITMAPCURSOR; pCursor->id = lpcmShared->cmCursorStamp; CM_SHM_STOP_READING; DebugExitVOID(CMGetCurrentCursor); } // // FUNCTION: CMSendSystemCursor // // DESCRIPTION: // // Sends a packet containing the given system cursor IDC. // // PARAMETERS: // // cursorIDC - the IDC of the system cursor to send // // RETURNS: TRUE if successful, FALSE otherwise. // // BOOL ASHost::CMSendSystemCursor(UINT cursorIDC) { BOOL rc = FALSE; PCMPACKETID pCMPacket; #ifdef _DEBUG UINT sentSize; #endif // _DEBUG DebugEntry(ASHost::CMSendSystemCursor); ASSERT((cursorIDC == CM_IDC_NULL) || (cursorIDC == CM_IDC_ARROW)); // // The cursor is one of the system cursors - create a PROTCURSOR packet // pCMPacket = (PCMPACKETID)m_pShare->SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, sizeof(*pCMPacket)); if (!pCMPacket) { WARNING_OUT(("Failed to alloc CM system image packet")); DC_QUIT; } // // Fill in the packet. // pCMPacket->header.header.data.dataType = DT_CM; pCMPacket->header.type = CM_CURSOR_ID; pCMPacket->idc = cursorIDC; TRACE_OUT(( "Send CMCURSORID %ld", cursorIDC)); // // Send it // if (m_pShare->m_scfViewSelf) m_pShare->CM_ReceivedPacket(m_pShare->m_pasLocal, &(pCMPacket->header.header)); #ifdef _DEBUG sentSize = #endif // _DEBUG m_pShare->DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID, &(pCMPacket->header.header), sizeof(*pCMPacket)); TRACE_OUT(("CM ID packet size: %08d, sent %08d", sizeof(*pCMPacket), sentSize)); // // Indicate that we successfully sent a packet. // rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASHost::CMSendSystemCursor, rc); return(rc); } // // FUNCTION: CMSendBitmapCursor // // DESCRIPTION: // // Sends the current cursor as a bitmap. // // PARAMETERS: None // // RETURNS: TRUE if successful, FALSE otherwise. // // BOOL ASHost::CMSendBitmapCursor(void) { BOOL rc = FALSE; LPCM_SHAPE pCursor; UINT cbCursorDataSize; DebugEntry(ASHost::CMSendBitmapCursor); // // If cursor is hidden, send Null cursor // if (m_cmfCursorHidden) { TRACE_OUT(( "Send Null cursor (cursor hidden)")); CMSendSystemCursor(CM_IDC_NULL); DC_QUIT; } // // Get a pointer to the current cursor shape. // if (!CMGetCursorShape(&pCursor, &cbCursorDataSize)) { DC_QUIT; } // // If this is a Null pointer, send the relevant packet. // if (CM_CURSOR_IS_NULL(pCursor)) { TRACE_OUT(( "Send Null cursor")); CMSendSystemCursor(CM_IDC_NULL); DC_QUIT; } // // If all of the parties in the call support the color cursor protocol // then we try to send the cursor using that protocol, otherwise we // send a mono cursor. // if (m_cmfUseColorCursorProtocol) { if (!CMSendCursorShape(pCursor, cbCursorDataSize)) { DC_QUIT; } } else { // // We cannot send cursors that are not 32x32 using the mono // protocol. // if ((pCursor->hdr.cx != 32) || (pCursor->hdr.cy != 32)) { // // Maybe copy and alter the cursor definition so that it is // 32x32 ? // WARNING_OUT(( "Non-standard cursor (%d x %d)", pCursor->hdr.cx, pCursor->hdr.cy )); DC_QUIT; } if (!CMSendMonoBitmapCursor(pCursor)) { DC_QUIT; } } // // Return success. // rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASHost::CMSendBitmapCursor, rc); return(rc); } // // FUNCTION: CMCalculateColorCursorSize // // DESCRIPTION: // // Calculates the size in bytes of a given color cursor. // // PARAMETERS: // // pCursor - pointer to the cursor shape // // pcbANDMaskSize - pointer to a UINT variable that receives the AND mask // size in bytes // // pcbXORBitmapSize - pointer to a UINT variable that receives the XOR // bitmap size in bytes // // RETURNS: Nothing // // void CMCalculateColorCursorSize( LPCM_SHAPE pCursor, LPUINT pcbANDMaskSize, LPUINT pcbXORBitmapSize) { DebugEntry(CMCalculcateColorCursorSize); *pcbANDMaskSize = CURSOR_AND_MASK_SIZE(pCursor); *pcbXORBitmapSize = CURSOR_DIB_BITS_SIZE( pCursor->hdr.cx, pCursor->hdr.cy, 24 ); DebugExitVOID(CMCalculateColorCursorSize); } // // FUNCTION: CMSendColorBitmapCursor // // DESCRIPTION: // // Sends a given cursor as a color bitmap. // // PARAMETERS: // // pCursor - pointer to the cursor shape // // iCacheEntry - cache index to store in the transmitted packet // // RETURNS: TRUE if packet sent, FALSE otherwise // // BOOL ASHost::CMSendColorBitmapCursor(LPCM_SHAPE pCursor, UINT iCacheEntry) { UINT cbPacketSize; PCMPACKETCOLORBITMAP pCMPacket; BOOL rc = FALSE; UINT cbANDMaskSize; UINT cbXORBitmapSize; UINT cbColorCursorSize; #ifdef _DEBUG UINT sentSize; #endif // _DEBUG DebugEntry(ASHost::CMSendColorBitmapCursor); CMCalculateColorCursorSize(pCursor, &cbANDMaskSize, &cbXORBitmapSize ); cbColorCursorSize = cbANDMaskSize + cbXORBitmapSize; // // Allocate a packet. // cbPacketSize = sizeof(CMPACKETCOLORBITMAP) + (cbColorCursorSize - 1); pCMPacket = (PCMPACKETCOLORBITMAP)m_pShare->SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, cbPacketSize); if (!pCMPacket) { WARNING_OUT(("Failed to alloc CM color image packet, size %u", cbPacketSize)); DC_QUIT; } // // Fill in the packet. // pCMPacket->header.header.data.dataType = DT_CM; // // Fill in fields. // pCMPacket->header.type = CM_CURSOR_COLOR_BITMAP; pCMPacket->cacheIndex = (TSHR_UINT16)iCacheEntry; if (!CMGetColorCursorDetails(pCursor, &(pCMPacket->cxWidth), &(pCMPacket->cyHeight), &(pCMPacket->xHotSpot), &(pCMPacket->yHotSpot), pCMPacket->aBits + cbXORBitmapSize, &(pCMPacket->cbANDMask), pCMPacket->aBits, &(pCMPacket->cbXORBitmap ))) { // // Failed to get a cursor details. Must free up SNI packet // S20_FreeDataPkt(&(pCMPacket->header.header)); DC_QUIT; } ASSERT((pCMPacket->cbANDMask == cbANDMaskSize)); ASSERT((pCMPacket->cbXORBitmap == cbXORBitmapSize)); // // Send it // if (m_pShare->m_scfViewSelf) m_pShare->CM_ReceivedPacket(m_pShare->m_pasLocal, &(pCMPacket->header.header)); #ifdef _DEBUG sentSize = #endif // _DEBUG m_pShare->DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID, &(pCMPacket->header.header), sizeof(*pCMPacket)); TRACE_OUT(("CM COLOR BITMAP packet size: %08d, sent %08d", sizeof(*pCMPacket), sentSize)); // // Indicate that we successfully sent a packet. // rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASHost::CMSendColorBitmapCursor, rc); return(rc); } // // FUNCTION: CMSendMonoBitmapCursor // // DESCRIPTION: // // Sends a given cursor as a mono bitmap // // PARAMETERS: // // pCursor - pointer to the cursor shape // // RETURNS: TRUE if packet sent, FALSE otherwise // // BOOL ASHost::CMSendMonoBitmapCursor(LPCM_SHAPE pCursor) { UINT cbPacketSize; PCMPACKETMONOBITMAP pCMPacket; BOOL rc = FALSE; TSHR_UINT16 cbANDMaskSize; TSHR_UINT16 cbXORBitmapSize; #ifdef _DEBUG UINT sentSize; #endif // _DEBUG DebugEntry(AShare::CMSendMonoBitmapCursor); // // Calculate the sizes of the converted (1bpp) AND and XOR bitmaps. // cbANDMaskSize = (TSHR_UINT16)CURSOR_AND_MASK_SIZE(pCursor); cbXORBitmapSize = cbANDMaskSize; // // Allocate a packet. // cbPacketSize = sizeof(CMPACKETMONOBITMAP) + (cbANDMaskSize + cbXORBitmapSize - 1); pCMPacket = (PCMPACKETMONOBITMAP)m_pShare->SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, cbPacketSize); if (!pCMPacket) { WARNING_OUT(("Failed to alloc CM mono image packet, size %u", cbPacketSize)); DC_QUIT; } // // Fill FF in to initialize the XOR and AND bits // FillMemory((LPBYTE)(pCMPacket+1)-1, cbANDMaskSize + cbXORBitmapSize, 0xFF); // // Fill in the packet. // pCMPacket->header.header.data.dataType = DT_CM; // // Fill in fields. // pCMPacket->header.type = CM_CURSOR_MONO_BITMAP; CMGetMonoCursorDetails(pCursor, &(pCMPacket->width), &(pCMPacket->height), &(pCMPacket->xHotSpot), &(pCMPacket->yHotSpot), pCMPacket->aBits + cbXORBitmapSize, &cbANDMaskSize, pCMPacket->aBits, &cbXORBitmapSize ); pCMPacket->cbBits = (TSHR_UINT16) (cbANDMaskSize + cbXORBitmapSize); TRACE_OUT(( "Mono cursor cx:%u cy:%u xhs:%u yhs:%u cbAND:%u cbXOR:%u", pCMPacket->width, pCMPacket->height, pCMPacket->xHotSpot, pCMPacket->yHotSpot, cbANDMaskSize, cbXORBitmapSize)); // // Send it // if (m_pShare->m_scfViewSelf) m_pShare->CM_ReceivedPacket(m_pShare->m_pasLocal, &(pCMPacket->header.header)); #ifdef _DEBUG sentSize = #endif // _DEBUG m_pShare->DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID, &(pCMPacket->header.header), sizeof(*pCMPacket)); TRACE_OUT(("CM MONO BITMAP packet size: %08d, sent %08d", sizeof(*pCMPacket), sentSize)); // // Indicate that we successfully sent a packet. // rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASHost::CMSendMonoBitmapCursor, rc); return(rc); } // // FUNCTION: CMCreateMonoCursor // // DESCRIPTION: Creates a mono cursor // // PARAMETERS: // // xHotSpot - x position of the hotspot // // yHotSpot - y position of the hotspot // // cxWidth - width of the cursor // // cyHeight - height of the cursor // // pANDMask - pointer to a 1bpp, word-padded AND mask // // pXORBitmap - pointer to a 1bpp, word-padded XOR bitmap // // RETURNS: a valid cursor id, or NULL if the function fails // // HCURSOR ASShare::CMCreateMonoCursor(UINT xHotSpot, UINT yHotSpot, UINT cxWidth, UINT cyHeight, LPBYTE pANDMask, LPBYTE pXORBitmap) { HCURSOR rc; DebugEntry(ASShare::CMCreateMonoCursor); // // Attempt to create the mono cursor. // rc = CreateCursor(g_asInstance, xHotSpot, yHotSpot, cxWidth, cyHeight, pANDMask, pXORBitmap); // // Check that the cursor handle is not null. // if (NULL == rc) { // // Substitute the default arrow cursor. // rc = m_cmArrowCursor; WARNING_OUT(( "Could not create cursor - substituting default arrow")); } // // Return the cursor // DebugExitDWORD(ASShare::CMCreateMonoCursor, HandleToUlong(rc)); return(rc); } // // FUNCTION: CMCreateColorCursor // // DESCRIPTION: // // Creates a color cursor. // // PARAMETERS: // // xHotSpot - x position of the hotspot // // yHotSpot - y position of the hotspot // // cxWidth - width of the cursor // // cyHeight - height of the cursor // // pANDMask - pointer to a 1bpp, word-padded AND mask // // pXORBitmap - pointer to a 24bpp, word-padded XOR bitmap // // cbANDMask - the size in bytes of the AND mask // // cbXORBitmap - the size in bytes of the XOR bitmap // // RETURNS: a valid cursor id, or NULL if the function fails // // HCURSOR ASShare::CMCreateColorCursor ( UINT xHotSpot, UINT yHotSpot, UINT cxWidth, UINT cyHeight, LPBYTE pANDMask, LPBYTE pXORBitmap, UINT cbANDMask, UINT cbXORBitmap ) { HCURSOR rc = 0; UINT cbAllocSize; LPBITMAPINFO pbmi = NULL; HDC hdc = NULL; ICONINFO iconInfo; HBITMAP hbmXORBitmap = NULL; HBITMAP hbmANDMask = NULL; HWND hwndDesktop = NULL; DebugEntry(ASShare::CMCreateColorCursor); TRACE_OUT(("xhs(%u) yhs(%u) cx(%u) cy(%u) cbXOR(%u) cbAND(%u)", xHotSpot, yHotSpot, cxWidth, cyHeight, cbXORBitmap, cbANDMask )); // // We need a BITMAPINFO structure plus one additional RGBQUAD (there is // one included within the BITMAPINFO). We use this to pass the 24bpp // XOR bitmap (which has no color table) and the 1bpp AND mask (which // requires 2 colors). // cbAllocSize = sizeof(*pbmi) + sizeof(RGBQUAD); pbmi = (LPBITMAPINFO)new BYTE[cbAllocSize]; if (pbmi == NULL) { WARNING_OUT(( "Failed to alloc bmi(%x)", cbAllocSize)); DC_QUIT; } // // Get a screen DC that we can pass to CreateDIBitmap. We do not use // CreateCompatibleDC(NULL) here because that results in Windows // creating a mono bitmap. // hwndDesktop = GetDesktopWindow(); hdc = GetWindowDC(hwndDesktop); if (hdc == NULL) { WARNING_OUT(( "Failed to create DC")); DC_QUIT; } pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbmi->bmiHeader.biWidth = cxWidth; pbmi->bmiHeader.biHeight = cyHeight; pbmi->bmiHeader.biPlanes = 1; pbmi->bmiHeader.biBitCount = 24; pbmi->bmiHeader.biCompression = 0; pbmi->bmiHeader.biSizeImage = cbXORBitmap; pbmi->bmiHeader.biXPelsPerMeter = 0; pbmi->bmiHeader.biYPelsPerMeter = 0; pbmi->bmiHeader.biClrUsed = 0; pbmi->bmiHeader.biClrImportant = 0; hbmXORBitmap = CreateDIBitmap( hdc, (LPBITMAPINFOHEADER)pbmi, CBM_INIT, pXORBitmap, pbmi, DIB_RGB_COLORS ); ReleaseDC(hwndDesktop, hdc); if (hbmXORBitmap == NULL) { WARNING_OUT(( "Failed to create XOR bitmap")); DC_QUIT; } // // Create MONOCHROME mask bitmap. This works on both Win95 and NT. // COLOR masks don't work on Win95, just NT. // hdc = CreateCompatibleDC(NULL); if (!hdc) { WARNING_OUT(("Failed to get screen dc")); DC_QUIT; } pbmi->bmiHeader.biBitCount = 1; pbmi->bmiHeader.biCompression = 0; pbmi->bmiHeader.biSizeImage = cbANDMask; // Black pbmi->bmiColors[0].rgbRed = 0x00; pbmi->bmiColors[0].rgbGreen = 0x00; pbmi->bmiColors[0].rgbBlue = 0x00; pbmi->bmiColors[0].rgbReserved = 0x00; // White pbmi->bmiColors[1].rgbRed = 0xFF; pbmi->bmiColors[1].rgbGreen = 0xFF; pbmi->bmiColors[1].rgbBlue = 0xFF; pbmi->bmiColors[1].rgbReserved = 0x00; hbmANDMask = CreateDIBitmap( hdc, (LPBITMAPINFOHEADER)pbmi, CBM_INIT, pANDMask, pbmi, DIB_RGB_COLORS ); DeleteDC(hdc); if (hbmANDMask == NULL) { WARNING_OUT(( "Failed to create AND mask")); DC_QUIT; } #ifdef _DEBUG // // Make sure the AND mask is monochrome // { BITMAP bmp; GetObject(hbmANDMask, sizeof(BITMAP), &bmp); ASSERT(bmp.bmPlanes == 1); ASSERT(bmp.bmBitsPixel == 1); } #endif iconInfo.fIcon = FALSE; iconInfo.xHotspot = xHotSpot; iconInfo.yHotspot = yHotSpot; iconInfo.hbmMask = hbmANDMask; iconInfo.hbmColor = hbmXORBitmap; rc = CreateIconIndirect(&iconInfo); TRACE_OUT(( "CreateCursor(%x) cx(%u)cy(%u)", rc, cxWidth, cyHeight)); DC_EXIT_POINT: if (hbmXORBitmap != NULL) { DeleteBitmap(hbmXORBitmap); } if (hbmANDMask != NULL) { DeleteBitmap(hbmANDMask); } if (pbmi != NULL) { delete[] pbmi; } // // Check that we have successfully managed to create the cursor. If // not then substitute the default cursor. // if (rc == 0) { // // Substitute the default arrow cursor. // rc = m_cmArrowCursor; WARNING_OUT(( "Could not create cursor - substituting default arrow")); } DebugExitDWORD(ASShare::CMCreateColorCursor, HandleToUlong(rc)); return(rc); } // // FUNCTION: CMCreateAbbreviatedName // // DESCRIPTION: // // This function attempts to take a name, and create an abbreviation from // the first characters of the first and last name. // // PARAMETERS: // // szTagName - a pointer to a string containing the name to abbreviate. // szBuf - a pointer to a buffer into which the abbreviation will // be created. // cbBuf - size of buffer pointed to by szBuf. // // RETURNS: // // TRUE: Success. szBuf filled in. // FALSE: Failure. szBuf is not filled in. // // BOOL CMCreateAbbreviatedName(LPCSTR szTagName, LPSTR szBuf, UINT cbBuf) { BOOL rc = FALSE; LPSTR p; LPSTR q; DebugEntry(CMCreateAbbreviatedName); // // This function isn't DBCS safe, so we don't abbreviate in DBCS // character sets. // if (TRUE == GetSystemMetrics(SM_DBCSENABLED)) { DC_QUIT; } // // Try to create initials. If that doesn't work, fail the call. // if ((NULL != (p = (LPSTR)_StrChr(szTagName, ' '))) && ('\0' != *(p+1))) { // // Is there enough room for initials? // if (cbBuf < NTRUNCLETTERS) { DC_QUIT; } q = szBuf; *q++ = *szTagName; *q++ = '.'; *q++ = *(p+1); *q++ = '.'; *q = '\0'; AnsiUpper(szBuf); rc = TRUE; } DC_EXIT_POINT: DebugExitBOOL(CMCreateAbbreviatedName, rc); return rc; } // // FUNCTION: CMDrawCursorTag // // DESCRIPTION: // // PARAMETERS: // // hdcWindow - DC handle of the window to be drawn to // // cursorID - handle of cursor to drawn // // RETURNS: Nothing. // // void ASShare::CMDrawCursorTag ( ASPerson * pasHost, HDC hdc ) { ASPerson * pasPerson; char ShortName[TSHR_MAX_PERSON_NAME_LEN]; HFONT hOldFont = NULL; RECT rect; UINT cCharsFit; LPSTR p; DebugEntry(ASShare::CMDrawCursorTag); pasPerson = pasHost->m_caControlledBy; if (!pasPerson) { // Nothing to do DC_QUIT; } ValidatePerson(pasPerson); // // Try to abbreviate the person's name, so it will fit into the tag. // If the abbreviation fails, just copy the entire name for now. // if (!(CMCreateAbbreviatedName(pasPerson->scName, ShortName, sizeof(ShortName)))) { lstrcpyn(ShortName, pasPerson->scName, sizeof(ShortName)); } // // Select the cursor tag font into the DC. // hOldFont = SelectFont(hdc, m_cmCursorTagFont); if (hOldFont == NULL) { WARNING_OUT(("CMDrawCursorTag failed")); DC_QUIT; } // // Create the tag background... // PatBlt(hdc, TAGXOFF, TAGYOFF, TAGXSIZ, TAGYSIZ, WHITENESS); // // See how many characters of the name or abbreviation we can fit into // the tag. First assume the whole thing fits. // cCharsFit = lstrlen(ShortName); // // Determine how many characters actually fit. // rect.left = rect.top = rect.right = rect.bottom = 0; for (p = AnsiNext(ShortName); ; p = AnsiNext(p)) { if (DrawText(hdc, ShortName, (int)(p - ShortName), &rect, DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX)) { if (rect.right > TAGXSIZ) { // // This number of characters does not fit into the tag. Try // the next smaller number. // cCharsFit = (UINT)(AnsiPrev(ShortName, p) - ShortName); break; } } if ( '\0' == *p) break; } // // Now draw the text. Note that DrawText does not return a documented // error code, so we don't check. // rect.left = TAGXOFF; rect.top = TAGYOFF; rect.right = TAGXOFF + TAGXSIZ; rect.bottom = TAGYOFF + TAGYSIZ; DrawText(hdc, ShortName, cCharsFit, &rect, DT_CENTER | DT_SINGLELINE | DT_NOPREFIX); DC_EXIT_POINT: // // Perform necessary cleanup. // if (hOldFont) { SelectFont(hdc, hOldFont); } DebugExitVOID(ASShare::CMDrawCursorTag); } // // FUNCTION: CMGetCursorShape // // DESCRIPTION: // // Returns a pointer to a DCCURSORSHAPE structure that defines the bit // definition of the currently displayed cursor. // // A DCCURSORSHAPE structure is OS-specific. The higher level code does // not look at any individual fields in this structure - it just compares // the whole data block with others in the cursor cache. If two // DCCURSORSHAPE structures contain the same the data, then the // corresponding cursors are assumed to be the same. // // The LPCM_SHAPE returned here is passed back into // CMGetColorCursorDetails or CMGetMonoCursorDetails to retrieve the // specific details. // // PARAMETERS: // // ppCursorShape - pointer to a LPCM_SHAPE variable that receives the // pointer to the DCCURSORSHAPE structure // // pcbCursorDataSize - pointer to a UINT variable that receives the size // in bytes of the DCCURSORSHAPE structure // // RETURNS: Success TRUE/FALSE // // BOOL CMGetCursorShape(LPCM_SHAPE * ppCursorShape, LPUINT pcbCursorDataSize ) { LPCM_FAST_DATA lpcmShared; BOOL rc = FALSE; DebugEntry(CMGetCursorShape); lpcmShared = CM_SHM_START_READING; // // Check that a cursor has been written to shared memory - may happen // on start-up before the display driver has written a cursor - or if // the display driver is not working. // if (lpcmShared->cmCursorShapeData.hdr.cBitsPerPel == 0) { TRACE_OUT(( "No cursor in shared memory")); DC_QUIT; } *ppCursorShape = (LPCM_SHAPE)&lpcmShared->cmCursorShapeData; *pcbCursorDataSize = CURSORSHAPE_SIZE(&lpcmShared->cmCursorShapeData); rc = TRUE; DC_EXIT_POINT: CM_SHM_STOP_READING; DebugExitDWORD(CMGetCursorShape, rc); return(rc); } // // FUNCTION: CMGetColorCursorDetails // // DESCRIPTION: // // Returns details of a cursor at 24bpp, given a DCCURSORSHAPE structure. // // PARAMETERS: // // pCursor - pointer to a DCCURSORSHAPE structure from which this function // extracts the details // // pcxWidth - pointer to a TSHR_UINT16 variable that receives the cursor width // in pixels // // pcyHeight - pointer to a TSHR_UINT16 variable that receives the cursor // height in pixels // // pxHotSpot - pointer to a TSHR_UINT16 variable that receives the cursor // hotspot x coordinate // // pyHotSpot - pointer to a TSHR_UINT16 variable that receives the cursor // hotspot y coordinate // // pANDMask - pointer to a buffer that receives the cursor AND mask // // pcbANDMask - pointer to a TSHR_UINT16 variable that receives the size in // bytes of the cursor AND mask // // pXORBitmap - pointer to a buffer that receives the cursor XOR bitmap at // 24bpp // // pcbXORBitmap - pointer to a TSHR_UINT16 variable that receives the size in // bytes of the cursor XOR bitmap // // BOOL ASHost::CMGetColorCursorDetails ( LPCM_SHAPE pCursor, LPTSHR_UINT16 pcxWidth, LPTSHR_UINT16 pcyHeight, LPTSHR_UINT16 pxHotSpot, LPTSHR_UINT16 pyHotSpot, LPBYTE pANDMask, LPTSHR_UINT16 pcbANDMask, LPBYTE pXORBitmap, LPTSHR_UINT16 pcbXORBitmap ) { BOOL rc = FALSE; LPCM_SHAPE_HEADER pCursorHdr; HDC hdcScreen = NULL; HBITMAP hbmp = NULL; UINT cbANDMaskSize; UINT cbXORBitmapSize; HDC hdcTmp = NULL; UINT cbANDMaskRowWidth; UINT cbSrcRowOffset; UINT cbDstRowOffset; UINT y; LPUINT pDestBitmasks; BITMAPINFO_ours bmi; BITMAPINFO_ours srcbmi; HBITMAP oldBitmap; void * pBmBits = NULL; int numColors; int ii; LPCM_FAST_DATA lpcmShared; DebugEntry(ASHost::CMGetColorCursorDetails); if (pCursor == NULL) { DC_QUIT; } pCursorHdr = &(pCursor->hdr); // // Copy the cursor size and hotspot coords. // *pcxWidth = pCursorHdr->cx; *pcyHeight = pCursorHdr->cy; *pxHotSpot = (TSHR_UINT16)pCursorHdr->ptHotSpot.x; *pyHotSpot = (TSHR_UINT16)pCursorHdr->ptHotSpot.y; TRACE_OUT(( "cx(%u) cy(%u) cbWidth %d planes(%u) bpp(%u)", pCursorHdr->cx, pCursorHdr->cy, pCursorHdr->cbRowWidth, pCursorHdr->cPlanes, pCursorHdr->cBitsPerPel )); cbANDMaskSize = CURSOR_AND_MASK_SIZE(pCursor); cbXORBitmapSize = CURSOR_XOR_BITMAP_SIZE(pCursor); // // Copy the AND mask - this is always mono. // // The AND mask is currently in top-down format (the top row of the // bitmap comes first). // // The protocol sends bitmaps in Device Independent format, which is // bottom-up. We therefore have to flip the rows as we copy the mask. // cbANDMaskRowWidth = pCursorHdr->cbRowWidth; cbSrcRowOffset = 0; cbDstRowOffset = cbANDMaskRowWidth * (pCursorHdr->cy-1); for (y = 0; y < pCursorHdr->cy; y++) { memcpy( pANDMask + cbDstRowOffset, pCursor->Masks + cbSrcRowOffset, cbANDMaskRowWidth ); cbSrcRowOffset += cbANDMaskRowWidth; cbDstRowOffset -= cbANDMaskRowWidth; } // // The XOR mask is color and is in DIB format - at 1bpp for mono // cursors, or the display driver bpp. // // We create a bitmap of the same size, set the bits into it and then // get the bits out in 24bpp DIB format. // hdcTmp = CreateCompatibleDC(NULL); if (hdcTmp == NULL) { ERROR_OUT(( "failed to create DC")); DC_QUIT; } // // Setup source bitmap information. // m_pShare->USR_InitDIBitmapHeader((BITMAPINFOHEADER *)&srcbmi, pCursorHdr->cBitsPerPel); srcbmi.bmiHeader.biWidth = pCursorHdr->cx; srcbmi.bmiHeader.biHeight = pCursorHdr->cy; numColors = COLORS_FOR_BPP(pCursorHdr->cBitsPerPel); // // Setup source palette info. // if (pCursorHdr->cBitsPerPel > 8) { // // If the device bpp is > 8, we have to set up the DIB section to // use the same bitmasks as the device. This means setting the // compression type to BI_BITFIELDS and setting the first 3 DWORDS // of the bitmap info color table to be the bitmasks for R, G and B // respectively. // But not for 24bpp. No bitmask or palette are used - it is // always 8,8,8 RGB. // if (pCursorHdr->cBitsPerPel != 24) { TRACE_OUT(( "Copy bitfields")); srcbmi.bmiHeader.biCompression = BI_BITFIELDS; lpcmShared = CM_SHM_START_READING; pDestBitmasks = (LPUINT)(srcbmi.bmiColors); pDestBitmasks[0] = lpcmShared->bitmasks[0]; pDestBitmasks[1] = lpcmShared->bitmasks[1]; pDestBitmasks[2] = lpcmShared->bitmasks[2]; CM_SHM_STOP_READING; } else { TRACE_OUT(( "24bpp cursor: no bitmasks")); } } else { TRACE_OUT(( "Get palette %d", numColors)); lpcmShared = CM_SHM_START_READING; // // Flip the palette - its RGB in the kernel, and needs to be BGR // here. // for (ii = 0; ii < numColors; ii++) { srcbmi.bmiColors[ii].rgbRed = lpcmShared->colorTable[ii].peRed; srcbmi.bmiColors[ii].rgbGreen = lpcmShared->colorTable[ii].peGreen; srcbmi.bmiColors[ii].rgbBlue = lpcmShared->colorTable[ii].peBlue; } CM_SHM_STOP_READING; } // // Create source bitmap and write in the bitmap bits. // hbmp = CreateDIBSection(hdcTmp, (BITMAPINFO *)&srcbmi, DIB_RGB_COLORS, &pBmBits, NULL, 0); if (hbmp == NULL) { ERROR_OUT(( "Failed to create bitmap")); DC_QUIT; } TRACE_OUT(( "Copy %d bytes of data into bitmap 0x%08x", cbXORBitmapSize, pBmBits)); memcpy(pBmBits, pCursor->Masks + cbANDMaskSize, cbXORBitmapSize); // // Set up the structure required by GetDIBits - 24bpp. Set the height // -ve to allow for top-down ordering of the bitmap. // m_pShare->USR_InitDIBitmapHeader((BITMAPINFOHEADER *)&bmi, 24); bmi.bmiHeader.biWidth = pCursorHdr->cx; bmi.bmiHeader.biHeight = -pCursorHdr->cy; if (GetDIBits(hdcTmp, hbmp, 0, pCursorHdr->cy, pXORBitmap, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS) == 0) { ERROR_OUT(( "GetDIBits failed hdc(%x) hbmp(%x) cy(%d)", (TSHR_UINT16)hdcTmp, (TSHR_UINT16)hbmp, pCursorHdr->cy )); DC_QUIT; } *pcbANDMask = (TSHR_UINT16) CURSOR_AND_MASK_SIZE(pCursor); *pcbXORBitmap = (TSHR_UINT16) CURSOR_DIB_BITS_SIZE(pCursor->hdr.cx, pCursor->hdr.cy, 24); // // Return success. // rc = TRUE; DC_EXIT_POINT: // // Clean up before exit. // if (hdcTmp) { DeleteDC(hdcTmp); } if (hbmp != NULL) { DeleteBitmap(hbmp); } DebugExitBOOL(ASHost::CMGetColorCursorDetails, rc); return(rc); } // // FUNCTION: CMGetMonoCursorDetails // // DESCRIPTION: // // Returns details of a cursor at 1bpp, given a DCCURSORSHAPE structure. // // PARAMETERS: // // pCursor - pointer to a DCCURSORSHAPE structure from which this function // extracts the details // // pcxWidth - pointer to a TSHR_UINT16 variable that receives the cursor width // in pixels // // pcyHeight - pointer to a TSHR_UINT16 variable that receives the cursor // height in pixels // // pxHotSpot - pointer to a TSHR_UINT16 variable that receives the cursor // hotspot x coordinate // // pyHotSpot - pointer to a TSHR_UINT16 variable that receives the cursor // hotspot y coordinate // // pANDMask - pointer to a buffer that receives the cursor AND mask // // pcbANDMask - pointer to a TSHR_UINT16 variable that receives the size in // bytes of the cursor AND mask // // pXORBitmap - pointer to a buffer that receives the cursor XOR bitmap at // 1bpp // // pcbXORBitmap - pointer to a TSHR_UINT16 variable that receives the size in // bytes of the cursor XOR bitmap // // BOOL CMGetMonoCursorDetails(LPCM_SHAPE pCursor, LPTSHR_UINT16 pcxWidth, LPTSHR_UINT16 pcyHeight, LPTSHR_UINT16 pxHotSpot, LPTSHR_UINT16 pyHotSpot, LPBYTE pANDMask, LPTSHR_UINT16 pcbANDMask, LPBYTE pXORBitmap, LPTSHR_UINT16 pcbXORBitmap) { BOOL rc = FALSE; LPCM_SHAPE_HEADER pCursorHdr; UINT x; UINT y; LPBYTE pSrcRow; UINT cbDstRowWidth; LPBYTE pDstData; UINT cbSrcANDMaskSize; LPBYTE pSrcXORMask; PFNCMCOPYTOMONO pfnCopyToMono; DebugEntry(CMGetMonoCursor); pCursorHdr = &(pCursor->hdr); TRACE_OUT(( "cx(%u) cy(%u) cbWidth %d planes(%u) bpp(%u)", pCursorHdr->cx, pCursorHdr->cy, pCursorHdr->cbRowWidth, pCursorHdr->cPlanes, pCursorHdr->cBitsPerPel )); // // Copy the cursor size and hotspot coords. // *pcxWidth = pCursorHdr->cx; *pcyHeight = pCursorHdr->cy; *pxHotSpot = (TSHR_UINT16)pCursorHdr->ptHotSpot.x; *pyHotSpot = (TSHR_UINT16)pCursorHdr->ptHotSpot.y; // // Copy the AND mask - this is always mono... // The rows are padded to word (16-bit) boundaries. // pDstData = pANDMask; pSrcRow = pCursor->Masks; cbDstRowWidth = ((pCursorHdr->cx + 15)/16) * 2; for (y = 0; y < pCursorHdr->cy; y++) { for (x = 0; x < cbDstRowWidth; x++) { if (x < pCursorHdr->cbRowWidth) { // // Copy data from the cursor definition. // *pDstData++ = pSrcRow[x]; } else { // // Padding required. // *pDstData++ = 0xFF; } } pSrcRow += pCursorHdr->cbRowWidth; } // // Copy the XOR mask - this may be color. We convert to mono by: // // - turning all zero values into a binary 0 // - turning all non-zero value into a binary 1 // // switch (pCursorHdr->cBitsPerPel) { case 1: TRACE_OUT(( "1bpp")); pfnCopyToMono = CMCopy1bppTo1bpp; break; case 4: TRACE_OUT(( "4bpp")); pfnCopyToMono = CMCopy4bppTo1bpp; break; case 8: TRACE_OUT(( "8bpp")); pfnCopyToMono = CMCopy8bppTo1bpp; break; case 16: TRACE_OUT(( "16bpp")); pfnCopyToMono = CMCopy16bppTo1bpp; break; case 24: TRACE_OUT(( "24bpp")); pfnCopyToMono = CMCopy24bppTo1bpp; break; default: ERROR_OUT(( "Unexpected bpp: %d", pCursorHdr->cBitsPerPel)); DC_QUIT; } cbSrcANDMaskSize = pCursorHdr->cbRowWidth * pCursorHdr->cy; pSrcXORMask = pCursor->Masks + cbSrcANDMaskSize; (*pfnCopyToMono)( pSrcXORMask, pXORBitmap, pCursorHdr->cx, pCursorHdr->cy ); *pcbANDMask = (TSHR_UINT16) (cbDstRowWidth * pCursorHdr->cy); *pcbXORBitmap = (TSHR_UINT16) *pcbANDMask; // // Return success. // rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(CMGetMonoCursor, rc); return(rc); } // // FUNCTION: CMSetCursorTransform // // DESCRIPTION: // // This function is responsible for setting cursor transforms. // // PARAMETERS: // // cWidth - the width in pels of the AND mask and the XOR DIB // cHeight - the height in pels of the AND mask and the XOR DIB // pOrigANDMask - a pointer to the bits of a WORD padded AND mask (the // bits are top-down) // pOrigXORDIB - a pointer to a DIB of the size given by cWidth and // cHeight. // // BOOL ASHost::CMSetCursorTransform ( LPBYTE pOrigANDMask, LPBITMAPINFO pOrigXORDIB ) { BOOL rc = FALSE; LPBYTE pBits = NULL; UINT cbSize; CM_DRV_XFORM_INFO drvXformInfo; UINT srcRowLength; DebugEntry(ASHost::CMSetCursorTransform); // // The transform should be monochrome // ASSERT(pOrigXORDIB->bmiHeader.biBitCount == 1); // // For mono tags, create a single 1bpp DIB with AND followed by XOR // data. Since both the AND mask and the XOR bitmap are word // aligned we need to know the word aligned row length for // allocating memory. // // // Calculate the source and destination row lengths (in bytes). // srcRowLength = ((m_pShare->m_cmCursorWidth + 15)/16) * 2; cbSize = srcRowLength * m_pShare->m_cmCursorHeight; pBits = new BYTE[cbSize * 2]; if (!pBits) { ERROR_OUT(( "Alloc %lu bytes failed", cbSize * 2)); DC_QUIT; } // // Copy the packed 1bpp AND and XOR bits to the buffer // TRACE_OUT(( "Copy %d bytes from 0x%08x", cbSize, pOrigANDMask)); // // Copy the AND and XOR 1bpp masks. // memcpy(pBits, pOrigANDMask, cbSize); memcpy(pBits + cbSize, POINTER_TO_DIB_BITS(pOrigXORDIB), cbSize); // // Call the display driver to set the pointer transform. // drvXformInfo.width = m_pShare->m_cmCursorWidth; drvXformInfo.height = m_pShare->m_cmCursorHeight; drvXformInfo.pANDMask = pBits; drvXformInfo.result = FALSE; if (!OSI_FunctionRequest(CM_ESC_XFORM, (LPOSI_ESCAPE_HEADER)&drvXformInfo, sizeof(drvXformInfo)) || !drvXformInfo.result) { ERROR_OUT(("CM_ESC_XFORM failed")); DC_QUIT; } // // Set flag inidicating that transform is applied. // m_cmfCursorTransformApplied = TRUE; rc = TRUE; DC_EXIT_POINT: // // Release allocated memory, bitmaps, DCs. // if (pBits) { delete[] pBits; } DebugExitBOOL(ASHost::CMSetCursorTransform, rc); return(rc); } // // FUNCTION: CMRemoveCursorTransform // // DESCRIPTION: // This function is responsible for removing cursor transforms. // // PARAMETERS: None. // void ASHost::CMRemoveCursorTransform(void) { DebugEntry(ASHost::CMRemoveCursorTransform); // // Check to see if there is currently a transform applied. // if (m_cmfCursorTransformApplied) { CM_DRV_XFORM_INFO drvXformInfo; // // Call down to the display driver to remove the pointer tag. // drvXformInfo.pANDMask = NULL; drvXformInfo.result = FALSE; OSI_FunctionRequest(CM_ESC_XFORM, (LPOSI_ESCAPE_HEADER)&drvXformInfo, sizeof(drvXformInfo)); m_cmfCursorTransformApplied = FALSE; } DebugExitVOID(ASHost::CMRemoveCursorTransform); } // // FUNCTION: CMProcessCursorIDPacket // // DESCRIPTION: // // Processes a received cursor ID packet. // // PARAMETERS: // // pCMPacket - pointer to the received cursor ID packet // // phNewCursor - pointer to a HCURSOR variable that receives the handle // of a cursor that corresponds to the received packet // // pNewHotSpot - pointer to a POINT variable that receives the hot-spot // of the new cursor // // RETURNS: Nothing // // void ASShare::CMProcessCursorIDPacket ( PCMPACKETID pCMPacket, HCURSOR* phNewCursor, LPPOINT pNewHotSpot ) { DebugEntry(ASShare::CMProcessCursorIDPacket); // // We only support NULL and ARROW // // // If the IDC is not NULL then load the cursor. // if (pCMPacket->idc != CM_IDC_NULL) { if (pCMPacket->idc != CM_IDC_ARROW) { WARNING_OUT(("ProcessCursorIDPacket: unrecognized ID, using arrow")); } *phNewCursor = m_cmArrowCursor; *pNewHotSpot = m_cmArrowCursorHotSpot; } else { // NULL is used for hidden cursors *phNewCursor = NULL; pNewHotSpot->x = 0; pNewHotSpot->y = 0; } DebugExitVOID(ASShare::CMProcessCursorIDPacket); } // // CM_Controlled() // // Called when we start/stop being controlled. // extern CURTAGINFO g_cti; void ASHost::CM_Controlled(ASPerson * pasController) { char szAbbreviatedName[128]; DebugEntry(ASHost::CM_Controlled); // // If we are not being controlled, turn off the cursor tag. Note that // being detached means we aren't controlled. // if (!pasController) { // We're not being controlled by a remote. No cursor xform CMRemoveCursorTransform(); } else { BOOL fAbbreviated = CMCreateAbbreviatedName(pasController->scName, szAbbreviatedName, sizeof(szAbbreviatedName)); if ( !fAbbreviated ) { lstrcpyn(szAbbreviatedName, pasController->scName, ARRAY_ELEMENTS(szAbbreviatedName)); } if (!CMGetCursorTagInfo(szAbbreviatedName)) { ERROR_OUT(("GetCurTagInfo failed, not setting cursor tag")); } else { CMSetCursorTransform(&g_cti.aAndBits[0], &g_cti.bmInfo); } } DebugExitVOID(ASHost::CM_Controlled); } // This initializes our single, volatile data for // creating cursor tags. CURTAGINFO g_cti = { 32, // height of masks 32, // width of masks // bits describing the AND mask, this is a 12x24 rectangle in lower right // if the tag size is changed, the mask will have to be edited, the // following helps draw attention to this #if ( TAGXOFF != 8 || TAGYOFF != 20 || TAGXSIZ != 24 || TAGYSIZ != 12 ) #error "Bitmap mask may be incorrect" #endif { 0xff, 0xff, 0xff, 0xff, // line 1 0xff, 0xff, 0xff, 0xff, // line 2 0xff, 0xff, 0xff, 0xff, // line 3 0xff, 0xff, 0xff, 0xff, // line 4 0xff, 0xff, 0xff, 0xff, // line 5 0xff, 0xff, 0xff, 0xff, // line 6 0xff, 0xff, 0xff, 0xff, // line 7 0xff, 0xff, 0xff, 0xff, // line 8 0xff, 0xff, 0xff, 0xff, // line 9 0xff, 0xff, 0xff, 0xff, // line 10 0xff, 0xff, 0xff, 0xff, // line 11 0xff, 0xff, 0xff, 0xff, // line 12 0xff, 0xff, 0xff, 0xff, // line 13 0xff, 0xff, 0xff, 0xff, // line 14 0xff, 0xff, 0xff, 0xff, // line 15 0xff, 0xff, 0xff, 0xff, // line 16 0xff, 0xff, 0xff, 0xff, // line 17 0xff, 0xff, 0xff, 0xff, // line 18 0xff, 0xff, 0xff, 0xff, // line 19 0xff, 0xff, 0xff, 0xff, // line 20 0xff, 0x00, 0x00, 0x00, // line 21 0xff, 0x00, 0x00, 0x00, // line 22 0xff, 0x00, 0x00, 0x00, // line 23 0xff, 0x00, 0x00, 0x00, // line 24 0xff, 0x00, 0x00, 0x00, // line 25 0xff, 0x00, 0x00, 0x00, // line 26 0xff, 0x00, 0x00, 0x00, // line 27 0xff, 0x00, 0x00, 0x00, // line 28 0xff, 0x00, 0x00, 0x00, // line 29 0xff, 0x00, 0x00, 0x00, // line 30 0xff, 0x00, 0x00, 0x00, // line 31 0xff, 0x00, 0x00, 0x00 // line 32 }, // Initialize the BITMAPINFO structure: { // Initialize the BITMAPINFOHEADER structure: { sizeof(BITMAPINFOHEADER), 32, // width -32, // height (top down bitmap) 1, // planes 1, // bits per pixel BI_RGB, // compression format (none) 0, // not used for uncompressed bitmaps 0, // xpels per meter, not set 0, // ypels per meter, not set 0, // biClrsUsed, indicates 2 color entries follow this struct 0 // biClrsImportant (all) }, // Initialize the foreground color (part of BITMAPINFO struct) // This is BLACK { 0x0, 0x0, 0x0, 0x0 }, }, // Initialize the background color (part of single RGBQUAD struct following // BITMAPINFO STRUCTURE { 0xff, 0xff, 0xff, 0x00 }, // Because this is a packed bitmap, the bitmap bits follow: // These will be written into dynamically to create the tag { 0, } }; // // This function isn't DBCS safe, so we don't abbreviate in // DBCS character sets // BOOL ASShare::CMCreateAbbreviatedName ( LPCSTR szTagName, LPSTR szBuf, UINT cbBuf ) { BOOL rc = FALSE; DebugEntry(ASShare::CMCreateAbbreviatedName); if (GetSystemMetrics(SM_DBCSENABLED)) { TRACE_OUT(("Do not attempt to abbreviate on DBCS system")); DC_QUIT; } // We will try to create initials first LPSTR p; if ( NULL != (p = (LPSTR) _StrChr ( szTagName, ' ' ))) { // Enough room for initials? if (cbBuf < NTRUNCLETTERS) { TRACE_OUT(("CMCreateAbbreviatedName: not enough room for initials")); DC_QUIT; } char * q = szBuf; *q++ = *szTagName; *q++ = '.'; *q++ = *(p+1); *q++ = '.'; *q = '\0'; CharUpper ( q ); rc = TRUE; } DC_EXIT_POINT: DebugExitBOOL(ASShare::CMCreateAbbreviatedName, rc); return(rc); } // This function will create the appropriate data in the // volatile global and return a pointer to it. BOOL ASHost::CMGetCursorTagInfo(LPCSTR szTagName) { HDC hdc = NULL; HDC hdcScratch = NULL; HBITMAP hBmpOld = NULL; HBITMAP hBitmap = NULL; PCURTAGINFO pctiRet = NULL; RECT rect; HFONT hOldFont; BOOL rc = FALSE; DebugEntry(ASHost::CMGetCursorTagInfo); hdcScratch = CreateCompatibleDC(NULL); if (!hdcScratch) { ERROR_OUT(("CMGetCursorTagInfo: couldn't get scratch DC")); DC_QUIT; } hBitmap = CreateDIBitmap(hdcScratch, &(g_cti.bmInfo.bmiHeader), 0, // don't initialize bits NULL, // don't initialize bits &(g_cti.bmInfo), DIB_RGB_COLORS ); if (!hBitmap) { ERROR_OUT(("CMGetCursorTagInfo: failed to create bitmap")); DC_QUIT; } hBmpOld = SelectBitmap(hdcScratch, hBitmap); hOldFont = SelectFont(hdcScratch, m_pShare->m_cmCursorTagFont); // Create the tag background... PatBlt ( hdcScratch, 0, 0, 32, 32, BLACKNESS ); PatBlt ( hdcScratch, TAGXOFF, TAGYOFF, TAGXSIZ, TAGYSIZ, WHITENESS ); // Now see how many characters of the name or abbreviation // we can fit into the tag int cCharsFit; SIZE size; LPSTR p; // First assume the whole thing fits cCharsFit = lstrlen(szTagName); // Now try to find out how big a part actually fits rect.left = rect.top = rect.right = rect.bottom = 0; for ( p = CharNext(szTagName); ; p = CharNext(p) ) { if ( DrawText(hdcScratch, szTagName, (int)(p - szTagName), &rect, DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX ) ) { if ( rect.right > TAGXSIZ ) { // This number of characters no longer fits into the // tag. Take the next smaller number and leave the loop cCharsFit = (int)(CharPrev(szTagName, p) - szTagName); break; } } if ( NULL == *p ) break; } TRACE_OUT(("Tag: [%s], showing %d chars", szTagName, cCharsFit )); // Now draw the text... // DrawText doesn't return a documented error... rect.top = TAGYOFF; rect.left = TAGXOFF; rect.bottom = TAGYOFF + TAGYSIZ; rect.right = TAGXOFF + TAGXSIZ; DrawText ( hdcScratch, szTagName, cCharsFit, &rect, DT_CENTER | DT_SINGLELINE | DT_NOPREFIX ); SelectFont (hdcScratch, hOldFont); // Now get the bitmap bits into the global volatile data area // Make sure the number of scan lines requested is returned if ( 32 != GetDIBits ( hdcScratch, hBitmap, 0, 32, g_cti.aXorBits, &(g_cti.bmInfo), DIB_RGB_COLORS )) { ERROR_OUT(("CMGetCursorTagInfo: GetDIBits failed")); DC_QUIT; } // Reset the foreground and background colors to black // and white respectively no matter what GetDIBits has filled in. // REVIEW: how do we get GetDIBits to fill in the expected (B&W) color // table? g_cti.bmInfo.bmiColors[0].rgbBlue = 0x0; g_cti.bmInfo.bmiColors[0].rgbGreen = 0x0; g_cti.bmInfo.bmiColors[0].rgbRed = 0x0; g_cti.bmInfo.bmiColors[0].rgbReserved = 0; g_cti.rgbBackground[0].rgbBlue = 0xff; g_cti.rgbBackground[0].rgbGreen = 0xff; g_cti.rgbBackground[0].rgbRed = 0xff; g_cti.rgbBackground[0].rgbReserved = 0; // Finally, we are happy rc = TRUE; DC_EXIT_POINT: // Perform necessary cleanup if (hBmpOld) SelectBitmap ( hdcScratch, hBmpOld); if ( hBitmap ) DeleteBitmap ( hBitmap ); if ( hdcScratch ) DeleteDC ( hdcScratch ); DebugExitBOOL(ASHost::CMGetCursorTagInfo, rc); return(rc); }