#include "precomp.h" // // USR.CPP // Update Sender/Receiver // // Copyright(c) Microsoft 1997- // #define MLZ_FILE_ZONE ZONE_NET // // USR strategy when network packets cannot be allocated. // // The USR sends three different types of packets: // // - font negotiation packets // - order packets // - screen data packets // // Font negotiation packets are sent by the USR_Periodic function. If the // packet cannot be sent first time then the USR will retry (on each call // to the USR_Periodic function) until it has succesfully sent the packet. // The only dependency on font packets is that until the systems in a share // have been able to exchange font negotiation packets they will not be // able to send text output as orders - they will simply send text as // screen data. // // The USR function UP_SendUpdates sends all update packets (both order // packets and screen data packets). Order packets must be sent first and // screen data packets are only sent if all the orders have been // succesfully sent. When sending screen data packets they are only sent // if the corresponding palette packets have been sent - otherwise they are // re-absorbed into the screen data to be transmitted later. // // // // USR_ShareStarting() // Creates share resources // BOOL ASShare::USR_ShareStarting(void) { BOOL rc = FALSE; BITMAPINFOHEADER bitmapInfo; HDC hdcDesktop = NULL; DebugEntry(ASShare::USR_ShareStarting); // // Set the black bitmap data and hatch bitmap data flags which can be // used as an aid for debugging. These are false unless there is an // entry in the ini file to override them. // COM_ReadProfInt(DBG_INI_SECTION_NAME, USR_INI_HATCHSCREENDATA, FALSE, &m_usrHatchScreenData); COM_ReadProfInt(DBG_INI_SECTION_NAME, USR_INI_HATCHBMPORDERS, FALSE, &m_usrHatchBitmaps); // // Double-check the order packet sizes are OK // ASSERT(SMALL_ORDER_PACKET_SIZE < LARGE_ORDER_PACKET_SIZE); ASSERT(LARGE_ORDER_PACKET_SIZE <= TSHR_MAX_SEND_PKT); // // Allocate a chunk of memory big enough to contain the largest packet // an application can receive from the network. This is required to // store uncompressed bitmaps and repeated general use by the USR. // m_usrPBitmapBuffer = new BYTE[TSHR_MAX_SEND_PKT]; if (!m_usrPBitmapBuffer) { ERROR_OUT(("USR_ShareStarted: failed to alloc memory m_usrPBitmapBuffer")); // // To continue the share would cause a GP fault as soon as anything // tries to use this buffer so delete this person from the share. // The reason is lack of resources. // DC_QUIT; } // // Create the transfer bitmaps for screen data and bitmap orders // USR_InitDIBitmapHeader(&bitmapInfo, g_usrScreenBPP); // // Create the transfer bitmaps. These are used for both outgoing and // incoming data. // // To avoid having to recreate the bitmaps whenever the parties in the // share change, (and hence the various bpp may change) from r2.0 we // now use a fixed vertical size and if necessary can handle incoming // bitmaps in multiple bands. // // These are the resulting heights for 256 pixel wide segments. // // TSHR_MAX_SEND_PKT - sizeof(DATAPACKETHEADER) / bytes per scan line // // 4bpp --> (32000 - 4) / 128 = 249 // 8bpp --> (32000 - 4) / 256 = 124 // 24bpp --> (32000 - 4) / 768 = 41 // // // // NOTE: // The VGA driver has a problem when the bitmap ends exactly on a 4K // (page) boundary. So we create the bitmaps one pixel taller. // // BOGUS BUGBUG LAURABU // Is this really true anymore? If not, save some memory and make these // the right size. // hdcDesktop = GetDC(NULL); if (!hdcDesktop) { ERROR_OUT(("USR_ShareStarting: can't get screen DC")); DC_QUIT; } // The large bitmap is short. The rest are medium height. bitmapInfo.biWidth = 1024; bitmapInfo.biHeight = MaxBitmapHeight(MEGA_WIDE_X_SIZE, 8) + 1; m_usrBmp1024 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp1024) { ERROR_OUT(("USR_ShareStarting: failed to reate m_usrBmp1024")); DC_QUIT; } bitmapInfo.biHeight = MaxBitmapHeight(MEGA_X_SIZE, 8) + 1; bitmapInfo.biWidth = 256; m_usrBmp256 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp256) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp256")); DC_QUIT; } bitmapInfo.biWidth = 128; m_usrBmp128 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp128) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp128")); DC_QUIT; } bitmapInfo.biWidth = 112; m_usrBmp112 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp112) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp112")); DC_QUIT; } bitmapInfo.biWidth = 96; m_usrBmp96 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp96) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp96")); DC_QUIT; } bitmapInfo.biWidth = 80; m_usrBmp80 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp80) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp80")); DC_QUIT; } bitmapInfo.biWidth = 64; m_usrBmp64 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp64) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp64")); DC_QUIT; } bitmapInfo.biWidth = 48; m_usrBmp48 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp48) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp48")); DC_QUIT; } bitmapInfo.biWidth = 32; m_usrBmp32 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp32) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp32")); DC_QUIT; } bitmapInfo.biWidth = 16; m_usrBmp16 = CreateDIBitmap(hdcDesktop, &bitmapInfo, 0, NULL, NULL, DIB_RGB_COLORS); if (!m_usrBmp16) { ERROR_OUT(("USR_ShareStarting: failed to create m_usrBmp16")); DC_QUIT; } rc = TRUE; DC_EXIT_POINT: if (hdcDesktop) { ReleaseDC(NULL, hdcDesktop); } DebugExitBOOL(ASShare::USR_ShareStarting, rc); return(rc); } // // USR_ShareEnded() // Cleans up share resources // void ASShare::USR_ShareEnded(void) { DebugEntry(ASShare::USR_ShareEnded); // // Delete Transfer Bitmaps. // if (m_usrBmp1024) { DeleteBitmap(m_usrBmp1024); m_usrBmp1024= NULL; } if (m_usrBmp256) { DeleteBitmap(m_usrBmp256); m_usrBmp256 = NULL; } if (m_usrBmp128) { DeleteBitmap(m_usrBmp128); m_usrBmp128 = NULL; } if (m_usrBmp112) { DeleteBitmap(m_usrBmp112); m_usrBmp112 = NULL; } if (m_usrBmp96) { DeleteBitmap(m_usrBmp96); m_usrBmp96 = NULL; } if (m_usrBmp80) { DeleteBitmap(m_usrBmp80); m_usrBmp80 = NULL; } if (m_usrBmp64) { DeleteBitmap(m_usrBmp64); m_usrBmp64 = NULL; } if (m_usrBmp48) { DeleteBitmap(m_usrBmp48); m_usrBmp48 = NULL; } if (m_usrBmp32) { DeleteBitmap(m_usrBmp32); m_usrBmp32 = NULL; } if (m_usrBmp16) { DeleteBitmap(m_usrBmp16); m_usrBmp16 = NULL; } // // Free Bitmap Buffer. // if (m_usrPBitmapBuffer != NULL) { delete[] m_usrPBitmapBuffer; m_usrPBitmapBuffer = NULL; } DebugExitVOID(ASShare::USR_ShareEnded); } // // USR_RecalcCaps() // // DESCRIPTION: // // Enumerates the bitmap capabilities of all parties currently in the // share, and determines the common capabilities. // // PARAMETERS: None. // // RETURNS: TRUE if there are good common caps, or false on failure (which // has the effect of rejecting a new party from joining the share). // // void ASShare::USR_RecalcCaps(BOOL fJoiner) { ASPerson * pasT; UINT capsMaxBPP; UINT capsMinBPP; UINT capsSupports4BPP; UINT capsSupports8BPP; UINT capsSupports24BPP; UINT capsOldBPP; DebugEntry(ASShare::USR_RecalcCaps); if (!m_pHost) { // Nothing to do DC_QUIT; } ValidatePerson(m_pasLocal); capsOldBPP = m_pHost->m_usrSendingBPP; // // Init the caps // capsSupports4BPP = m_pasLocal->cpcCaps.screen.capsSupports4BPP; capsSupports8BPP = m_pasLocal->cpcCaps.screen.capsSupports8BPP; capsSupports24BPP = m_pasLocal->cpcCaps.screen.capsSupports24BPP; capsMaxBPP = 0; capsMinBPP = 0xFFFFFFFF; for (pasT = m_pasLocal->pasNext; pasT != NULL; pasT = pasT->pasNext) { // // Check the bpps supported. // if (pasT->cpcCaps.screen.capsSupports4BPP != CAPS_SUPPORTED) { capsSupports4BPP = CAPS_UNSUPPORTED; } if (pasT->cpcCaps.screen.capsSupports8BPP != CAPS_SUPPORTED) { capsSupports8BPP = CAPS_UNSUPPORTED; } if (pasT->cpcCaps.screen.capsSupports24BPP != CAPS_SUPPORTED) { capsSupports24BPP = CAPS_UNSUPPORTED; } // // Set the combined bpp to the maximum so far found. // (If we send data at this bpp then one of the remote systems can // usefully process this number of colors). // capsMaxBPP = max(capsMaxBPP, pasT->cpcCaps.screen.capsBPP); capsMinBPP = min(capsMinBPP, pasT->cpcCaps.screen.capsBPP); } // // Now figure out what BPP we will transmit at. // // // Limit the combined caps bpp (which is currently the maximum bpp that // any system in the share wants) to the local bpp, since there is no // point sending at higher bpp than the local machine has. // capsMaxBPP = min(capsMaxBPP, g_usrScreenBPP); if (!capsMaxBPP) capsMaxBPP = g_usrScreenBPP; capsMinBPP = min(capsMinBPP, g_usrScreenBPP); // // m_usrSendingBPP is most often going to be 8. So it's easier to assume // it, then check for cases where it won't be. // m_pHost->m_usrSendingBPP = 8; if ((capsMaxBPP <= 4) && (capsSupports4BPP == CAPS_SUPPORTED)) { m_pHost->m_usrSendingBPP = 4; } else if ((capsMinBPP >= 24) && (g_asSettings & SHP_SETTING_TRUECOLOR) && (capsSupports24BPP == CAPS_SUPPORTED)) { m_pHost->m_usrSendingBPP = 24; } if (capsOldBPP != m_pHost->m_usrSendingBPP) { // // If switching to/from palettized, we need to update the // "need to send palette" flag. Note that 4bpp is also a // palettized color depth. // if ((capsOldBPP <= 8) && (m_pHost->m_usrSendingBPP > 8)) m_pHost->m_pmMustSendPalette = FALSE; else if ((capsOldBPP > 8) && (m_pHost->m_usrSendingBPP <= 8)) m_pHost->m_pmMustSendPalette = TRUE; #ifdef _DEBUG if (capsOldBPP == 24) { WARNING_OUT(("TRUE COLOR SHARING is now FINISHED")); } else if (m_pHost->m_usrSendingBPP == 24) { WARNING_OUT(("TRUE COLOR SHARING is now STARTING")); } #endif if (!fJoiner) { // // Sending BPP changed. Repaint all shared stuff. // NOTE: // We recalc the sendBPP at three points: // * When we start to share // * When a person joins // * When a person leaves // // In the first two cases, shared stuff is repainted, // so everybody gets the new sendBPP data. Only in the // leave case do we need to force this. // m_pHost->HET_RepaintAll(); } } DC_EXIT_POINT: DebugExitVOID(ASShare::USR_RecalcCaps); } // // USR_HostStarting() // BOOL ASHost::USR_HostStarting(void) { BOOL rc = FALSE; HDC hdc; DebugEntry(ASHost::USR_HostStarting); // // Create scratch DC // hdc = GetDC(NULL); if (!hdc) { ERROR_OUT(("USR_HostStarting: can't get screen DC")); DC_QUIT; } m_usrWorkDC = CreateCompatibleDC(hdc); ReleaseDC(NULL, hdc); if (!m_usrWorkDC) { ERROR_OUT(("USR_HostStarting: can't create m_usrWorkDC")); DC_QUIT; } m_pShare->USR_RecalcCaps(TRUE); rc = TRUE; DC_EXIT_POINT: DebugExitBOOL(ASHost::USR_HostStarting, rc); return(rc); } // // USR_HostEnded() // void ASHost::USR_HostEnded(void) { DebugEntry(ASHost::USR_HostEnded); if (m_usrWorkDC != NULL) { DeleteDC(m_usrWorkDC); m_usrWorkDC = NULL; } DebugExitVOID(ASHost::USR_HostEnded); } // // USR_ScrollDesktop // void ASShare::USR_ScrollDesktop ( ASPerson * pasPerson, int xNew, int yNew ) { int xOld; int yOld; DebugEntry(ASShare::USR_ScrollDesktop); ValidateView(pasPerson); // // If the origin has changed then do the update. // xOld = pasPerson->m_pView->m_dsScreenOrigin.x; yOld = pasPerson->m_pView->m_dsScreenOrigin.y; if ((xOld != xNew) || (yOld != yNew)) { pasPerson->m_pView->m_dsScreenOrigin.x = xNew; pasPerson->m_pView->m_dsScreenOrigin.y = yNew; // // We must ensure that data written to the ScreenBitmap is not // clipped // OD_ResetRectRegion(pasPerson); // // Offset the existing bitmap by the change in desktop origins. // BitBlt(pasPerson->m_pView->m_usrDC, 0, 0, pasPerson->cpcCaps.screen.capsScreenWidth, pasPerson->cpcCaps.screen.capsScreenHeight, pasPerson->m_pView->m_usrDC, xNew - xOld, yNew - yOld, SRCCOPY); // // Offset the shadow cursor pos -- same place on remote screen // but now different place in VD // pasPerson->cmPos.x += xNew - xOld; pasPerson->cmPos.y += yNew - yOld; // // Repaint the view // VIEW_InvalidateRgn(pasPerson, NULL); } DebugExitVOID(ASShare::USR_ScrollDesktop); } // // FUNCTION: USR_InitDIBitmapHeader // // DESCRIPTION: // // Initialises a Device Independent bitmap header to be the given bits per // pel. // // PARAMETERS: // // pbh - pointer to the bitmap header to be initialised. // bpp - bpp to be used for the bitmap // // RETURNS: VOID // // void ASShare::USR_InitDIBitmapHeader ( BITMAPINFOHEADER * pbh, UINT bpp ) { DebugEntry(ASShare::USR_InitDIBitmapHeader); pbh->biSize = sizeof(BITMAPINFOHEADER); pbh->biPlanes = 1; pbh->biBitCount = (WORD)bpp; pbh->biCompression = BI_RGB; pbh->biSizeImage = 0; pbh->biXPelsPerMeter = 10000; pbh->biYPelsPerMeter = 10000; pbh->biClrUsed = 0; pbh->biClrImportant = 0; DebugExitVOID(ASShare::USR_InitDIBitmapHeader); } // // USR_ViewStarting() // // Called when someone we're viewing starts to host. We create the desktop // bitmap for them plus scratch objects // BOOL ASShare::USR_ViewStarting(ASPerson * pasPerson) { BOOL rc; DebugEntry(ASShare::USR_ViewStarting); ValidateView(pasPerson); // // Create a bitmap for this new party // rc = USRCreateRemoteDesktop(pasPerson); DebugExitBOOL(ASShare::USR_ViewStarting, rc); return(rc); } // // FUNCTION: USRCreateRemoteDesktop // // DESCRIPTION: // // Creates the shadow bitmap for a remote party. // // PARAMETERS: // // personID - person to create the shadow bitmap for. // // RETURNS: TRUE if successful, FALSE otherwise. // // BOOL ASShare::USRCreateRemoteDesktop(ASPerson * pasPerson) { BOOL rc = FALSE; HDC hdcDesktop = NULL; RECT desktopRect; DebugEntry(ASShare::USRCreateRemoteDesktop); ValidateView(pasPerson); ASSERT(pasPerson->m_pView->m_usrDC == NULL); ASSERT(pasPerson->m_pView->m_usrBitmap == NULL); ASSERT(pasPerson->m_pView->m_usrOldBitmap == NULL); hdcDesktop = GetDC(NULL); // // Create the scratch DC // pasPerson->m_pView->m_usrWorkDC = CreateCompatibleDC(hdcDesktop); if (!pasPerson->m_pView->m_usrWorkDC) { ERROR_OUT(("Couldn't create workDC for person [%d]", pasPerson->mcsID)); DC_QUIT; } // // Create the DC that keeps the screen bitmap for this party // pasPerson->m_pView->m_usrDC = CreateCompatibleDC(hdcDesktop); if (!pasPerson->m_pView->m_usrDC) { ERROR_OUT(("Couldn't create usrDC for person [%d]", pasPerson->mcsID)); DC_QUIT; } // // We can't use this person's usrDC, since that currently has a MONO // bitmap selected into it. // pasPerson->m_pView->m_usrBitmap = CreateCompatibleBitmap(hdcDesktop, pasPerson->cpcCaps.screen.capsScreenWidth, pasPerson->cpcCaps.screen.capsScreenHeight); if (pasPerson->m_pView->m_usrBitmap == NULL) { ERROR_OUT(("Couldn't create screen bitmap for [%d]", pasPerson->mcsID)); DC_QUIT; } // // Select the screen bitmap into the person's DC, and save the previous // 1x1 bitmap away, so we can deselect it when done. // pasPerson->m_pView->m_usrOldBitmap = SelectBitmap(pasPerson->m_pView->m_usrDC, pasPerson->m_pView->m_usrBitmap); // // Fill the Screen Bitmap with grey. // // In practice the Shadow Window Presenter(SWP) should never display // any area of the Screen Bitmap that has not been updated with data // from a remote system. // // Therefore this operation is just "insurance" in case the SWP goes // wrong and momentarily displays a non-updated area - a flash of grey // is better than a flash of garbage. // desktopRect.left = 0; desktopRect.top = 0; desktopRect.right = pasPerson->cpcCaps.screen.capsScreenWidth; desktopRect.bottom = pasPerson->cpcCaps.screen.capsScreenHeight; FillRect(pasPerson->m_pView->m_usrDC, &desktopRect, GetSysColorBrush(COLOR_APPWORKSPACE)); rc = TRUE; DC_EXIT_POINT: if (hdcDesktop != NULL) { ReleaseDC(NULL, hdcDesktop); } DebugExitBOOL(ASShare::USRCreateRemoteDesktop, rc); return(rc); } // // USR_ViewEnded() // // Called when person we're viewing stops hosting. We get rid of their // desktop bitmap. // void ASShare::USR_ViewEnded(ASPerson * pasPerson) { ValidateView(pasPerson); // // Delete the desktop bitmap for the party that has left // USRDeleteRemoteDesktop(pasPerson); } // // FUNCTION: USRDeleteRemoteDesktop // // DESCRIPTION: // // Deletes a remote party's shadow bitmap. // // PARAMETERS: // // personID - party whose shadow bitmap is to be deleted. // // RETURNS: Nothing. // // void ASShare::USRDeleteRemoteDesktop(ASPerson * pasPerson) { DebugEntry(ASShare::USRDeleteRemoteDesktop); ValidateView(pasPerson); if (pasPerson->m_pView->m_usrOldBitmap != NULL) { // Deselect screen bitmap SelectBitmap(pasPerson->m_pView->m_usrDC, pasPerson->m_pView->m_usrOldBitmap); pasPerson->m_pView->m_usrOldBitmap = NULL; } if (pasPerson->m_pView->m_usrBitmap != NULL) { // Delete the screen bitmap DeleteBitmap(pasPerson->m_pView->m_usrBitmap); pasPerson->m_pView->m_usrBitmap = NULL; } if (pasPerson->m_pView->m_usrDC != NULL) { // // Delete the screen DC. Created objects should have // been selected out of it before now. // DeleteDC(pasPerson->m_pView->m_usrDC); pasPerson->m_pView->m_usrDC = NULL; } if (pasPerson->m_pView->m_usrWorkDC != NULL) { DeleteDC(pasPerson->m_pView->m_usrWorkDC); pasPerson->m_pView->m_usrWorkDC = NULL; } DebugExitVOID(ASShare::USRDeleteRemoteDesktop); } // // This function is a mess! First because it ought to be an FH API // function, and secondly because it mixes portable code and Windows API // calls. The details of what is to be done with it are deferred until the // UNIX port of FH is designed, though. STOPPRESS! Function replaced by new // FH_CreateAndSelectFont, which combines old USR_UseFont and // FH_CreateAndSelectFont - you have to write an NT version. // // // USR_UseFont() // BOOL ASShare::USR_UseFont ( HDC surface, HFONT* pHFont, TEXTMETRIC* pFontMetrics, LPSTR pName, UINT codePage, UINT MaxHeight, UINT Height, UINT Width, UINT Weight, UINT flags ) { BOOL rc = FALSE; HFONT hNewFont; HFONT hOldFont; DebugEntry(ASShare::USR_UseFont); rc = FH_CreateAndSelectFont(surface, &hNewFont, &hOldFont, pName, codePage, MaxHeight, Height, Width, Weight, flags); if (rc == FALSE) { // // Failed to create or select the font. // DC_QUIT; } // // Select in the new font which ensures that the old one is deselected. // // NB. We do not delete the font we are deselecting, rather the old // one that was passed to us. This is beacuse multiple components use // "surface", and so the deselected font may not be the current // component's last font at all - the important thing is that by // selecting in the new font we are ensuring that the old font is not // the selected one. // SelectFont(surface, hNewFont); if (*pHFont) { DeleteFont(*pHFont); } // // If a pointer to font metrics was passed in then we need to query // the metrics now. // if (pFontMetrics) GetTextMetrics(surface, pFontMetrics); // // Update the record of the last font we selected. // *pHFont = hNewFont; rc = TRUE; DC_EXIT_POINT: DebugExitDWORD(ASShare::USR_UseFont, rc); return(rc); } // // USR_ScreenChanged() // void ASShare::USR_ScreenChanged(ASPerson * pasPerson) { DebugEntry(ASShare::USR_ScreenChanged); ValidatePerson(pasPerson); pasPerson->cpcCaps.screen.capsScreenWidth = pasPerson->cpcCaps.screen.capsScreenWidth; pasPerson->cpcCaps.screen.capsScreenHeight = pasPerson->cpcCaps.screen.capsScreenHeight; if (pasPerson->m_pView) { // // Recreate screen bitmap // // // Discard the remote users current shadow bitmap // USRDeleteRemoteDesktop(pasPerson); // // Create a new shadow bitmap for remote user that is of the new size // USRCreateRemoteDesktop(pasPerson); } VIEW_ScreenChanged(pasPerson); DebugExitVOID(ASShare::USR_ScreenChanged); }