|
|
#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); }
|