// File: videview.cpp #include "precomp.h" #include "vidview.h" #include "resource.h" #include "confman.h" #include "rtoolbar.h" #include "pfndrawd.h" #include "NmManager.h" #include "cmd.h" #define DibHdrSize(lpbi) ((lpbi)->biSize + (int)(lpbi)->biClrUsed * sizeof(RGBQUAD)) #define DibDataSize(lpbi) ((lpbi)->biSizeImage) #define DibSize(lpbi) (DibHdrSize(lpbi) + DibDataSize(lpbi)) typedef struct { DWORD *pdwCapDevIDs; LPTSTR pszCapDevNames; DWORD dwNumCapDev; } ENUM_CAP_DEV; CSimpleArray *CVideoWindow::g_pVideos; BOOL CVideoWindow::g_fMirror = FALSE; BOOL CVideoWindow::g_bLocalOneTimeInited = FALSE; static const TCHAR REGVAL_VIDEO_QUALITY[] = TEXT("ImageQuality"); static const UINT VIDEO_ZOOM_MIN = 50; static const UINT VIDEO_ZOOM_MAX = 400; static const UINT QCIF_WIDTH = 176; static const UINT QCIF_HEIGHT = 144; CVideoWindow::CVideoWindow(VideoType eType, BOOL bEmbedded) : m_dwFrameSize(NM_VIDEO_MEDIUM), m_hdd(NULL), #ifdef DISPLAYFPS m_cFrame (0), m_dwTick (GetTickCount()), #endif // DISPLAYFPS m_pActiveChannel(NULL), m_dwCookie(0), m_dwImageQuality(NM_VIDEO_MIN_QUALITY), m_pNotify(NULL), m_fLocal(REMOTE!=eType), m_hBitmapMirror(NULL), m_hDCMirror(NULL), m_fZoomable(TRUE), m_bEmbedded(bEmbedded), m_hGDIObj(NULL) { if (NULL == g_pVideos) { g_pVideos = new CSimpleArray; } if (NULL != g_pVideos) { CVideoWindow* p = static_cast(this); g_pVideos->Add(p); } m_sizeVideo.cx = 0; m_sizeVideo.cy = 0; RegEntry reAudio(AUDIO_KEY); // HKCU RegEntry reVideo( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY, HKEY_CURRENT_USER ); DWORD dwFrameSizeDefault = FRAME_QCIF; DWORD dwImageQualityDefault = NM_VIDEO_DEFAULT_QUALITY; int nVideoWidthDefault = VIDEO_WIDTH_QCIF; int nVideoHeightDefault = VIDEO_HEIGHT_QCIF; UINT uBandWidth = reAudio.GetNumber(REGVAL_TYPICALBANDWIDTH, BW_DEFAULT); if (uBandWidth == BW_144KBS) { dwImageQualityDefault = NM_VIDEO_MIN_QUALITY; } m_dwFrameSize = reVideo.GetNumber( REGVAL_VIDEO_FRAME_SIZE, dwFrameSizeDefault ); if (!IsLocal()) { m_dwImageQuality = reVideo.GetNumber( REGVAL_VIDEO_QUALITY, dwImageQualityDefault ); } m_nXferOnConnect = IsLocal() ? VIDEO_SEND_CONNECT_DEFAULT : VIDEO_RECEIVE_CONNECT_DEFAULT; m_nXferOnConnect = reVideo.GetNumber( REGVAL_VIDEO_XFER_CONNECT, m_nXferOnConnect); m_zoom = 100; } VOID CVideoWindow::OnNCDestroy() { // remote channel will get released upon the NM_CHANNEL_REMOVED // notification. The preview channel needs to be released here, // and not in the destructor because of a circular ref count if (NULL != m_pActiveChannel) { NmUnadvise(m_pActiveChannel, IID_INmChannelVideoNotify, m_dwCookie); m_dwCookie = 0; m_pActiveChannel->Release(); m_pActiveChannel = NULL; } if (NULL != m_pNotify) { m_pNotify->Release(); m_pNotify = NULL; } } CVideoWindow::~CVideoWindow() { CVideoWindow* p = static_cast(this); g_pVideos->Remove(p); if (0 == g_pVideos->GetSize()) { delete g_pVideos; g_pVideos = NULL; } if (NULL != m_hdd) { DRAWDIB::DrawDibClose(m_hdd); } // BUGBUG PhilF: Does DrawDibClose() nuke the selected palette? // release resources related to mirror preview UnInitMirroring(); } BOOL CVideoWindow::Create(HWND hwndParent, HPALETTE hpal, IVideoChange *pNotify) { if (FAILED(DRAWDIB::Init())) { return(FALSE); } if (!CGenWindow::Create( hwndParent, 0, IsLocal() ? TEXT("NMLocalVideo") : TEXT("NMRemoteVideo"), WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_CLIENTEDGE)) { return(FALSE); } SetWindowPos(GetWindow(), NULL, 0, 0, m_sizeVideo.cx, m_sizeVideo.cy, SWP_NOZORDER|SWP_NOACTIVATE); // We do NOT own this palette m_hPal = hpal; m_hdd = DRAWDIB::DrawDibOpen(); if (NULL != m_hdd) { // Use the Indeo palette in 8 bit mode only if the decompressed // video data is H.261 or H.263. We will create this palette as // an identity palette. // PhilF: If the user is utilizing an installable codec, do we still want to do this? // Update the palette in the DrawDib surface if (NULL != hpal) { DRAWDIB::DrawDibSetPalette(m_hdd, hpal); } } if (NULL != pNotify) { m_pNotify = pNotify; m_pNotify->AddRef(); } if (IsLocal()) { // need to get PreviewChannel; INmManager2 *pManager = CConfMan::GetNmManager(); ASSERT (NULL != pManager); pManager->GetPreviewChannel(&m_pActiveChannel); pManager->Release(); if (m_pActiveChannel) { NmAdvise(m_pActiveChannel, static_cast(this), IID_INmChannelVideoNotify, &m_dwCookie); DWORD_PTR dwFrameSize = GetFrameSize(); if (g_bLocalOneTimeInited) { m_pActiveChannel->GetProperty(NM_VIDPROP_IMAGE_PREFERRED_SIZE, &dwFrameSize); } else { DWORD dwSizes = GetFrameSizes(); // if frame size is not valid if (!(m_dwFrameSize & dwSizes)) { // find an alternate size if (FRAME_QCIF & dwSizes) { dwFrameSize = FRAME_QCIF; } else if (FRAME_SQCIF & dwSizes) { dwFrameSize = FRAME_SQCIF; } else if (FRAME_CIF & dwSizes) { dwFrameSize = FRAME_CIF; } } RegEntry reVideo( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY, HKEY_CURRENT_USER ); SetMirror(reVideo.GetNumber(REGVAL_VIDEO_MIRROR, TRUE)); g_bLocalOneTimeInited = TRUE; } SetFrameSize((DWORD)dwFrameSize); } } ResizeWindowsToFrameSize(); return TRUE; } DWORD CVideoWindow::GetFrameSizes() { DWORD_PTR dwSizes = 0; if (NULL != m_pActiveChannel) { m_pActiveChannel->GetProperty(NM_VIDPROP_IMAGE_SIZES, &dwSizes); } return (DWORD)dwSizes; } VOID CVideoWindow::SetFrameSize(DWORD dwSize) { DWORD dwPrevSize = m_dwFrameSize; m_dwFrameSize = dwSize; if (IsLocal()) { if (NULL != m_pActiveChannel) { m_pActiveChannel->SetProperty(NM_VIDPROP_IMAGE_PREFERRED_SIZE, dwSize); } } if (dwPrevSize != dwSize) { if ((NULL == m_pActiveChannel) || (!IsLocal() && (S_OK != m_pActiveChannel->IsActive())) || (IsLocal() && IsPaused())) { ResizeWindowsToFrameSize(); } } } VOID CVideoWindow::ResizeWindowsToFrameSize() { SIZE size; switch(m_dwFrameSize) { case FRAME_SQCIF: size.cx = VIDEO_WIDTH_SQCIF; size.cy = VIDEO_HEIGHT_SQCIF; break; case FRAME_CIF: if (IsLocal()) { size.cx = VIDEO_WIDTH_CIF; size.cy = VIDEO_HEIGHT_CIF; break; } // else fall through to QCIF case FRAME_QCIF: default: size.cx = VIDEO_WIDTH_QCIF; size.cy = VIDEO_HEIGHT_QCIF; break; } SetVideoSize(&size); } STDMETHODIMP CVideoWindow::QueryInterface(REFIID riid, PVOID *ppv) { HRESULT hr = S_OK; if ((riid == IID_INmChannelVideoNotify) || (riid == IID_IUnknown)) { *ppv = static_cast(this); DbgMsgApi("CVideoWindow::QueryInterface()"); } else { hr = E_NOINTERFACE; *ppv = NULL; DbgMsgApi("CVideoWindow::QueryInterface(): Called on unknown interface."); } if (S_OK == hr) { AddRef(); } return hr; } STDMETHODIMP CVideoWindow::NmUI(CONFN uNotify) { return S_OK; } STDMETHODIMP CVideoWindow::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember) { return S_OK; } STDMETHODIMP CVideoWindow::StateChanged(NM_VIDEO_STATE uState) { InvalidateRect(GetWindow(), NULL, TRUE); if (NULL != m_pNotify) { m_pNotify->StateChange(this, uState); } CNmManagerObj::VideoChannelStateChanged(uState, !m_fLocal); return S_OK; } STDMETHODIMP CVideoWindow::PropertyChanged(DWORD dwReserved) { if (NM_VIDPROP_FRAME == dwReserved) { OnFrameAvailable(); } return S_OK; } HRESULT CVideoWindow::OnChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel) { INmChannelVideo* pChannelVideo; if (SUCCEEDED(pChannel->QueryInterface(IID_INmChannelVideo, (void**)&pChannelVideo))) { BOOL bIncoming = (S_OK == pChannelVideo->IsIncoming()); if ((bIncoming && !IsLocal()) || (!bIncoming && IsLocal())) { switch (uNotify) { case NM_CHANNEL_ADDED: if (NULL == m_pActiveChannel) { pChannelVideo->AddRef(); m_pActiveChannel = pChannelVideo; NmAdvise(m_pActiveChannel,static_cast(this), IID_INmChannelVideoNotify, &m_dwCookie); SetImageQuality(m_dwImageQuality); } if (!_Module.InitControlMode()) { switch(m_nXferOnConnect) { case VIDEO_XFER_START: if (IsPaused()) { Pause(FALSE); } break; case VIDEO_XFER_STOP: Pause(TRUE); break; default: if (!IsLocal()) { Pause(TRUE); } break; } } break; case NM_CHANNEL_REMOVED: if (!IsLocal() && (pChannel == m_pActiveChannel)) { NmUnadvise(m_pActiveChannel, IID_INmChannelVideoNotify, m_dwCookie); m_pActiveChannel->Release(); m_pActiveChannel = NULL; m_dwCookie = 0; ResizeWindowsToFrameSize(); } } // just in case we missed a notification // (in final version this should not be the case) // m_VideoWindow.OnStateChange(); } pChannelVideo->Release(); } return S_OK; } HRESULT CVideoWindow::SetImageQuality(DWORD dwQuality) { HRESULT hr = E_FAIL; m_dwImageQuality = dwQuality; if (NULL != m_pActiveChannel) { hr = m_pActiveChannel->SetProperty(NM_VIDPROP_IMAGE_QUALITY, dwQuality); CNmManagerObj::VideoPropChanged(NM_VIDPROP_IMAGE_QUALITY, !IsLocal()); } return hr; } HRESULT CVideoWindow::SetCameraDialog(ULONG ul) { if(IsLocal()) { return m_pActiveChannel->SetProperty(NM_VIDPROP_CAMERA_DIALOG, ul); CNmManagerObj::VideoPropChanged(NM_VIDPROP_CAMERA_DIALOG, IsLocal()); } return E_FAIL; } HRESULT CVideoWindow::GetCameraDialog(ULONG* pul) { HRESULT hr; DWORD_PTR dwPropVal; if(IsLocal()) { hr = m_pActiveChannel->GetProperty(NM_VIDPROP_CAMERA_DIALOG, &dwPropVal); *pul = (ULONG)dwPropVal; return hr; } return E_FAIL; } BOOL CVideoWindow::IsXferAllowed() { if (IsLocal()) { return FIsSendVideoAllowed() && (NULL != m_pActiveChannel); } else { return FIsReceiveVideoAllowed(); } } BOOL CVideoWindow::IsAutoXferEnabled() { return(VIDEO_XFER_START == m_nXferOnConnect); } BOOL CVideoWindow::IsXferEnabled() { if (NULL != m_pActiveChannel) { NM_VIDEO_STATE state; if (SUCCEEDED(m_pActiveChannel->GetState(&state))) { switch (state) { case NM_VIDEO_PREVIEWING: case NM_VIDEO_TRANSFERRING: case NM_VIDEO_REMOTE_PAUSED: return TRUE; case NM_VIDEO_IDLE: case NM_VIDEO_LOCAL_PAUSED: case NM_VIDEO_BOTH_PAUSED: default: return FALSE; } } } return FALSE; } HRESULT CVideoWindow::GetVideoState(NM_VIDEO_STATE* puState) { if(m_pActiveChannel) { return m_pActiveChannel->GetState(puState); } return E_FAIL; } VOID CVideoWindow::Pause(BOOL fPause) { if (NULL != m_pActiveChannel) { m_pActiveChannel->SetProperty(NM_VIDPROP_PAUSE, (ULONG)fPause); CNmManagerObj::VideoPropChanged(NM_VIDPROP_PAUSE, !IsLocal()); } } BOOL CVideoWindow::IsPaused() { if (NULL != m_pActiveChannel) { ULONG_PTR uPause; if (SUCCEEDED(m_pActiveChannel->GetProperty(NM_VIDPROP_PAUSE, &uPause))) { return (BOOL)uPause; } } return TRUE; } BOOL CVideoWindow::IsConnected() { if (NULL != m_pActiveChannel) { NM_VIDEO_STATE state; if (SUCCEEDED(m_pActiveChannel->GetState(&state))) { switch (state) { case NM_VIDEO_LOCAL_PAUSED: case NM_VIDEO_TRANSFERRING: case NM_VIDEO_BOTH_PAUSED: case NM_VIDEO_REMOTE_PAUSED: return TRUE; case NM_VIDEO_IDLE: case NM_VIDEO_PREVIEWING: default: return FALSE; } } } return FALSE; } DWORD CVideoWindow::GetNumCapDev() { DWORD_PTR dwNumDevs = 0; if (NULL != m_pActiveChannel) { m_pActiveChannel->GetProperty(NM_VIDPROP_NUM_CAPTURE_DEVS, &dwNumDevs); } return (DWORD)dwNumDevs; } DWORD CVideoWindow::GetMaxCapDevNameLen() { DWORD_PTR dwLen = 0; if (NULL != m_pActiveChannel) { m_pActiveChannel->GetProperty(NM_VIDPROP_MAX_CAPTURE_NAME, &dwLen); } return (DWORD)dwLen; } DWORD CVideoWindow::GetCurrCapDevID() { DWORD_PTR dwID = 0; if (NULL != m_pActiveChannel) { m_pActiveChannel->GetProperty(NM_VIDPROP_CAPTURE_DEV_ID, &dwID); } return (DWORD)dwID; } VOID CVideoWindow::SetCurrCapDevID(DWORD dwID) { if (NULL != m_pActiveChannel) { m_pActiveChannel->SetProperty(NM_VIDPROP_CAPTURE_DEV_ID, dwID); InvalidateRect(GetWindow(), NULL, TRUE); } } VOID CVideoWindow::EnableAutoXfer(BOOL fEnable) { m_nXferOnConnect = fEnable ? VIDEO_XFER_START : VIDEO_XFER_NOP; RegEntry reXfer( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY, HKEY_CURRENT_USER ); reXfer.SetValue ( REGVAL_VIDEO_XFER_CONNECT, m_nXferOnConnect); } VOID CVideoWindow::EnumCapDev(DWORD *pdwCapDevIDs, LPTSTR pszCapDevNames, DWORD dwNumCapDev) { ENUM_CAP_DEV enumCapDev; enumCapDev.pdwCapDevIDs = pdwCapDevIDs; enumCapDev.pszCapDevNames = pszCapDevNames; enumCapDev.dwNumCapDev = dwNumCapDev; if (NULL != m_pActiveChannel) { m_pActiveChannel->GetProperty(NM_VIDPROP_CAPTURE_LIST, (DWORD_PTR *)&enumCapDev); } } VOID CVideoWindow::GetDesiredSize(SIZE *ppt) { ppt->cx = m_sizeVideo.cx*m_zoom/100; ppt->cy = m_sizeVideo.cy*m_zoom/100; if (m_bEmbedded) { ppt->cx = min(ppt->cx, QCIF_WIDTH); ppt->cy = min(ppt->cy, QCIF_HEIGHT); } SIZE sGen; CGenWindow::GetDesiredSize(&sGen); ppt->cx += sGen.cx; ppt->cy += sGen.cy; } VOID CVideoWindow::SetVideoSize(LPSIZE lpsize) { m_sizeVideo = *lpsize; OnDesiredSizeChanged(); } #ifdef DEBUG DWORD g_fDisplayFPS = FALSE; #ifdef DISPLAYFPS VOID CVideoWindow::UpdateFps(void) { DWORD dwTick = GetTickCount(); m_cFrame++; // Update display every 4 seconds if ((dwTick - m_dwTick) < 4000) return; TCHAR sz[32]; wsprintf(sz, "%d FPS", m_cFrame / 4); SetWindowText(m_hwndParent, sz); m_cFrame = 0; m_dwTick = dwTick; } #endif /* DISPLAYFPS */ #endif // DEBUG VOID CVideoWindow::OnFrameAvailable(void) { ::InvalidateRect(GetWindow(), NULL, FALSE); #ifdef DISPLAYFPS if (g_fDisplayFPS) { UpdateFps(); } #endif // DISPLAYFPS } VOID CVideoWindow::PaintDib(HDC hdc, FRAMECONTEXT *pFrame) { RECT rcVideo; GetClientRect(GetWindow(), &rcVideo); HPALETTE hpOld = NULL; if (NULL != m_hPal) { hpOld = SelectPalette(hdc, m_hPal, FALSE); RealizePalette(hdc); } // create the bitmap object, only if it doesn't exist // and if the mirror bitmap object isn't the right size if (!ShouldMirror() || !InitMirroring(rcVideo)) { // ISSUE: (ChrisPi 2-19-97) should we use DDF_SAME_HDC? DRAWDIB::DrawDibDraw(m_hdd,hdc, rcVideo.left, rcVideo.top, RectWidth(rcVideo), RectHeight(rcVideo), &pFrame->lpbmi->bmiHeader, pFrame->lpData, pFrame->lpClipRect->left, pFrame->lpClipRect->top, RectWidth(pFrame->lpClipRect), RectHeight(pFrame->lpClipRect), 0); } else { if (NULL != m_hPal) { SelectPalette(m_hDCMirror, m_hPal, FALSE); RealizePalette(m_hDCMirror); } DRAWDIB::DrawDibDraw(m_hdd, m_hDCMirror, 0, 0, RectWidth(rcVideo), RectHeight(rcVideo), &pFrame->lpbmi->bmiHeader, pFrame->lpData, pFrame->lpClipRect->left, pFrame->lpClipRect->top, RectWidth(pFrame->lpClipRect), RectHeight(pFrame->lpClipRect), 0); ::StretchBlt(hdc, rcVideo.right, rcVideo.top, -RectWidth(rcVideo), RectHeight(rcVideo), m_hDCMirror, 0, 0, RectWidth(rcVideo), RectHeight(rcVideo), SRCCOPY); // HACKHACK georgep; don't worry about deselecting the palette in // the temp DC } if (NULL != hpOld) { SelectPalette(hdc, hpOld, FALSE); } } /* P A I N T L O G O */ /*------------------------------------------------------------------------- %%Function: PaintLogo Display the 256 color NetMeeting logo in the video window. -------------------------------------------------------------------------*/ VOID CVideoWindow::PaintLogo(HDC hdc, UINT idbLargeLogo, UINT idbSmallLogo) { RECT rcVideo; GetClientRect(GetWindow(), &rcVideo); ::FillRect(hdc, &rcVideo, (HBRUSH)::GetStockObject(WHITE_BRUSH)); // Create the memory DC HDC hdcMem = ::CreateCompatibleDC(hdc); if (NULL == hdcMem) { ERROR_OUT(("PaintLogo: Unable to CreateCompatibleDC")); return; } // Load the bitmap (LoadBitmap doesn't work for 256 color images) HANDLE hBitmap = LoadImage(::GetInstanceHandle(), MAKEINTRESOURCE(idbLargeLogo), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); if (NULL != hBitmap) { BITMAP bitmap; ::GetObject(hBitmap, sizeof(bitmap), &bitmap); int cx = bitmap.bmWidth; int cy = bitmap.bmHeight; if (RectWidth(rcVideo) < cx || RectHeight(rcVideo) < cy) { HANDLE hNew = LoadImage(::GetInstanceHandle(), MAKEINTRESOURCE(idbSmallLogo), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); if (NULL != hNew) { DeleteObject(hBitmap); hBitmap = hNew; ::GetObject(hBitmap, sizeof(bitmap), &bitmap); cx = bitmap.bmWidth; cy = bitmap.bmHeight; } } HBITMAP hBmpTmp = (HBITMAP)::SelectObject(hdcMem, hBitmap); // Select and realize the palette HPALETTE hPalette = m_hPal; if (NULL != hPalette) { SelectPalette(hdcMem, hPalette, FALSE); RealizePalette(hdcMem); SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); } int x = rcVideo.left + (RectWidth(rcVideo) - cx) / 2; int y = rcVideo.top + (RectHeight(rcVideo) - cy) / 2; ::BitBlt(hdc, x, y, cx, cy, hdcMem, 0, 0, SRCCOPY); ::SelectObject(hdcMem, hBmpTmp); ::DeleteObject(hBitmap); } ::DeleteDC(hdcMem); } VOID CVideoWindow::OnPaint() { PAINTSTRUCT ps; HDC hdc; hdc = ::BeginPaint(GetWindow(), &ps); if( hdc ) { if( RectWidth(ps.rcPaint) && RectHeight(ps.rcPaint) ) { // This means that we have a non-zero surface area ( there may be something to paint ) DBGENTRY(CVideoWindow::ProcessPaint); // paint the video rect FRAMECONTEXT fc; if ((S_OK == GetFrame(&fc))) { if (!IsPaused()) { SIZE vidSize = { RectWidth(fc.lpClipRect), RectHeight(fc.lpClipRect) } ; if ((vidSize.cx != m_sizeVideo.cx) || (vidSize.cy != m_sizeVideo.cy)) { // save the new image size SetVideoSize(&vidSize); } } PaintDib(hdc, &fc); ReleaseFrame(&fc); } else { PaintLogo(hdc, IDB_VIDEO_LOGO, IDB_VIDEO_LOGO_SMALL); } // check to see if needs painting outside the video rect #if FALSE // Currently just stretching the video to the window size if (ps.rcPaint.left < m_rcVideo.left || ps.rcPaint.top < m_rcVideo.top || ps.rcPaint.right > m_rcVideo.right || ps.rcPaint.bottom > m_rcVideo.bottom) { HFONT hfOld; int nBkModeOld; RECT rc, rcClient; ::GetClientRect(GetWindow(), &rcClient); // erase the background if requested if (ps.fErase) { ::ExcludeClipRect(hdc, m_rcVideo.left, m_rcVideo.top, m_rcVideo.right, m_rcVideo.bottom); ::FillRect(hdc, &rcClient, ::GetSysColorBrush(COLOR_BTNFACE)); } nBkModeOld = ::SetBkMode(hdc, TRANSPARENT); hfOld = (HFONT)::SelectObject(hdc, g_hfontDlg); // paint the status text // first erase the old text if not already done if (!ps.fErase) { ::FillRect(hdc, &m_rcStatusText, ::GetSysColorBrush(COLOR_BTNFACE)); } TCHAR szState[MAX_PATH]; if (GetState(szState, CCHMAX(szState))) { COLORREF crOld; crOld = ::SetTextColor(hdc, ::GetSysColor(COLOR_BTNTEXT)); rc = m_rcStatusText; rc.left += STATUS_MARGIN; ::DrawText(hdc, szState, -1, &rc, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX); ::SetTextColor(hdc, crOld); } // paint border around video rc = m_rcVideo; ::InflateRect(&rc, ::GetSystemMetrics(SM_CXEDGE), ::GetSystemMetrics(SM_CYEDGE)); ::DrawEdge(hdc, &rc, EDGE_SUNKEN , BF_RECT); // restore DC stuff ::SelectObject(hdc, hfOld); ::SetBkMode(hdc, nBkModeOld); } #endif // FALSE DBGEXIT(CVideoWindow::ProcessPaint); } ::EndPaint(GetWindow(), &ps); } } VOID CVideoWindow::SetZoom(UINT zoom) { if (m_zoom == zoom) { return; } m_zoom = zoom; OnDesiredSizeChanged(); } void CVideoWindow::OnCommand(int idCmd) { switch (idCmd) { case IDM_VIDEO_GETACAMERA: CmdLaunchWebPage(idCmd); break; case IDM_VIDEO_COPY: CopyToClipboard(); break; case IDM_VIDEO_FREEZE: Pause(!IsPaused()); break; case IDM_VIDEO_UNDOCK: CMainUI::NewVideoWindow(GetConfRoom()); break; case IDM_VIDEO_ZOOM1: SetZoom(100); break; case IDM_VIDEO_ZOOM2: SetZoom(200); break; case IDM_VIDEO_ZOOM3: SetZoom(300); break; case IDM_VIDEO_ZOOM4: SetZoom(400); break; case IDM_VIDEO_PROPERTIES: LaunchConfCpl(GetWindow(), OPTIONS_VIDEO_PAGE); break; } } void CVideoWindow::UpdateVideoMenu(HMENU hMenu) { EnableMenuItem(hMenu, IDM_VIDEO_COPY, MF_BYCOMMAND|(CanCopy() ? MF_ENABLED : MF_GRAYED|MF_DISABLED)); EnableMenuItem(hMenu, IDM_VIDEO_UNDOCK, MF_BYCOMMAND|(IsLocal() ? MF_ENABLED : MF_GRAYED|MF_DISABLED)); if (!FIsSendVideoAllowed() && !FIsReceiveVideoAllowed()) { EnableMenuItem(hMenu, IDM_VIDEO_FREEZE, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED); EnableMenuItem(hMenu, IDM_VIDEO_PROPERTIES, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED); EnableMenuItem(hMenu, IDM_VIDEO_GETACAMERA, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED); } else { CheckMenuItem(hMenu, IDM_VIDEO_FREEZE, MF_BYCOMMAND|(IsPaused() ? MF_CHECKED : MF_UNCHECKED)); UINT uEnable = ::CanShellExecHttp() ? MF_ENABLED : MF_GRAYED; ::EnableMenuItem(hMenu, IDM_VIDEO_GETACAMERA, uEnable); } int nZoom = GetZoom(); int idZoom; if (350 < nZoom) idZoom = IDM_VIDEO_ZOOM4; else if (250 < nZoom) idZoom = IDM_VIDEO_ZOOM3; else if (150 < nZoom) idZoom = IDM_VIDEO_ZOOM2; else idZoom = IDM_VIDEO_ZOOM1; UINT uFlags = IsZoomable() && !m_bEmbedded ? MF_ENABLED : MF_GRAYED|MF_DISABLED; for (int id=IDM_VIDEO_ZOOM1; id<=IDM_VIDEO_ZOOM4; ++id) { EnableMenuItem(hMenu, id, MF_BYCOMMAND|uFlags); CheckMenuItem(hMenu, id, MF_BYCOMMAND|MF_UNCHECKED); } CheckMenuItem(hMenu, idZoom, MF_BYCOMMAND|MF_CHECKED); uFlags = CanLaunchConfCpl() ? MF_ENABLED : MF_GRAYED|MF_DISABLED; EnableMenuItem(hMenu, IDM_VIDEO_PROPERTIES, MF_BYCOMMAND|uFlags); } void CVideoWindow::OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos) { HMENU hmLoad = LoadMenu(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDR_VIDEO_POPUP)); if (NULL != hmLoad) { HMENU hmPopup = GetSubMenu(hmLoad, 0); ASSERT(NULL != hmPopup); UpdateVideoMenu(hmPopup); int idCmd = TrackPopupMenu(hmPopup, TPM_RETURNCMD|TPM_RIGHTBUTTON, xPos, yPos, 0, hwnd, NULL); if (0 != idCmd) { OnCommand(idCmd); } DestroyMenu(hmLoad); } } LRESULT CVideoWindow::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { HANDLE_MSG(hwnd, WM_CONTEXTMENU, OnContextMenu); case WM_ERASEBKGND: return 0; case WM_PAINT: OnPaint(); return 0; case WM_NCDESTROY: OnNCDestroy(); return 0; case WM_SIZE: // Need to redraw the whole window InvalidateRect(GetWindow(), NULL, FALSE); break; default: break; } return CGenWindow::ProcessMessage(hwnd, message, wParam, lParam) ; } #if FALSE int CVideoWindow::GetState(LPTSTR lpszState, int nStateMax) { int uStringID; NM_VIDEO_STATE state = NM_VIDEO_IDLE; if (NULL != m_pActiveChannel) { m_pActiveChannel->GetState(&state); } if (IsLocal()) { switch (state) { case NM_VIDEO_PREVIEWING: uStringID = IDS_VIDEO_STATE_PREVIEWING; break; case NM_VIDEO_TRANSFERRING: uStringID = IDS_VIDEO_STATE_SENDING; break; case NM_VIDEO_REMOTE_PAUSED: uStringID = IDS_VIDEO_STATE_REMOTEPAUSED; break; case NM_VIDEO_LOCAL_PAUSED: case NM_VIDEO_BOTH_PAUSED: uStringID = IDS_VIDEO_STATE_PAUSED; break; case NM_VIDEO_IDLE: default: uStringID = IDS_VIDEO_STATE_NOTSENDING; break; } } else { switch (state) { case NM_VIDEO_TRANSFERRING: uStringID = IDS_VIDEO_STATE_RECEIVING; break; case NM_VIDEO_REMOTE_PAUSED: uStringID = IDS_VIDEO_STATE_REMOTEPAUSED; break; case NM_VIDEO_LOCAL_PAUSED: case NM_VIDEO_BOTH_PAUSED: uStringID = IDS_VIDEO_STATE_PAUSED; break; case NM_VIDEO_IDLE: case NM_VIDEO_PREVIEWING: default: uStringID = IDS_VIDEO_STATE_NOTRECEIVING; break; } } return ::LoadString( ::GetInstanceHandle(), uStringID, lpszState, nStateMax); } #endif // FALSE // Return value will be TRUE if Windows can handle the format static BOOL IsKnownDIBFormat(BITMAPINFOHEADER *pbmih) { if (sizeof(BITMAPINFOHEADER) != pbmih->biSize) { return(FALSE); } if (1 != pbmih->biPlanes) { return(FALSE); } int bits = pbmih->biBitCount; int comp = pbmih->biCompression; switch (bits) { case 1: if (BI_RGB == comp) { return(TRUE); } break; case 4: if (BI_RGB == comp || BI_RLE4 == comp) { return(TRUE); } break; case 8: if (BI_RGB == comp || BI_RLE8 == comp) { return(TRUE); } break; case 16: if (BI_RGB == comp || BI_BITFIELDS == comp) { return(TRUE); } break; case 24: case 32: if (BI_RGB == comp) { return(TRUE); } break; default: break; } return(FALSE); } #ifdef TryPaintDIB BOOL CVideoWindow::CopyToClipboard() { FRAMECONTEXT fc; BOOL fSuccess = FALSE; // Get the current frame and open the clipboard if (S_OK == GetFrame(&fc)) { HWND hwnd = GetWindow(); if (OpenClipboard(hwnd)) { // Allocate memory that we will be giving to the clipboard BITMAPINFOHEADER *pbmih = &fc.lpbmi->bmiHeader; BOOL bCopy = IsKnownDIBFormat(pbmih); // BUGBUG georgep; I think this doesn't work for 15 or 16 bit DIBs int nColors = pbmih->biClrUsed; if (0 == nColors && pbmih->biBitCount <= 8) { nColors = 1 << pbmih->biBitCount; } // Special case for 16-bit bitfield bitmaps if (16 == pbmih->biBitCount && BI_BITFIELDS == pbmih->biCompression) { nColors = 3; } int nHdrSize = pbmih->biSize + nColors * sizeof(RGBQUAD); int bitsPer = pbmih->biBitCount; if (!bCopy) { bitsPer *= pbmih->biPlanes; if (bitsPer > 24) { bitsPer = 32; } // LAZYLAZY georgep: Skipping 16-bit format else if (bitsPer > 8) { bitsPer = 24; } else if (bitsPer > 4) { bitsPer = 8; } else if (bitsPer > 1) { bitsPer = 4; } else { bitsPer = 1; } } int nDataSize = bCopy ? pbmih->biSizeImage : 0; if (0 == nDataSize) { // Make an uncompressed DIB int nByteWidth = (pbmih->biWidth*bitsPer+7) / 8; nDataSize = ((nByteWidth + 3)&~3) * pbmih->biHeight; } // Allocate the total memory for the DIB HGLOBAL hCopy = GlobalAlloc(GHND, nHdrSize + nDataSize); if (NULL != hCopy) { BITMAPINFO *lpvCopy = reinterpret_cast(GlobalLock(hCopy)); CopyMemory(lpvCopy, pbmih, nHdrSize); // Create a temporary DC for drawing into HDC hdc = GetDC(hwnd); if (NULL != hdc) { HDC hdcTemp = CreateCompatibleDC(hdc); ReleaseDC(hwnd, hdc); hdc = hdcTemp; } if (NULL != hdc) { if (bCopy) { if (ShouldMirror()) { // Create a DIB section for drawing into LPVOID pData; HBITMAP hDIB = CreateDIBSection(hdc, lpvCopy, DIB_RGB_COLORS, &pData, NULL, 0); if (NULL != hDIB) { // Draw into the DIB, and then copy the bits HGDIOBJ hOld = SelectObject(hdc, hDIB); RECT rc = { 0, 0, pbmih->biWidth, pbmih->biHeight }; StretchDIBits(hdc, 0, 0, pbmih->biWidth, pbmih->biHeight, pbmih->biWidth, 0, -pbmih->biWidth, pbmih->biHeight, fc.lpData, lpvCopy, DIB_RGB_COLORS, SRCCOPY); CopyMemory(&lpvCopy->bmiColors[nColors], pData, nDataSize); // Start cleaning up SelectObject(hdc, hOld); DeleteObject(hDIB); } } else { CopyMemory(&lpvCopy->bmiColors[nColors], fc.lpData, nDataSize); } fSuccess = TRUE; } else { lpvCopy->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); lpvCopy->bmiHeader.biPlanes = 1; lpvCopy->bmiHeader.biBitCount = bitsPer; lpvCopy->bmiHeader.biCompression = BI_RGB; lpvCopy->bmiHeader.biSizeImage = nDataSize; // Create a DIB section for drawing into LPVOID pData; HBITMAP hDIB = CreateDIBSection(hdc, lpvCopy, DIB_RGB_COLORS, &pData, NULL, 0); if (NULL != hDIB) { // Draw into the DIB, and then copy the bits HGDIOBJ hOld = SelectObject(hdc, hDIB); RECT rc = { 0, 0, pbmih->biWidth, pbmih->biHeight }; PaintDib(hdc, &fc, &rc); CopyMemory(&lpvCopy->bmiColors[nColors], pData, nDataSize); // Start cleaning up SelectObject(hdc, hOld); DeleteObject(hDIB); fSuccess = TRUE; } } DeleteDC(hdc); } GlobalUnlock(hCopy); if (fSuccess) { // Set the DIB into the clipboard EmptyClipboard(); fSuccess = (NULL != SetClipboardData(CF_DIB, (HANDLE)hCopy)); } if (!fSuccess) { GlobalFree(hCopy); } } CloseClipboard(); } ReleaseFrame(&fc); } return fSuccess; } #else // TryPaintDIB BOOL CVideoWindow::CopyToClipboard() { FRAMECONTEXT fc; BOOL fSuccess = FALSE; if (S_OK == GetFrame(&fc)) { if (OpenClipboard(GetWindow())) { EmptyClipboard(); { HGLOBAL hCopy; BITMAPINFOHEADER *pbmih; pbmih = &fc.lpbmi->bmiHeader; hCopy = GlobalAlloc(GHND, DibSize(pbmih)); if (NULL != hCopy) { LPVOID lpvCopy = GlobalLock(hCopy); int nHdrSize = DibHdrSize(pbmih); CopyMemory(lpvCopy, pbmih, nHdrSize); CopyMemory((LPBYTE)lpvCopy + nHdrSize, fc.lpData, DibDataSize(pbmih)); GlobalUnlock(hCopy); fSuccess = (NULL != SetClipboardData(CF_DIB, (HANDLE)hCopy)); if (!fSuccess) { GlobalFree(hCopy); } } } CloseClipboard(); } ReleaseFrame(&fc); } return fSuccess; } #endif // TryPaintDIB HRESULT CVideoWindow::GetFrame(FRAMECONTEXT *pFrameContext) { HRESULT hr = E_FAIL; if (NULL != m_pActiveChannel) { hr = m_pActiveChannel->GetProperty(NM_VIDPROP_FRAME, (DWORD_PTR *)pFrameContext); } return hr; } HRESULT CVideoWindow::ReleaseFrame(FRAMECONTEXT *pFrameContext) { HRESULT hr = E_FAIL; if (NULL != m_pActiveChannel) { hr = m_pActiveChannel->SetProperty(NM_VIDPROP_FRAME, (DWORD_PTR)pFrameContext); } return hr; } void CVideoWindow::SaveSettings() { RegEntry re( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY, HKEY_CURRENT_USER ); re.SetValue(REGVAL_VIDEO_FRAME_SIZE, m_dwFrameSize); if (!IsLocal()) { re.SetValue(REGVAL_VIDEO_QUALITY, m_dwImageQuality); } else { re.SetValue(REGVAL_VIDEO_MIRROR, GetMirror()); } } VOID CVideoWindow::ForwardSysChangeMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_PALETTECHANGED: OnPaletteChanged(); break; } } VOID CVideoWindow::OnPaletteChanged(void) { ::RedrawWindow(GetWindow(), NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); } BOOL CVideoWindow::HasDialog(DWORD dwDialog) { DWORD_PTR dwDialogs = 0; if (NULL != m_pActiveChannel) { m_pActiveChannel->GetProperty(NM_VIDPROP_CAMERA_DIALOG, &dwDialogs); } return (BOOL)(dwDialogs & dwDialog); } VOID CVideoWindow::ShowDialog(DWORD dwDialog) { if (NULL != m_pActiveChannel) { m_pActiveChannel->SetProperty(NM_VIDPROP_CAMERA_DIALOG, dwDialog); } } BOOL CVideoWindow::InitMirroring(RECT &rcVideo) { HDC hdcWindow; if ((m_hBitmapMirror != NULL) && (RectWidth(rcVideo) == m_sizeBitmapMirror.cx) && (RectHeight(rcVideo) == m_sizeBitmapMirror.cy)) { return TRUE; } hdcWindow = ::GetDC(NULL); UnInitMirroring(); m_hBitmapMirror = ::CreateCompatibleBitmap(hdcWindow, RectWidth(rcVideo), RectHeight(rcVideo)); if (m_hBitmapMirror == NULL) { ReleaseDC(NULL, hdcWindow); return FALSE; } m_hDCMirror = ::CreateCompatibleDC(hdcWindow); if (m_hDCMirror == NULL) { UnInitMirroring(); ReleaseDC(NULL, hdcWindow); return FALSE; } // preserve the handle of the object being replaced m_hGDIObj = ::SelectObject(m_hDCMirror, m_hBitmapMirror); ::SetMapMode(m_hDCMirror, GetMapMode(hdcWindow)); m_sizeBitmapMirror.cx = RectWidth(rcVideo); m_sizeBitmapMirror.cy = RectHeight(rcVideo); ReleaseDC(NULL, hdcWindow); return TRUE; } VOID CVideoWindow::UnInitMirroring() { if (m_hBitmapMirror) { if (m_hDCMirror) { ::SelectObject(m_hDCMirror, m_hGDIObj); } ::DeleteObject(m_hBitmapMirror); m_hBitmapMirror = NULL; } if (m_hDCMirror) { ::DeleteObject(m_hDCMirror); m_hDCMirror = NULL; } } VOID CVideoWindow::InvalidateAll() { for(int i = g_pVideos->GetSize()-1; i >= 0 ; --i) { CVideoWindow *pVideo = (*g_pVideos)[i]; HWND hwnd = pVideo->GetWindow(); if (NULL != hwnd) { InvalidateRect(hwnd, NULL, FALSE); } } } VOID CVideoWindow::SetMirror(BOOL bMirror) { bMirror = bMirror != FALSE; if (g_fMirror != bMirror) { g_fMirror = bMirror; InvalidateAll(); } } BOOL CVideoWindow::CanCopy() { BOOL bCopy = (IsXferEnabled() && IsLocal()) || IsConnected(); if (bCopy) { bCopy = FALSE; FRAMECONTEXT fc; if (S_OK == GetFrame(&fc)) { BITMAPINFOHEADER *pbmih = &fc.lpbmi->bmiHeader; bCopy = IsKnownDIBFormat(pbmih); ReleaseFrame(&fc); } } return(bCopy); } BOOL CVideoWindow::FDidNotDisplayIntelLogo() { return FALSE; } VOID CVideoWindow::DisplayIntelLogo( BOOL bDisplay ) { }