windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/view.cpp
2020-09-26 16:20:57 +08:00

5872 lines
164 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "precomp.h"
#include <version.h>
#include <nmhelp.h>
//
// VIEW.CPP
// The frame, widgets, and client area that presents the shared apps/desktop
// for a remote host.
//
// Copyright(c) Microsoft 1997-
//
//
// NOTE:
// The client of the shared view frame represents the virtual desktop (VD)
// of the host. For 3.0 hosts, the VD is the same as the screen. But for
// 2.x hosts, the VD is the union of the screen size of all hosts. Hence
// the recalculation every time someone starts sharing or changes screen
// size, and the extra fun this entails for existing shared 2.x views.
//
#define MLZ_FILE_ZONE ZONE_CORE
// Help file
static const TCHAR s_cszHtmlHelpFile[] = TEXT("conf.chm");
//
// VIEW_Init()
//
BOOL VIEW_Init(void)
{
BOOL rc = FALSE;
WNDCLASSEX wc;
DebugEntry(VIEW_Init);
//
// Register the frame window class.
// NOTE: Change CS_NOCLOSE if/when we ever let you close the view
// of a person's shared apps.
//
wc.cbSize = sizeof(wc);
wc.style = CS_DBLCLKS | CS_NOCLOSE;
wc.lpfnWndProc = VIEWFrameWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_asInstance;
wc.hIcon = NULL;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = VIEW_FRAME_CLASS_NAME;
wc.hIconSm = NULL;
if (!RegisterClassEx(&wc))
{
ERROR_OUT(("Failed to register AS Frame class"));
DC_QUIT;
}
//
// Register the view window class. This sits in the client area of
// the frame along with the statusbar, tray, etc. It displays
// the remote host's shared contents.
//
wc.cbSize = sizeof(wc);
wc.style = CS_DBLCLKS | CS_NOCLOSE;
wc.lpfnWndProc = VIEWClientWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_asInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = VIEW_CLIENT_CLASS_NAME;
wc.hIconSm = NULL;
if (!RegisterClassEx(&wc))
{
ERROR_OUT(("Failed to register AS Client class"));
DC_QUIT;
}
//
// Register the window bar class. This hugs the bottom of
// frames for shared apps (not desktop) and acts like a tray
// surrogate. It allows controllers to minimize, restore, and
// activate shared windows that may not be on screen currently
// and therefore not in the view area.
//
// It also is handy reference for what top level windows are shared
// currently.
//
wc.cbSize = sizeof(wc);
wc.style = 0;
wc.lpfnWndProc = VIEWWindowBarProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_asInstance;
wc.hIcon = NULL;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = VIEW_WINDOWBAR_CLASS_NAME;
wc.hIconSm = NULL;
if (!RegisterClassEx(&wc))
{
ERROR_OUT(("Failed to register AS WindowBar class"));
DC_QUIT;
}
//
// Register the window bar items class. This is a child of the window
// bar and contains the actual items.
//
wc.cbSize = sizeof(wc);
wc.style = 0;
wc.lpfnWndProc = VIEWWindowBarItemsProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_asInstance;
wc.hIcon = NULL;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = VIEW_WINDOWBARITEMS_CLASS_NAME;
wc.hIconSm = NULL;
if (!RegisterClassEx(&wc))
{
ERROR_OUT(("Failed to register AS WindowBarItems class"));
DC_QUIT;
}
//
// Register the full screen exit button class. This is a child of the
// the view client when present.
//
wc.cbSize = sizeof(wc);
wc.style = 0;
wc.lpfnWndProc = VIEWFullScreenExitProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_asInstance;
wc.hIcon = NULL;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = VIEW_FULLEXIT_CLASS_NAME;
wc.hIconSm = NULL;
if (!RegisterClassEx(&wc))
{
ERROR_OUT(("Failed to register AS full screen exit class"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(VIEW_Init, rc);
return(rc);
}
//
// VIEW_Term()
//
void VIEW_Term(void)
{
DebugEntry(VIEW_Term);
//
// Free all resources we created (or may have created in window class
// case).
//
UnregisterClass(VIEW_FULLEXIT_CLASS_NAME, g_asInstance);
UnregisterClass(VIEW_WINDOWBARITEMS_CLASS_NAME, g_asInstance);
UnregisterClass(VIEW_WINDOWBAR_CLASS_NAME, g_asInstance);
UnregisterClass(VIEW_CLIENT_CLASS_NAME, g_asInstance);
UnregisterClass(VIEW_FRAME_CLASS_NAME, g_asInstance);
DebugExitVOID(VIEW_Term);
}
//
// VIEW_ShareStarting()
// Creates share resources
//
BOOL ASShare::VIEW_ShareStarting(void)
{
BOOL rc = FALSE;
HBITMAP hbmpT;
TEXTMETRIC tm;
HDC hdc;
HFONT hfnOld;
char szRestore[256];
SIZE extent;
DebugEntry(ASShare::VIEW_ShareStarting);
ASSERT(m_viewVDSize.x == 0);
ASSERT(m_viewVDSize.y == 0);
//
// Get NODROP cursor
//
m_viewNotInControl = ::LoadCursor(NULL, IDC_NO);
//
// Get MOUSEWHEEL lines metric
//
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
&m_viewMouseWheelScrollLines, 0);
//
// Create a pattern brush from the obscured bitmap
//
hbmpT = LoadBitmap(g_asInstance, MAKEINTRESOURCE(IDB_OBSCURED));
m_viewObscuredBrush = CreatePatternBrush(hbmpT);
DeleteBitmap(hbmpT);
if (!m_viewObscuredBrush)
{
ERROR_OUT(( "Failed to create obscured bitmap brush"));
DC_QUIT;
}
//
// NOTE THAT since the icons are VGA colors, we don't need to recreate
// our brush on a SYSCOLOR change.
//
// Get the full screen cancel icon
m_viewFullScreenExitIcon = (HICON)LoadImage(g_asInstance,
MAKEINTRESOURCE(IDI_CANCELFULLSCREEN), IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
LR_DEFAULTCOLOR);
m_viewEdgeCX = ::GetSystemMetrics(SM_CXEDGE);
m_viewEdgeCY = ::GetSystemMetrics(SM_CYEDGE);
//
// Get metrics of GUI_FONT, the one we use in the window bar and
// status bar.
//
LoadString(g_asInstance, IDS_RESTORE, szRestore, sizeof(szRestore));
hdc = ::GetDC(NULL);
hfnOld = (HFONT)::SelectObject(hdc, ::GetStockObject(DEFAULT_GUI_FONT));
::GetTextMetrics(hdc, &tm);
::GetTextExtentPoint(hdc, szRestore, lstrlen(szRestore), &extent);
::SelectObject(hdc, hfnOld);
::ReleaseDC(NULL, hdc);
//
// Calculate size of full screen button
// Edge on left + margin on left + sm icon + margin + text + margin on
// right + edge on right == 5 edges + sm icon + text
//
m_viewFullScreenCX = extent.cx + 5*m_viewEdgeCX + GetSystemMetrics(SM_CXSMICON);
m_viewFullScreenCY = max(GetSystemMetrics(SM_CYSMICON), extent.cy) + 4*m_viewEdgeCY;
//
// Calculate size of items on window bar
//
m_viewItemCX = 4*m_viewEdgeCX + ::GetSystemMetrics(SM_CXSMICON) +
m_viewEdgeCX + VIEW_MAX_ITEM_CHARS * tm.tmAveCharWidth;
m_viewItemCY = max(::GetSystemMetrics(SM_CYSMICON), tm.tmHeight) +
2*m_viewEdgeCY + 2*m_viewEdgeCY;
//
// Calculate the width & height of the items scroll buttons. We want
// to make sure it fits, but isn't ungainly.
//
m_viewItemScrollCX = ::GetSystemMetrics(SM_CXHSCROLL);
m_viewItemScrollCX = 2 * min(m_viewItemScrollCX, m_viewItemCY);
m_viewItemScrollCY = ::GetSystemMetrics(SM_CYHSCROLL);
m_viewItemScrollCY = min(m_viewItemScrollCY, m_viewItemCY);
//
// Calculate height of active window bar. We leave a CYEDGE gap on the
// top. between it and the sunken border around the view client.
//
m_viewWindowBarCY = m_viewItemCY + m_viewEdgeCY;
//
// Calculate height of status bar. It's height of GUIFONT plus edge
// space.
//
m_viewStatusBarCY = tm.tmHeight + 4*m_viewEdgeCY;
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(ASShare::VIEW_ShareStarting, rc);
return(rc);
}
//
// VIEW_ShareEnded()
// Cleans up resources for share
//
void ASShare::VIEW_ShareEnded(void)
{
DebugEntry(ASShare::VIEW_ShareEnded);
//
// Destroy the full screen cancel icon
//
if (m_viewFullScreenExitIcon != NULL)
{
DestroyIcon(m_viewFullScreenExitIcon);
m_viewFullScreenExitIcon = NULL;
}
if (m_viewObscuredBrush != NULL)
{
DeleteBrush(m_viewObscuredBrush);
m_viewObscuredBrush = NULL;
}
DebugExitVOID(ASShre::VIEW_ShareEnded);
}
//
// VIEW_PartyLeftShare()
//
// This is called when somebody leaves a share. We need this to
// simulate what back-level systems did to calculate the virtual desktop
// size. They didn't recalc when someone stopped shared, that person's
// screne size counted until they left the share.
//
void ASShare::VIEW_PartyLeftShare(ASPerson * pasPerson)
{
DebugEntry(ASShare::VIEW_PartyLeftShare);
ValidatePerson(pasPerson);
// If this dude ever shared, now remove him from the VD total
if (pasPerson->viewExtent.x != 0)
{
pasPerson->viewExtent.x = 0;
pasPerson->viewExtent.y = 0;
VIEW_RecalcVD();
}
DebugExitVOID(ASShare::VIEW_PartyLeftShare);
}
//
// VIEW_HostStarting()
//
// Called when we start to host.
//
BOOL ASHost::VIEW_HostStarting(void)
{
DebugEntry(ASHost:VIEW_HostStarting);
m_pShare->VIEW_RecalcExtent(m_pShare->m_pasLocal);
m_pShare->VIEW_RecalcVD();
DebugExitBOOL(ASHost::VIEW_HostStarting, TRUE);
return(TRUE);
}
//
// VIEW_ViewStarting()
// Called when someone in the meeting starts to share. For all in the
// conference, we keep a running tally of the VD, but use it only for
// 2.x views. For remotes only, we create a view of their desktop.
//
BOOL ASShare::VIEW_ViewStarting(ASPerson * pasHost)
{
BOOL rc = FALSE;
HWND hwnd;
RECT rcSize;
DebugEntry(ASShare::VIEW_ViewStarting);
ValidateView(pasHost);
//
// First, calculate the extents, and the VD size.
//
VIEW_RecalcExtent(pasHost);
VIEW_RecalcVD();
//
// Next, create scratch regions
//
pasHost->m_pView->m_viewExtentRgn = CreateRectRgn(0, 0, 0, 0);
pasHost->m_pView->m_viewScreenRgn = CreateRectRgn(0, 0, 0, 0);
pasHost->m_pView->m_viewPaintRgn = CreateRectRgn(0, 0, 0, 0);
pasHost->m_pView->m_viewScratchRgn = CreateRectRgn(0, 0, 0, 0);
if (!pasHost->m_pView->m_viewExtentRgn || !pasHost->m_pView->m_viewScreenRgn || !pasHost->m_pView->m_viewPaintRgn || !pasHost->m_pView->m_viewScratchRgn)
{
ERROR_OUT(("ViewStarting: Couldn't create scratch regions"));
DC_QUIT;
}
ASSERT(pasHost->m_pView->m_viewFrame == NULL);
ASSERT(pasHost->m_pView->m_viewClient == NULL);
ASSERT(pasHost->m_pView->m_viewSharedRgn == NULL);
ASSERT(pasHost->m_pView->m_viewObscuredRgn == NULL);
ASSERT(pasHost->m_pView->m_viewPos.x == 0);
ASSERT(pasHost->m_pView->m_viewPos.y == 0);
ASSERT(pasHost->m_pView->m_viewPage.x == 0);
ASSERT(pasHost->m_pView->m_viewPage.y == 0);
ASSERT(!pasHost->m_pView->m_viewStatusBarOn);
ASSERT(!pasHost->m_pView->m_viewWindowBarOn);
ASSERT(!pasHost->m_pView->m_viewFullScreen);
pasHost->m_pView->m_viewStatusBarOn = TRUE;
if (pasHost->hetCount != HET_DESKTOPSHARED)
{
pasHost->m_pView->m_viewWindowBarOn = TRUE;
}
//
// Calculate the ideal size for this window.
//
VIEWFrameGetSize(pasHost, &rcSize);
//
// Create the frame. This will in turn create its children.
//
pasHost->m_pView->m_viewMenuBar = ::LoadMenu(g_asInstance,
MAKEINTRESOURCE(IDM_FRAME));
if (!pasHost->m_pView->m_viewMenuBar)
{
ERROR_OUT(("ViewStarting: couldn't load frame menu"));
DC_QUIT;
}
//
// Do once-only capabilities/menu stuff.
//
//
// SEND CTRL+ALT+DEL:
// Append Ctrl+Alt+Del after separator to control menu, if this
// is a view of a service host on NT.
//
if (pasHost->hetCount == HET_DESKTOPSHARED)
{
//
// Remove applications submenu
//
DeleteMenu(pasHost->m_pView->m_viewMenuBar, IDSM_WINDOW,
MF_BYPOSITION);
if ((pasHost->cpcCaps.general.typeFlags & AS_SERVICE) &&
(pasHost->cpcCaps.general.OS == CAPS_WINDOWS) &&
(pasHost->cpcCaps.general.OSVersion == CAPS_WINDOWS_NT))
{
HMENU hSubMenu;
MENUITEMINFO mii;
CHAR szMenu[32];
hSubMenu = GetSubMenu(pasHost->m_pView->m_viewMenuBar, IDSM_CONTROL);
ZeroMemory(&mii, sizeof(mii));
// Separator
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE;
mii.fType = MFT_SEPARATOR;
InsertMenuItem(hSubMenu, -1, TRUE, &mii);
// Send Ctrl-Alt-Del command
mii.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
mii.fType = MFT_STRING;
mii.fState = MFS_ENABLED;
mii.wID = CMD_CTRLALTDEL;
LoadString(g_asInstance, IDS_CMD_CTRLALTDEL, szMenu,
sizeof(szMenu));
mii.dwTypeData = szMenu;
mii.cch = lstrlen(szMenu);
InsertMenuItem(hSubMenu, -1, TRUE, &mii);
}
}
//
// FULL SCREEN:
// We only enable Full Screen for 3.0 hosts (since with 2.x desktop
// scrolling the view area can change) who have screen sizes identical
// to ours.
//
if ((pasHost->cpcCaps.general.version >= CAPS_VERSION_30) &&
(pasHost->cpcCaps.screen.capsScreenWidth ==
m_pasLocal->cpcCaps.screen.capsScreenWidth) &&
(pasHost->cpcCaps.screen.capsScreenHeight ==
m_pasLocal->cpcCaps.screen.capsScreenHeight))
{
::EnableMenuItem(pasHost->m_pView->m_viewMenuBar, CMD_VIEWFULLSCREEN,
MF_ENABLED | MF_BYCOMMAND);
}
if (m_pasLocal->m_caControlledBy)
{
WARNING_OUT(("VIEWStarting: currently controlled, create view hidden"));
}
//
// If we are currently controlled, create the frame invisible since
// we hid all the visible ones when we started being this way.
//
hwnd = CreateWindowEx(
WS_EX_WINDOWEDGE,
VIEW_FRAME_CLASS_NAME, // See RegisterClass() call.
NULL,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX |
WS_MAXIMIZEBOX | WS_CLIPCHILDREN | (!m_pasLocal->m_caControlledBy ? WS_VISIBLE : 0),
CW_USEDEFAULT,
CW_USEDEFAULT,
pasHost->viewExtent.x >= m_pasLocal->cpcCaps.screen.capsScreenWidth ?
CW_USEDEFAULT : rcSize.right - rcSize.left,
pasHost->viewExtent.y >= m_pasLocal->cpcCaps.screen.capsScreenHeight ?
CW_USEDEFAULT : rcSize.bottom - rcSize.top,
NULL,
pasHost->m_pView->m_viewMenuBar,
g_asInstance,
pasHost // Pass in person ptr
);
if (hwnd == NULL)
{
ERROR_OUT(("ViewStarting: couldn't create frame window"));
DC_QUIT;
}
//
// OK, now we've created this frame window. Go through the sizing
// process again to make sure the scrollbars are OK.
//
VIEWClientExtentChange(pasHost, FALSE);
if (!m_pasLocal->m_caControlledBy)
{
SetForegroundWindow(hwnd);
UpdateWindow(hwnd);
}
#ifdef _DEBUG
TRACE_OUT(("TIME TO SEE SOMETHING: %08d MS",
::GetTickCount() - g_asSession.scShareTime));
g_asSession.scShareTime = 0;
#endif // DEBUG
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(ASShare::VIEW_ViewStarting, rc);
return(rc);
}
//
// VIEW_ViewEnded()
//
// Called when someone we are viewing stops hosting, so we can clean up.
//
void ASShare::VIEW_ViewEnded(ASPerson * pasHost)
{
DebugEntry(ASShare::VIEW_ViewEnded);
ValidateView(pasHost);
if (pasHost->m_pView->m_viewInformDlg != NULL)
{
SendMessage(pasHost->m_pView->m_viewInformDlg, WM_COMMAND, IDCANCEL, 0);
ASSERT(!pasHost->m_pView->m_viewInformDlg);
ASSERT(!pasHost->m_pView->m_viewInformMsg);
ASSERT(IsWindowEnabled(pasHost->m_pView->m_viewFrame));
}
if (pasHost->m_pView->m_viewFrame != NULL)
{
//
// The frame is the parent of the view, toolbar, etc. Those
// should all be NULL when we return.
//
DestroyWindow(pasHost->m_pView->m_viewFrame);
ASSERT(pasHost->m_pView->m_viewFrame == NULL);
}
ASSERT(pasHost->m_pView->m_viewClient == NULL);
if (pasHost->m_pView->m_viewMenuBar != NULL)
{
::DestroyMenu(pasHost->m_pView->m_viewMenuBar);
pasHost->m_pView->m_viewMenuBar = NULL;
}
if (pasHost->m_pView->m_viewSharedRgn != NULL)
{
DeleteRgn(pasHost->m_pView->m_viewSharedRgn);
pasHost->m_pView->m_viewSharedRgn = NULL;
}
if (pasHost->m_pView->m_viewObscuredRgn != NULL)
{
DeleteRgn(pasHost->m_pView->m_viewObscuredRgn);
pasHost->m_pView->m_viewObscuredRgn = NULL;
}
//
// Destroy scratch regions
//
if (pasHost->m_pView->m_viewScratchRgn != NULL)
{
DeleteRgn(pasHost->m_pView->m_viewScratchRgn);
pasHost->m_pView->m_viewScratchRgn = NULL;
}
if (pasHost->m_pView->m_viewPaintRgn != NULL)
{
DeleteRgn(pasHost->m_pView->m_viewPaintRgn);
pasHost->m_pView->m_viewPaintRgn = NULL;
}
if (pasHost->m_pView->m_viewScreenRgn != NULL)
{
DeleteRgn(pasHost->m_pView->m_viewScreenRgn);
pasHost->m_pView->m_viewScreenRgn = NULL;
}
if (pasHost->m_pView->m_viewExtentRgn != NULL)
{
DeleteRgn(pasHost->m_pView->m_viewExtentRgn);
pasHost->m_pView->m_viewExtentRgn = NULL;
}
pasHost->m_pView->m_viewPos.x = 0;
pasHost->m_pView->m_viewPos.y = 0;
pasHost->m_pView->m_viewPage.x = 0;
pasHost->m_pView->m_viewPage.y = 0;
pasHost->m_pView->m_viewPgSize.x = 0;
pasHost->m_pView->m_viewPgSize.y = 0;
pasHost->m_pView->m_viewLnSize.x = 0;
pasHost->m_pView->m_viewLnSize.y = 0;
DebugExitVOID(ASShare::VIEW_ViewEnded);
}
//
// VIEW_InControl()
//
// Called when we start/stop controlling this host. We enable the
// toolbar, statusbar, tray, etc., and change the cursor from being the
// nodrop.
//
void ASShare::VIEW_InControl
(
ASPerson * pasHost,
BOOL fStart
)
{
POINT ptCursor;
DebugEntry(ASShare::VIEW_InControl);
//
// We're changing our state, and that affects the contents of our
// menu bar. So cancel out of menu mode, and spare problems/faults/
// inapplicable commands.
//
if (pasHost->m_pView->m_viewInMenuMode)
{
SendMessage(pasHost->m_pView->m_viewFrame, WM_CANCELMODE, 0, 0);
ASSERT(!pasHost->m_pView->m_viewInMenuMode);
}
//
// If starting in control and a message is up, kill it. Then bring our
// window to the front.
//
if (fStart)
{
if (pasHost->m_pView->m_viewInformDlg != NULL)
{
SendMessage(pasHost->m_pView->m_viewInformDlg, WM_COMMAND, IDCANCEL, 0);
ASSERT(!pasHost->m_pView->m_viewInformDlg);
ASSERT(!pasHost->m_pView->m_viewInformMsg);
ASSERT(IsWindowEnabled(pasHost->m_pView->m_viewFrame));
}
SetForegroundWindow(pasHost->m_pView->m_viewFrame);
}
//
// App Sharing (not desktop sharing) stuff
//
if (pasHost->hetCount && (pasHost->hetCount != HET_DESKTOPSHARED))
{
//
// Enable/disable window bar
//
ASSERT(IsWindow(pasHost->m_pView->m_viewWindowBar));
::EnableWindow(::GetDlgItem(pasHost->m_pView->m_viewWindowBar,
IDVIEW_ITEMS), fStart);
//
// Enable/Disable Applications submenu
//
EnableMenuItem(pasHost->m_pView->m_viewMenuBar, IDSM_WINDOW,
(fStart ? MF_ENABLED : MF_GRAYED) | MF_BYPOSITION);
if (!pasHost->m_pView->m_viewFullScreen)
{
DrawMenuBar(pasHost->m_pView->m_viewFrame);
}
}
//
// Change title bar
//
VIEW_HostStateChange(pasHost);
//
// Turn off/on shadow cursors
//
CM_UpdateShadowCursor(pasHost, fStart, pasHost->cmPos.x, pasHost->cmPos.y,
pasHost->cmHotSpot.x, pasHost->cmHotSpot.y);
//
// This will reset cursor image:
// * from nodrop to shared if in control
// * from shared to nodrop if not in control
//
// This will also, if in control, cause a mousemove to get sent to the
// host we're controlling so his cursor pos is synced with ours, if the
// mouse is over the frame client area.
//
GetCursorPos(&ptCursor);
SetCursorPos(ptCursor.x, ptCursor.y);
DebugExitVOID(ASShare::VIEW_InControl);
}
//
// VIEW_PausedInControl()
//
// Updates status bar etc. when control is paused.
//
void ASShare::VIEW_PausedInControl
(
ASPerson * pasHost,
BOOL fPaused
)
{
DebugEntry(ASShare::VIEW_PausedInControl);
ValidatePerson(pasHost);
ASSERT(pasHost->m_caControlledBy == m_pasLocal);
//
// Update status bar
//
//
// Disable/Enable window menu
//
//
// Put shadow cursors on/off
//
//
// Jiggle cursor
//
DebugExitVOID(ASShare::VIEW_PausedInControl);
}
//
// VIEW_HostStateChange()
//
// Called when a host's state has changed, via broadcast notification or
// local operations.
//
// We update the titlebar and commands.
//
void ASShare::VIEW_HostStateChange
(
ASPerson * pasHost
)
{
char szFormat[256];
char szTitleText[256];
char szOtherPart[128];
DebugEntry(ASShare::VIEW_HostStateChange);
ValidatePerson(pasHost);
//
// If this person isn't hosting anymore, don't do anything. We're
// cleaning up after him.
//
if (!pasHost->hetCount)
{
DC_QUIT;
}
//
// Make up trailing string
//
if (pasHost->m_caControlledBy)
{
LoadString(g_asInstance, IDS_TITLE_INCONTROL, szFormat, sizeof(szFormat));
wsprintf(szOtherPart, szFormat, pasHost->m_caControlledBy->scName);
}
else if (pasHost->m_caAllowControl)
{
LoadString(g_asInstance, IDS_TITLE_CONTROLLABLE, szOtherPart, sizeof(szOtherPart));
}
else
{
szOtherPart[0] = 0;
}
if (pasHost->hetCount == HET_DESKTOPSHARED)
{
LoadString(g_asInstance, IDS_TITLE_SHAREDDESKTOP, szFormat, sizeof(szFormat));
}
else
{
ASSERT(pasHost->hetCount);
LoadString(g_asInstance, IDS_TITLE_SHAREDPROGRAMS, szFormat, sizeof(szFormat));
}
wsprintf(szTitleText, szFormat, pasHost->scName, szOtherPart);
::SetWindowText(pasHost->m_pView->m_viewFrame, szTitleText);
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEW_HostStateChange);
}
//
// VIEW_UpdateStatus()
//
// Updates the PERMANENT status of this frame. When we go into menu mode,
// the strings shown are temporary only, not saved. When we come out of
// menu mode, we put back the temporary status.
//
void ASShare::VIEW_UpdateStatus
(
ASPerson * pasHost,
UINT idsStatus
)
{
DebugEntry(ASShare::VIEW_UpdateStatus);
ValidatePerson(pasHost);
pasHost->m_pView->m_viewStatus = idsStatus;
VIEWFrameSetStatus(pasHost, idsStatus);
DebugExitVOID(ASShare::VIEW_UpdateStatus);
}
void ASShare::VIEWFrameSetStatus
(
ASPerson * pasHost,
UINT idsStatus
)
{
char szStatus[256];
DebugEntry(ASShare::VIEWFrameSetStatus);
if (idsStatus != IDS_STATUS_NONE)
{
LoadString(g_asInstance, idsStatus, szStatus, sizeof(szStatus));
}
else
{
szStatus[0] = 0;
}
::SetWindowText(pasHost->m_pView->m_viewStatusBar, szStatus);
DebugExitVOID(ASShare::VIEWFrameSetStatus);
}
//
// VIEW_Message()
//
// Puts up a message to inform the end user of something.
//
void ASShare::VIEW_Message
(
ASPerson * pasHost,
UINT ids
)
{
DebugEntry(ASShare::VIEW_Message);
ValidateView(pasHost);
if (!pasHost->m_pView)
{
WARNING_OUT(("Can't show VIEW message; [%d] not hosting", pasHost->mcsID));
DC_QUIT;
}
if (pasHost->m_pView->m_viewInformDlg)
{
// Kill the previous one
TRACE_OUT(("Killing previous informational mesage for [%d]",
pasHost->mcsID));
SendMessage(pasHost->m_pView->m_viewInformDlg, WM_COMMAND, IDCANCEL, 0);
ASSERT(!pasHost->m_pView->m_viewInformDlg);
ASSERT(!pasHost->m_pView->m_viewInformMsg);
}
if (m_pasLocal->m_caControlledBy)
{
WARNING_OUT(("VIEW_Message: ignoring, view is hidden since we're controlled"));
}
else
{
pasHost->m_pView->m_viewInformMsg = ids;
pasHost->m_pView->m_viewInformDlg = CreateDialogParam(g_asInstance,
((ids != IDS_ABOUT) ? MAKEINTRESOURCE(IDD_INFORM) : MAKEINTRESOURCE(IDD_ABOUT)),
pasHost->m_pView->m_viewFrame, VIEWDlgProc, (LPARAM)pasHost);
if (!pasHost->m_pView->m_viewInformDlg)
{
ERROR_OUT(("Failed to create inform message box for [%d]",
pasHost->mcsID));
pasHost->m_pView->m_viewInformMsg = 0;
}
}
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEW_Message);
}
//
// VIEWStartControlled()
//
// If we are about to start being controlled, we hide all the frames
// to get them out of the way AND prevent hangs caused by modal loop code
// in Win9x title bar dragging.
//
void ASShare::VIEWStartControlled(BOOL fStart)
{
ASPerson * pasT;
DebugEntry(ASShare::VIEWStartControlled);
for (pasT = m_pasLocal; pasT != NULL; pasT = pasT->pasNext)
{
if (pasT->m_pView)
{
if (fStart)
{
ASSERT(IsWindowVisible(pasT->m_pView->m_viewFrame));
ShowOwnedPopups(pasT->m_pView->m_viewFrame, FALSE);
SetWindowPos(pasT->m_pView->m_viewFrame, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER |
SWP_HIDEWINDOW);
}
else
{
ASSERT(!IsWindowVisible(pasT->m_pView->m_viewFrame));
SetWindowPos(pasT->m_pView->m_viewFrame, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER |
SWP_SHOWWINDOW);
ShowOwnedPopups(pasT->m_pView->m_viewFrame, TRUE);
}
}
}
DebugEntry(ASShare::VIEWStartControlled);
}
//
// VIEW_DlgProc()
//
// Handles informing user dialog
//
INT_PTR CALLBACK VIEWDlgProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
return(g_asSession.pShare->VIEW_DlgProc(hwnd, message, wParam, lParam));
}
BOOL ASShare::VIEW_DlgProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
BOOL rc = TRUE;
ASPerson * pasHost;
DebugEntry(VIEW_DlgProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pasHost)
{
ValidateView(pasHost);
}
switch (message)
{
case WM_INITDIALOG:
{
char szT[256];
char szRes[512];
RECT rc;
RECT rcOwner;
pasHost = (ASPerson *)lParam;
ValidateView(pasHost);
pasHost->m_pView->m_viewInformDlg = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
ASSERT(pasHost->m_pView->m_viewInformMsg);
if (pasHost->m_pView->m_viewInformMsg == IDS_ABOUT)
{
// About box
GetDlgItemText(hwnd, CTRL_ABOUTVERSION, szT, sizeof(szT));
wsprintf(szRes, szT, VER_PRODUCTRELEASE_STR,
VER_PRODUCTVERSION_STR);
SetDlgItemText(hwnd, CTRL_ABOUTVERSION, szRes);
}
else
{
HDC hdc;
HFONT hfn;
// Set title.
if ((pasHost->m_pView->m_viewInformMsg >= IDS_ERR_TAKECONTROL_FIRST) &&
(pasHost->m_pView->m_viewInformMsg <= IDS_ERR_TAKECONTROL_LAST))
{
LoadString(g_asInstance, IDS_TITLE_TAKECONTROL_FAILED,
szT, sizeof(szT));
SetWindowText(hwnd, szT);
}
// Set message
LoadString(g_asInstance, pasHost->m_pView->m_viewInformMsg,
szT, sizeof(szT));
wsprintf(szRes, szT, pasHost->scName);
SetDlgItemText(hwnd, CTRL_INFORM, szRes);
// Center the message vertically
GetWindowRect(GetDlgItem(hwnd, CTRL_INFORM), &rcOwner);
MapWindowPoints(NULL, hwnd, (LPPOINT)&rcOwner, 2);
rc = rcOwner;
hdc = GetDC(hwnd);
hfn = (HFONT)SendDlgItemMessage(hwnd, CTRL_INFORM, WM_GETFONT, 0, 0);
hfn = SelectFont(hdc, hfn);
DrawText(hdc, szRes, -1, &rc, DT_NOCLIP | DT_EXPANDTABS |
DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
SelectFont(hdc, hfn);
ReleaseDC(hwnd, hdc);
ASSERT((rc.bottom - rc.top) <= (rcOwner.bottom - rcOwner.top));
SetWindowPos(GetDlgItem(hwnd, CTRL_INFORM), NULL,
rcOwner.left,
((rcOwner.top + rcOwner.bottom) - (rc.bottom - rc.top)) / 2,
(rcOwner.right - rcOwner.left),
rc.bottom - rc.top,
SWP_NOACTIVATE | SWP_NOZORDER);
}
// Disable owner
EnableWindow(pasHost->m_pView->m_viewFrame, FALSE);
// Show window, centered around owner midpoint
GetWindowRect(pasHost->m_pView->m_viewFrame, &rcOwner);
GetWindowRect(hwnd, &rc);
SetWindowPos(hwnd, NULL,
((rcOwner.left + rcOwner.right) - (rc.right - rc.left)) / 2,
((rcOwner.top + rcOwner.bottom) - (rc.bottom - rc.top)) / 2,
0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
break;
}
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
case IDCANCEL:
{
ASSERT(!IsWindowEnabled(pasHost->m_pView->m_viewFrame));
EnableWindow(pasHost->m_pView->m_viewFrame, TRUE);
DestroyWindow(hwnd);
break;
}
}
break;
}
case WM_DESTROY:
{
if (pasHost)
{
pasHost->m_pView->m_viewInformDlg = NULL;
pasHost->m_pView->m_viewInformMsg = 0;
}
SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
break;
}
default:
{
rc = FALSE;
break;
}
}
DebugExitBOOL(VIEW_DlgProc, rc);
return(rc);
}
//
// VIEW_RecalcExtent()
//
// This recalculates the extent of the view of the host.
//
void ASShare::VIEW_RecalcExtent(ASPerson * pasHost)
{
DebugEntry(ASShare::VIEW_RecalcExtent);
TRACE_OUT(("VIEW_RecalcExtent: New view extent (%04d, %04d) for [%d] version %x",
pasHost->viewExtent.x, pasHost->viewExtent.y,
pasHost->mcsID, pasHost->cpcCaps.general.version));
//
// Compute the extent of the view:
// For 2.x dudes, it's the VD size (union of all hosts)
// For 3.0 dudes, it's the host screen size
//
// REMOVE THIS WHEN 2.X COMPAT IS GONE
//
if (pasHost->cpcCaps.general.version >= CAPS_VERSION_30)
{
pasHost->viewExtent.x = pasHost->cpcCaps.screen.capsScreenWidth;
pasHost->viewExtent.y = pasHost->cpcCaps.screen.capsScreenHeight;
}
else
{
//
// We do this so that the window is created the right size in the
// first place. Then in VIEW_RecalcVD nothing will happen to it,
// because the extent won't alter.
//
pasHost->viewExtent.x = max(m_viewVDSize.x, pasHost->cpcCaps.screen.capsScreenWidth);
pasHost->viewExtent.y = max(m_viewVDSize.y, pasHost->cpcCaps.screen.capsScreenHeight);
}
DebugExitVOID(ASShare::VIEW_RecalcExtent);
}
//
// VIEW_RecalcVD()
// This recalculates the virtual desktop size when a remote starts/stops
// sharing, or if a host's screen changes size. The VD is the union of
// all the screen sizes of those hosting. 2.x nodes work in a virtual
// desktop, and may scroll over. For each 2.x view, we want the client to
// represent the VD, but with only the stuff on screen on the host to be
// interactable.
//
void ASShare::VIEW_RecalcVD(void)
{
POINT ptVDNew;
ASPerson * pas;
DebugEntry(ASShare::VIEW_RecalcVD);
//
// First, loop through all the hosts and recompute the VD.
//
ptVDNew.x = 0;
ptVDNew.y = 0;
for (pas = m_pasLocal; pas != NULL; pas = pas->pasNext)
{
//
// NOTE:
// For local dudes, we won't have an HWND. Use viewExtent, if
// we don't think the person is sharing, it will be zero.
//
if (pas->viewExtent.x != 0)
{
TRACE_OUT(("VIEW_RecalcVD: Found host [%d], screen size (%04d, %04d)",
pas->mcsID, pas->cpcCaps.screen.capsScreenWidth, pas->cpcCaps.screen.capsScreenHeight));
ptVDNew.x = max(ptVDNew.x, pas->cpcCaps.screen.capsScreenWidth);
ptVDNew.y = max(ptVDNew.y, pas->cpcCaps.screen.capsScreenHeight);
TRACE_OUT(("VIEW_RecalcVD: Computed VD size now (%04d, %04d)",
ptVDNew.x, ptVDNew.y));
}
}
//
// If the VD size didn't change, we're done.
//
if ((ptVDNew.x != m_viewVDSize.x) || (ptVDNew.y != m_viewVDSize.y))
{
TRACE_OUT(("VIEW_RecalcVD: VD size changed from (%04d, %04d) to (%04d, %04d)",
m_viewVDSize.x, m_viewVDSize.y, ptVDNew.x, ptVDNew.y));
m_viewVDSize = ptVDNew;
//
// Now loop through all the 2.x hosts, and update their extent, then
// have them do the resize voodoo so the scrollbar pos isn't out of
// range, etc.
//
// NOTE: Since us, the local guy, is not 2.x we can skip ourselves.
//
ValidatePerson(m_pasLocal);
for (pas = m_pasLocal->pasNext; pas != NULL; pas = pas->pasNext)
{
if ((pas->cpcCaps.general.version < CAPS_VERSION_30) && (pas->m_pView != NULL))
{
ASSERT(m_viewVDSize.x != 0);
ASSERT(m_viewVDSize.y != 0);
// Potential resize/range change
if ((pas->viewExtent.x != m_viewVDSize.x) ||
(pas->viewExtent.y != m_viewVDSize.y))
{
TRACE_OUT(("VIEW_RecalcVD: Found 2.x host [%d], must update old extent (%04d, %04d)",
pas->mcsID, pas->viewExtent.x, pas->viewExtent.y));
VIEW_RecalcExtent(pas);
VIEWClientExtentChange(pas, TRUE);
}
}
}
}
DebugExitVOID(ASShare::VIEW_RecalcVD);
}
//
// VIEW_IsPointShared()
// This determines, given a point relative to the client of the view for
// the remote on this system, if it is in a shared area.
//
BOOL ASShare::VIEW_IsPointShared
(
ASPerson * pasHost,
POINT ptLocal
)
{
BOOL rc = FALSE;
RECT rcClient;
DebugEntry(ASShare::VIEW_IsPointShared);
ValidateView(pasHost);
//
// Convert to client coords, and adjust for scrolling offset. That
// result is the equivalent point on the host desktop.
//
GetClientRect(pasHost->m_pView->m_viewClient, &rcClient);
if (!PtInRect(&rcClient, ptLocal))
{
TRACE_OUT(("VIEW_IsPointShared: point not in client area"));
return(FALSE);
}
//
// The obscured and shared areas are saved in frame client coords,
// so we don't need to account for the scroll position all the time.
// When the scroll position changes the regions are updated.
//
//
// NOTE that this order works for both desktop and app sharing
//
if ((pasHost->m_pView->m_viewObscuredRgn != NULL) &&
PtInRegion(pasHost->m_pView->m_viewObscuredRgn, ptLocal.x, ptLocal.y))
{
rc = FALSE;
}
else if ((pasHost->m_pView->m_viewSharedRgn != NULL) &&
!PtInRegion(pasHost->m_pView->m_viewSharedRgn, ptLocal.x, ptLocal.y))
{
rc = FALSE;
}
else
{
//
// 2.x hosts may be scrolled over. If so, shared stuff offscreen
// is also considered to be obscured.
//
RECT rcScreen;
//
// Compute what part of the VD, in local client coords, is visible
// on the remote's screen.
//
SetRect(&rcScreen, 0, 0, pasHost->cpcCaps.screen.capsScreenWidth, pasHost->cpcCaps.screen.capsScreenHeight);
OffsetRect(&rcScreen,
pasHost->m_pView->m_dsScreenOrigin.x - pasHost->m_pView->m_viewPos.x,
pasHost->m_pView->m_dsScreenOrigin.y - pasHost->m_pView->m_viewPos.y);
if (!PtInRect(&rcScreen, ptLocal))
{
TRACE_OUT(("VIEW_IsPointShared: point is in shared stuff but not visible on remote screen"));
rc = FALSE;
}
else
{
rc = TRUE;
}
}
DebugExitBOOL(AShare::VIEW_IsPointShared, rc);
return(rc);
}
//
// VIEW_ScreenChanged()
//
void ASShare::VIEW_ScreenChanged(ASPerson * pasPerson)
{
DebugEntry(ASShare::VIEW_ScreenChanged);
ValidatePerson(pasPerson);
//
// Recompute the extent
//
VIEW_RecalcExtent(pasPerson);
VIEWClientExtentChange(pasPerson, TRUE);
VIEW_RecalcVD();
DebugExitVOID(ASShare::VIEW_ScreenChanged);
}
//
// VIEW_SetHostRegions()
// This sets the new shared & obscured areas.
//
// Note that this routine takes responsibility for the regions pass in; it
// will delete them and/or the old ones if necessary.
//
void ASShare::VIEW_SetHostRegions
(
ASPerson * pasHost,
HRGN rgnShared,
HRGN rgnObscured
)
{
DebugEntry(ASShare::VIEW_SetHostRegions);
ValidateView(pasHost);
//
// Return immediately if either region handle is bogus. This can happen
// when we are running low on memory.
//
if (!rgnShared || !rgnObscured)
{
ERROR_OUT(("Bad host regions for person [%u]", pasHost->mcsID));
if (rgnShared != NULL)
{
DeleteRgn(rgnShared);
}
if (rgnObscured != NULL)
{
DeleteRgn(rgnObscured);
}
}
else
{
HRGN hrgnInvalid;
#ifdef _DEBUG
RECT rcT;
::GetRgnBox(rgnShared, &rcT);
TRACE_OUT(("Shared region {%04d, %04d, %04d, %04d} for host [%d]",
rcT.left, rcT.top, rcT.right, rcT.bottom, pasHost->mcsID));
::GetRgnBox(rgnObscured, &rcT);
TRACE_OUT(("Obscured region {%04d, %04d, %04d, %04d} for host [%d]",
rcT.left, rcT.top, rcT.right, rcT.bottom, pasHost->mcsID));
#endif // _DEBUG
//
// Update the current shared, obscured areas. Adjust for the
// scroll position so these are saved in client-relative coords.
//
OffsetRgn(rgnShared, -pasHost->m_pView->m_viewPos.x, -pasHost->m_pView->m_viewPos.y);
OffsetRgn(rgnObscured, -pasHost->m_pView->m_viewPos.x, -pasHost->m_pView->m_viewPos.y);
//
// The invalid area is whatever changed in the obscured area and
// the shared area. In other words, the union - the intersection.
//
hrgnInvalid = NULL;
if (pasHost->m_pView->m_viewSharedRgn != NULL)
{
HRGN hrgnU;
HRGN hrgnI;
ASSERT(pasHost->m_pView->m_viewObscuredRgn != NULL);
//
// If we're in a low memory situation, just invalidate everything
// and hope it can be repainted.
//
hrgnU = CreateRectRgn(0, 0, 0, 0);
hrgnI = CreateRectRgn(0, 0, 0, 0);
if (!hrgnU || !hrgnI)
goto SkipMinimalInvalidate;
hrgnInvalid = CreateRectRgn(0, 0, 0, 0);
if (!hrgnInvalid)
goto SkipMinimalInvalidate;
//
// WE'RE GOING TO DO THE SAME THING FOR BOTH SHARED AND
// OBSCURED REGIONS.
//
// Get the union of the old and new shared regions
UnionRgn(hrgnU, pasHost->m_pView->m_viewSharedRgn, rgnShared);
// Get the intersection of the old and new shared regions
IntersectRgn(hrgnI, pasHost->m_pView->m_viewSharedRgn, rgnShared);
//
// The intersection is what used to be shared and is still shared.
// The rest is changing, it needs to be repainted. That's the
// union minus the intersection.
//
SubtractRgn(hrgnU, hrgnU, hrgnI);
#ifdef _DEBUG
GetRgnBox(hrgnU, &rcT);
TRACE_OUT(("VIEW_SetHostRegions: Shared area change {%04d, %04d, %04d, %04d}",
rcT.left, rcT.top, rcT.right, rcT.bottom));
#endif // _DEBUG
// Add this to the invalidate total
UnionRgn(hrgnInvalid, hrgnInvalid, hrgnU);
//
// REPEAT FOR THE OBSCURED REGION
//
UnionRgn(hrgnU, pasHost->m_pView->m_viewObscuredRgn, rgnObscured);
IntersectRgn(hrgnI, pasHost->m_pView->m_viewObscuredRgn, rgnObscured);
SubtractRgn(hrgnU, hrgnU, hrgnI);
#ifdef _DEBUG
GetRgnBox(hrgnU, &rcT);
TRACE_OUT(("VIEW_SetHostRegions: Obscured area change {%04d, %04d, %04d, %04d}",
rcT.left, rcT.top, rcT.right, rcT.bottom));
#endif // _DEBUG
UnionRgn(hrgnInvalid, hrgnInvalid, hrgnU);
SkipMinimalInvalidate:
//
// Clean up scratch regions
//
if (hrgnI != NULL)
DeleteRgn(hrgnI);
if (hrgnU != NULL)
DeleteRgn(hrgnU);
DeleteRgn(pasHost->m_pView->m_viewSharedRgn);
pasHost->m_pView->m_viewSharedRgn = rgnShared;
DeleteRgn(pasHost->m_pView->m_viewObscuredRgn);
pasHost->m_pView->m_viewObscuredRgn = rgnObscured;
//
// DO NOT CALL VIEW_InvalidateRgn here, that expects a region in
// screen coords of pasHost. We have a region that is
// client coords relative. So just call InvalidateRgn() directly.
//
InvalidateRgn(pasHost->m_pView->m_viewClient, hrgnInvalid, FALSE);
if (hrgnInvalid != NULL)
DeleteRgn(hrgnInvalid);
}
else
{
RECT rcBound;
// The shared & obscured regions are both NULL or both non-NULL
ASSERT(pasHost->m_pView->m_viewObscuredRgn == NULL);
pasHost->m_pView->m_viewSharedRgn = rgnShared;
pasHost->m_pView->m_viewObscuredRgn = rgnObscured;
//
// This is the first SWL packet we've received. Snap the
// scrollbars to the start of the total shared area. This avoids
// having the view come up, but look empty because the shared
// apps are out of the range. We do this even if the user
// scrolled around in the window already.
//
// The total shared area is the union of the real shared +
// obscured shared areas. Convert back to remote VD coords!
//
UnionRgn(pasHost->m_pView->m_viewScratchRgn, rgnShared, rgnObscured);
GetRgnBox(pasHost->m_pView->m_viewScratchRgn, &rcBound);
OffsetRect(&rcBound, pasHost->m_pView->m_viewPos.x, pasHost->m_pView->m_viewPos.y);
//
// Is any part of what was shared within the extent of the view?
// If not, we can't do anything--there's nothing to show.
//
if ((rcBound.right <= 0) ||
(rcBound.left >= pasHost->viewExtent.x) ||
(rcBound.bottom <= 0) ||
(rcBound.top >= pasHost->viewExtent.y))
{
TRACE_OUT(("VIEW_SetHostRegions: Can't snap to shared area; none is visible"));
}
else
{
//
// Use top left corner of bounds
// VIEWClientScroll() will pin position w/in range
//
VIEWClientScroll(pasHost, rcBound.left, rcBound.top);
}
InvalidateRgn(pasHost->m_pView->m_viewClient, NULL, FALSE);
}
}
DebugExitVOID(ASShare::VIEW_SetHostRegions);
}
//
// VIEW_InvalidateRect()
// Repaints the given rect. This is for EXTERNAL code which passes in VD
// coords. We convert to client coordinates by accounting for the scroll
// position.
//
void ASShare::VIEW_InvalidateRect
(
ASPerson * pasPerson,
LPRECT lprc
)
{
DebugEntry(ASShare::VIEW_InvalidateRect);
ValidateView(pasPerson);
//
// Convert to client coords
//
if (lprc != NULL)
{
OffsetRect(lprc, -pasPerson->m_pView->m_viewPos.x, -pasPerson->m_pView->m_viewPos.y);
}
InvalidateRect(pasPerson->m_pView->m_viewClient, lprc, FALSE);
//
// Convert back so caller doesn't get a modified lprc
//
if (lprc != NULL)
{
OffsetRect(lprc, pasPerson->m_pView->m_viewPos.x, pasPerson->m_pView->m_viewPos.y);
}
DebugExitVOID(ASShare::VIEW_InvalidateRect);
}
//
// VIEW_InvalidateRgn()
// Repaints the given region. This is for EXTERNAL code which passes in VD
// coords. We convert to client coordinates by accounting fo the scroll
// position.
//
void ASShare::VIEW_InvalidateRgn
(
ASPerson * pasHost,
HRGN rgnInvalid
)
{
#ifdef _DEBUG
//
// Make sure we the invalid region goes back to the caller unaltered,
// even though we modify it temporarily here to avoid a copy.
//
RECT rcBoundBefore;
RECT rcBoundAfter;
#endif // _DEBUG
DebugEntry(ASShare::VIEW_InvalidateRgn);
ValidatePerson(pasHost);
//
// Adjust the region if the frame view is scrolled over.
//
if (rgnInvalid != NULL)
{
#ifdef _DEBUG
GetRgnBox(rgnInvalid, &rcBoundBefore);
#endif // _DEBUG
OffsetRgn(rgnInvalid, -pasHost->m_pView->m_viewPos.x, -pasHost->m_pView->m_viewPos.y);
#ifdef _DEBUG
TRACE_OUT(("VIEW_InvalidateRgn: Invalidating area {%04d, %04d, %04d, %04d}",
rcBoundBefore.left, rcBoundBefore.top, rcBoundBefore.right, rcBoundBefore.bottom));
#endif // _DEBUG
}
else
{
TRACE_OUT(("VIEW_InvalidateRgn: Invalidating entire client area"));
}
InvalidateRgn(pasHost->m_pView->m_viewClient, rgnInvalid, FALSE);
if (rgnInvalid != NULL)
{
OffsetRgn(rgnInvalid, pasHost->m_pView->m_viewPos.x, pasHost->m_pView->m_viewPos.y);
#ifdef _DEBUG
GetRgnBox(rgnInvalid, &rcBoundAfter);
ASSERT(EqualRect(&rcBoundBefore, &rcBoundAfter));
#endif // _DEBUG
}
DebugExitVOID(ASShare::VIEW_InvalidateRgn);
}
//
// VIEWClientExtentChange()
//
void ASShare::VIEWClientExtentChange(ASPerson * pasHost, BOOL fRedraw)
{
RECT rcl;
SCROLLINFO si;
DebugEntry(ASShare::VIEWClientExtentChange);
ValidatePerson(pasHost);
if (!pasHost->m_pView)
DC_QUIT;
#ifdef _DEBUG
//
// The client area (page size) shouldn't have changed. Only the
// extent has.
//
GetClientRect(pasHost->m_pView->m_viewClient, &rcl);
ASSERT(pasHost->m_pView->m_viewPage.x == rcl.right - rcl.left);
ASSERT(pasHost->m_pView->m_viewPage.y == rcl.bottom - rcl.top);
#endif // _DEBUG
pasHost->m_pView->m_viewPgSize.x = pasHost->viewExtent.x / 8;
pasHost->m_pView->m_viewPgSize.y = pasHost->viewExtent.y / 8;
pasHost->m_pView->m_viewLnSize.x = pasHost->viewExtent.x / 64;
pasHost->m_pView->m_viewLnSize.y = pasHost->viewExtent.y / 64;
//
// Move the scroll position to the origin.
//
//
// Clip the current scroll pos if we need to, now that the extent
// has changed size.
//
VIEWClientScroll(pasHost, pasHost->m_pView->m_viewPos.x, pasHost->m_pView->m_viewPos.y);
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_DISABLENOSCROLL;
// Set vertical info. Is vert pos out of range now?
si.nMin = 0;
si.nMax = pasHost->viewExtent.y - 1;
si.nPage = pasHost->m_pView->m_viewPage.y;
si.nPos = pasHost->m_pView->m_viewPos.y;
ASSERT(si.nPos <= si.nMax);
TRACE_OUT(("VIEWClientExtentChange: Setting VERT scroll info:"));
TRACE_OUT(("VIEWClientExtentChange: nMin %04d", si.nMin));
TRACE_OUT(("VIEWClientExtentChange: nMax %04d", si.nMax));
TRACE_OUT(("VIEWClientExtentChange: nPage %04d", si.nPage));
TRACE_OUT(("VIEWClientExtentChange: nPos %04d", si.nPos));
SetScrollInfo(pasHost->m_pView->m_viewClient, SB_VERT, &si, TRUE );
// Set horizontal (x) information
si.nMin = 0;
si.nMax = pasHost->viewExtent.x - 1;
si.nPage = pasHost->m_pView->m_viewPage.x;
si.nPos = pasHost->m_pView->m_viewPos.x;
ASSERT(si.nPos <= si.nMax);
TRACE_OUT(("VIEWClientExtentChange: Setting HORZ scroll info:"));
TRACE_OUT(("VIEWClientExtentChange: nMin %04d", si.nMin));
TRACE_OUT(("VIEWClientExtentChange: nMax %04d", si.nMax));
TRACE_OUT(("VIEWClientExtentChange: nPage %04d", si.nPage));
TRACE_OUT(("VIEWClientExtentChange: nPos %04d", si.nPos));
SetScrollInfo(pasHost->m_pView->m_viewClient, SB_HORZ, &si, TRUE );
if (fRedraw)
{
// Is the frame window too big now?
if ( (pasHost->m_pView->m_viewPage.x > pasHost->viewExtent.x) ||
(pasHost->m_pView->m_viewPage.y > pasHost->viewExtent.y) )
{
TRACE_OUT(("VIEWClientExtentChange: client size (%04d, %04d) now bigger than view extent (%04d, %04d)",
pasHost->m_pView->m_viewPage.x, pasHost->m_pView->m_viewPage.y,
pasHost->viewExtent.x, pasHost->viewExtent.y));
//
// Calculate the ideal size for this window.
//
VIEWFrameGetSize(pasHost, &rcl);
SetWindowPos( pasHost->m_pView->m_viewFrame,
NULL, 0, 0, rcl.right - rcl.left, rcl.bottom - rcl.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
TRACE_OUT(("VIEWClientExtentChange: Invalidating client area"));
VIEW_InvalidateRgn(pasHost, NULL);
}
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEWClientExtentChange);
}
//
// VIEWFrameWindowProc()
//
LRESULT CALLBACK VIEWFrameWindowProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
return(g_asSession.pShare->VIEW_FrameWindowProc(hwnd, message, wParam, lParam));
}
LRESULT ASShare::VIEW_FrameWindowProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT rc = 0;
ASPerson * pasHost;
DebugEntry(VIEW_FrameWindowProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pasHost)
{
ValidateView(pasHost);
}
switch (message)
{
case WM_NCCREATE:
{
// Get the passed in host pointer, and set in our window long
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
pasHost->m_pView->m_viewFrame = hwnd;
//
// Set the window icon
//
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)
((pasHost->hetCount == HET_DESKTOPSHARED) ?
g_hetDeskIcon : g_hetASIcon));
goto DefWndProc;
break;
}
case WM_NCDESTROY:
{
if (pasHost != NULL)
{
pasHost->m_pView->m_viewFrame = NULL;
}
goto DefWndProc;
break;
}
case WM_CREATE:
{
// Set title
VIEW_HostStateChange(pasHost);
if (!VIEWFrameCreate(pasHost))
{
ERROR_OUT(("VIEWFrameWindowProc: errors in creation handling for [%d]", pasHost->mcsID));
rc = -1;
}
break;
}
case WM_DESTROY:
{
//
// Clear menu bar; we always destroy it ourself.
//
::SetMenu(hwnd, NULL);
break;
}
case WM_ACTIVATE:
{
//
// If we're switching back to the view of the host we're in
// control of, update the key states.
//
if (wParam)
{
SetFocus(pasHost->m_pView->m_viewClient);
}
else
{
//
// If we're full screen but are deactivating, kick out of
// full screenmode.
//
if (pasHost->m_pView->m_viewFullScreen)
{
// Do this later, so title bar state isn't messed up
::PostMessage(hwnd, WM_COMMAND, MAKELONG(CMD_VIEWFULLSCREEN, 0), 0);
}
}
break;
}
case WM_ENTERMENULOOP:
{
pasHost->m_pView->m_viewInMenuMode = TRUE;
break;
}
case WM_EXITMENULOOP:
{
pasHost->m_pView->m_viewInMenuMode = FALSE;
break;
}
case WM_COMMAND:
{
VIEWFrameCommand(pasHost, wParam, lParam);
break;
}
case WM_INITMENU:
{
if ((HMENU)wParam == pasHost->m_pView->m_viewMenuBar)
{
VIEWFrameInitMenuBar(pasHost);
}
break;
}
case WM_MENUSELECT:
{
VIEWFrameOnMenuSelect(pasHost, wParam, lParam);
break;
}
case WM_PALETTECHANGED:
//
// The system palette has changed - repaint the window.
//
VIEW_InvalidateRgn(pasHost, NULL);
//
// The system palette has changed. If we are not the
// window that triggered this message then realize our
// palette now to set up our new palette mapping.
//
if ((HWND)wParam == hwnd)
{
//
// If this window caused the change return without
// realizing our logical palette or we could end up in
// an infinite loop.
//
break;
}
TRACE_OUT(("Palette changed - fall through to realize palette (%x)",
hwnd));
//
// Do not break here but FALL THROUGH to the code which
// realizes the remote palette into this window. This allows
// the window to grab some color entries for itself in the new
// system palette.
//
case WM_QUERYNEWPALETTE:
rc = FALSE;
if (message == WM_QUERYNEWPALETTE)
{
TRACE_OUT(( "WM_QUERYNEWPALETTE hwnd(%x)", hwnd));
}
if (g_usrPalettized)
{
HDC hdc;
HPALETTE hPalOld;
UINT cChangedEntries;
//
// Realize this window's palette, and force a repaint
// if necessary.
//
hdc = GetDC(hwnd);
hPalOld = SelectPalette(hdc, pasHost->pmPalette, FALSE);
cChangedEntries = RealizePalette(hdc);
SelectPalette(hdc, hPalOld, FALSE);
ReleaseDC(hwnd, hdc);
rc = (cChangedEntries > 0);
if (rc)
{
// Have to repaint this window
VIEW_InvalidateRgn(pasHost, NULL);
}
}
break;
case WM_GETMINMAXINFO:
{
RECT rc;
LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam;
int cx,cy;
if (!pasHost)
{
// We're not created yet; bail.
break;
}
//
// Calculate the ideal maximized size for this window
//
VIEWFrameGetSize(pasHost, &rc);
//
// If it's bigger than the local screen, clip it.
//
cx = min(rc.right - rc.left, m_pasLocal->cpcCaps.screen.capsScreenWidth);
cy = min(rc.bottom - rc.top, m_pasLocal->cpcCaps.screen.capsScreenHeight);
lpmmi->ptMaxSize.x = cx;
lpmmi->ptMaxSize.y = cy;
lpmmi->ptMaxTrackSize.x = cx;
lpmmi->ptMaxTrackSize.y = cy;
//
// Make sure that we don't size this window too narrow. Keep
// space for borders and one window bar button + scroll ctl.
//
lpmmi->ptMinTrackSize.x = 2*::GetSystemMetrics(SM_CXSIZEFRAME) +
(m_viewItemCX + m_viewEdgeCX) + m_viewItemScrollCX;
//
// And prevent sizing too short. Keep space for borders, menu
// bar, status bar, and window bar
//
lpmmi->ptMinTrackSize.y = 2*::GetSystemMetrics(SM_CYSIZEFRAME) +
::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYMENU);
if (pasHost->m_pView->m_viewWindowBarOn)
{
lpmmi->ptMinTrackSize.y += m_viewWindowBarCY + m_viewEdgeCY;
}
if (pasHost->m_pView->m_viewStatusBarOn)
{
lpmmi->ptMinTrackSize.y += m_viewStatusBarCY + m_viewEdgeCY;
}
break;
}
case WM_SIZE:
{
if (wParam != SIZE_MINIMIZED)
{
VIEWFrameResize(pasHost);
}
break;
}
default:
DefWndProc:
rc = DefWindowProc(hwnd, message, wParam, lParam);
break;
}
DebugExitDWORD(ASShare::VIEW_FrameWindowProc, rc);
return(rc);
}
//
// VIEWFrameCreate()
//
BOOL ASShare::VIEWFrameCreate(ASPerson * pasPerson)
{
RECT rect;
BOOL rc = FALSE;
DebugEntry(VIEWFrameCreate);
ValidateView(pasPerson);
//
// Creates the children which lie in the frame's client:
// * the toolbar hugs the top
// * the statusbar hugs the bottom
// * the tray hugs the left underneath the toolbar and above the
// statusbar
// * the view fills in what's left
//
GetClientRect(pasPerson->m_pView->m_viewFrame, &rect);
//
// Create the statusbar (hugs bottom)
//
pasPerson->m_pView->m_viewStatusBar = ::CreateWindowEx(0, STATUSCLASSNAME,
NULL, WS_CHILD | WS_VISIBLE | CCS_NOPARENTALIGN | CCS_NOMOVEY | CCS_NORESIZE |
SBARS_SIZEGRIP,
rect.left, rect.bottom - m_viewStatusBarCY, rect.right - rect.left,
m_viewStatusBarCY, pasPerson->m_pView->m_viewFrame, NULL, g_asInstance,
NULL);
if (!pasPerson->m_pView->m_viewStatusBar)
{
ERROR_OUT(("Couldn't create statusbar for frame of person [%d]", pasPerson->mcsID));
DC_QUIT;
}
rect.bottom -= m_viewStatusBarCY + m_viewEdgeCY;
//
// Create the tray (hugs top of status bar, bottom of view)
// BUT NOT FOR DESKTOP SHARING
//
if (pasPerson->hetCount != HET_DESKTOPSHARED)
{
pasPerson->m_pView->m_viewWindowBar = ::CreateWindowEx(0,
VIEW_WINDOWBAR_CLASS_NAME, NULL,
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_CHILD,
rect.left, rect.bottom - m_viewWindowBarCY,
rect.right - rect.left, m_viewWindowBarCY,
pasPerson->m_pView->m_viewFrame, NULL, g_asInstance, pasPerson);
if (!pasPerson->m_pView->m_viewWindowBar)
{
ERROR_OUT(("VIEWFrameCreate: Failed to create window bar"));
DC_QUIT;
}
// Subtract tray space + an edge above it of margin
rect.bottom -= m_viewWindowBarCY + m_viewEdgeCY;
}
//
// Create the view (takes up rest of client)
//
if (!CreateWindowEx(WS_EX_CLIENTEDGE,
VIEW_CLIENT_CLASS_NAME, NULL,
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_CHILD |
WS_VSCROLL | WS_HSCROLL,
rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
pasPerson->m_pView->m_viewFrame,
NULL, g_asInstance, pasPerson))
{
ERROR_OUT(("VIEWFrameCreate: Failed to create view"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(ASShare::VIEWFrameCreate, rc);
return(rc);
}
//
// VIEWFrameResize()
// Repositions the child windows when the frame is resized.
//
void ASShare::VIEWFrameResize(ASPerson * pasPerson)
{
RECT rect;
DebugEntry(ASShare::VIEWFrameResize);
ValidateView(pasPerson);
GetClientRect(pasPerson->m_pView->m_viewFrame, &rect);
//
// Move the statusbar
//
if ((pasPerson->m_pView->m_viewStatusBar != NULL) &&
(pasPerson->m_pView->m_viewStatusBarOn))
{
MoveWindow(pasPerson->m_pView->m_viewStatusBar, rect.left,
rect.bottom - m_viewStatusBarCY, rect.right - rect.left,
m_viewStatusBarCY, TRUE);
rect.bottom -= m_viewStatusBarCY + m_viewEdgeCY;
}
//
// Move the tray
//
if ((pasPerson->m_pView->m_viewWindowBar != NULL) &&
(pasPerson->m_pView->m_viewWindowBarOn))
{
MoveWindow(pasPerson->m_pView->m_viewWindowBar, rect.left,
rect.bottom - m_viewWindowBarCY, rect.right - rect.left,
m_viewWindowBarCY, TRUE);
rect.bottom -= m_viewWindowBarCY + m_viewEdgeCY;
}
//
// Move the view
//
MoveWindow(pasPerson->m_pView->m_viewClient, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, TRUE);
DebugExitVOID(ASShare::VIEWFrameResize);
}
//
// VIEWFrameResizeChanged()
//
// Called when the widgets of the frame (the status bar, the window bar, etc.)
// come or go. We may need to shrink the window, if the view is going
// to end up being bigger than the host's desktop.
//
void ASShare::VIEWFrameResizeChanged(ASPerson * pasHost)
{
RECT rcView;
DebugEntry(ASShare::VIEWFrameResizeChanged);
// Get current view size
GetClientRect(pasHost->m_pView->m_viewClient, &rcView);
//
// The view area can't be bigger than the remote's desktop area
//
if ((rcView.bottom - rcView.top) >= pasHost->viewExtent.y)
{
RECT rcWindowCur;
RECT rcWindowMax;
// Get current frame size
GetWindowRect(pasHost->m_pView->m_viewFrame, &rcWindowCur);
// Get maximum frame size
VIEWFrameGetSize(pasHost, &rcWindowMax);
// Resize vertically to just hold everything
SetWindowPos(pasHost->m_pView->m_viewFrame, NULL, 0, 0,
rcWindowCur.right - rcWindowCur.left,
rcWindowMax.bottom - rcWindowMax.top,
SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}
else
{
// We can stay the same size, and just shuffle the pieces around
VIEWFrameResize(pasHost);
}
DebugExitVOID(ASShare::VIEWFrameResizeChanged);
}
//
// VIEWFrameCommand()
//
// Handles commands from menus/accelerators for frame views
//
void ASShare::VIEWFrameCommand
(
ASPerson* pasHost,
WPARAM wParam,
LPARAM lParam
)
{
UINT cmd;
MENUITEMINFO mi;
DebugEntry(ASShare::VIEWFrameCommand);
ValidateView(pasHost);
cmd = GET_WM_COMMAND_ID(wParam, lParam);
switch (cmd)
{
case CMD_TAKECONTROL:
{
CA_TakeControl(pasHost);
break;
}
case CMD_CANCELCONTROL:
{
CA_CancelTakeControl(pasHost, TRUE);
break;
}
case CMD_RELEASECONTROL:
{
CA_ReleaseControl(pasHost, TRUE);
break;
}
case CMD_CTRLALTDEL:
{
AWC_SendMsg(pasHost->mcsID, AWC_MSG_SAS, 0, 0);
break;
}
case CMD_VIEWSTATUSBAR:
{
ASSERT(::IsWindow(pasHost->m_pView->m_viewStatusBar));
// Toggle show/hide of status bar, then resize
if (pasHost->m_pView->m_viewStatusBarOn)
{
pasHost->m_pView->m_viewStatusBarOn = FALSE;
::ShowWindow(pasHost->m_pView->m_viewStatusBar, SW_HIDE);
}
else
{
pasHost->m_pView->m_viewStatusBarOn = TRUE;
::ShowWindow(pasHost->m_pView->m_viewStatusBar, SW_SHOW);
}
VIEWFrameResizeChanged(pasHost);
break;
}
case CMD_VIEWWINDOWBAR:
{
ASSERT(::IsWindow(pasHost->m_pView->m_viewWindowBar));
// Toggle show/hide of window bar, then resize
if (pasHost->m_pView->m_viewWindowBarOn)
{
pasHost->m_pView->m_viewWindowBarOn = FALSE;
::ShowWindow(pasHost->m_pView->m_viewWindowBar, SW_HIDE);
}
else
{
pasHost->m_pView->m_viewWindowBarOn = TRUE;
::ShowWindow(pasHost->m_pView->m_viewWindowBar, SW_SHOW);
}
VIEWFrameResizeChanged(pasHost);
break;
}
case CMD_VIEWFULLSCREEN:
{
VIEWFrameFullScreen(pasHost, (pasHost->m_pView->m_viewFullScreen == 0));
break;
}
case CMD_HELPTOPICS:
{
VIEWFrameHelp(pasHost);
break;
}
case CMD_HELPABOUT:
{
VIEWFrameAbout(pasHost);
break;
}
default:
{
if ((cmd >= CMD_APPSTART) && (cmd < CMD_APPMAX))
{
if ((pasHost->m_caControlledBy == m_pasLocal) &&
!pasHost->m_caControlPaused)
{
//
// This is a request to activate a host window.
// Get the item data, the remote HWND, then look to see
// if it's still on the tray.
//
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_DATA;
GetMenuItemInfo(GetSubMenu(pasHost->m_pView->m_viewMenuBar,
IDSM_WINDOW), cmd, FALSE, &mi);
if (!mi.dwItemData)
{
ERROR_OUT(("No item data for command %d", cmd));
}
else
{
PWNDBAR_ITEM pItem;
COM_BasedListFind(LIST_FIND_FROM_FIRST,
&(pasHost->m_pView->m_viewWindowBarItems),
(void**)&pItem, FIELD_OFFSET(WNDBAR_ITEM, chain),
FIELD_OFFSET(WNDBAR_ITEM, winIDRemote),
mi.dwItemData, FIELD_SIZE(WNDBAR_ITEM, winIDRemote));
if (pItem)
{
VIEWWindowBarDoActivate(pasHost, pItem);
}
}
}
}
else if ((cmd >= CMD_FORWARDCONTROLSTART) && (cmd < CMD_FORWARDCONTROLMAX))
{
if ((pasHost->m_caControlledBy == m_pasLocal) &&
!pasHost->m_caControlPaused)
{
//
// This is a request to pass control. Get the item data,
// the remote's MCS ID, then look to see if this person is
// still in the share. If so, pass control to them.
//
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_DATA;
GetMenuItemInfo(GetSubMenu(GetSubMenu(pasHost->m_pView->m_viewMenuBar,
IDSM_CONTROL), POS_FORWARDCONTROLCMD), cmd, FALSE, &mi);
if (!mi.dwItemData)
{
ERROR_OUT(("No item data for command %d", cmd));
}
else
{
ASPerson * pasT;
if (SC_ValidateNetID((MCSID)mi.dwItemData, &pasT))
{
CA_PassControl(pasHost, pasT);
}
}
}
}
else
{
ERROR_OUT(("Unrecognized WM_COMMAND id"));
}
break;
}
}
DebugExitVOID(ASShare::VIEWFrameCommand);
}
//
// ASShare::VIEWFrameInitMenuBar()
//
void ASShare::VIEWFrameInitMenuBar(ASPerson* pasHost)
{
HMENU hMenu;
HMENU hSubMenu;
int iItem;
MENUITEMINFO mi;
UINT cmd;
UINT ids;
UINT flags;
char szItem[256];
DebugEntry(ASShare::VIEWFrameInitMenu);
ValidateView(pasHost);
hMenu = pasHost->m_pView->m_viewMenuBar;
ASSERT(hMenu);
//
// CONTROL MENU
//
cmd = CMD_TAKECONTROL;
ids = IDS_CMD_TAKECONTROL;
flags = MF_ENABLED;
if (pasHost->m_caControlledBy == m_pasLocal)
{
ASSERT(pasHost->m_caAllowControl);
cmd = CMD_RELEASECONTROL;
ids = IDS_CMD_RELEASECONTROL;
//
// If the remote is unattended and we're in control, no releasing.
//
if (pasHost->cpcCaps.general.typeFlags & AS_UNATTENDED)
flags = MF_GRAYED;
}
else if ((m_caWaitingForReplyFrom == pasHost) &&
(m_caWaitingForReplyMsg == CA_REPLY_REQUEST_TAKECONTROL))
{
ASSERT(pasHost->m_caAllowControl);
cmd = CMD_CANCELCONTROL;
ids = IDS_CMD_CANCELCONTROL;
}
else if (!pasHost->m_caAllowControl || pasHost->m_caControlledBy)
{
//
// Host isn't allowing control, or somebody else is in control right
// now.
//
flags = MF_GRAYED;
}
flags |= MF_STRING | MF_BYPOSITION;
::LoadString(g_asInstance, ids, szItem, sizeof(szItem));
hSubMenu = GetSubMenu(hMenu, IDSM_CONTROL);
ModifyMenu(hSubMenu, POS_CONTROLCMD, flags, cmd, szItem);
//
// If we're in control, and there's another 3.0 dude in the conference,
// enable PassControl and build the popup.
//
EnableMenuItem(hSubMenu, POS_FORWARDCONTROLCMD, MF_GRAYED | MF_BYPOSITION);
if ((pasHost->m_caControlledBy == m_pasLocal) &&
!pasHost->m_caControlPaused &&
(pasHost->cpcCaps.general.version >= CAPS_VERSION_30))
{
ASPerson * pasT;
HMENU hPassMenu;
hPassMenu = GetSubMenu(hSubMenu, POS_FORWARDCONTROLCMD);
ASSERT(IsMenu(hPassMenu));
//
// Delete existing items.
//
iItem = GetMenuItemCount(hPassMenu);
while (iItem > 0)
{
iItem--;
DeleteMenu(hPassMenu, iItem, MF_BYPOSITION);
}
//
// Add items for the other 3.0 nodes besides us & the host.
//
iItem = CMD_FORWARDCONTROLSTART;
pasT = m_pasLocal->pasNext;
while (pasT != NULL)
{
if ((pasT != pasHost) &&
(pasT->cpcCaps.general.version >= CAPS_VERSION_30))
{
//
// This dude is a candidate. We must store the MCS IDs since the
// any person could go away while we're in menu mode.
//
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE | MIIM_DATA;
mi.fType = MFT_STRING;
mi.fState = MFS_ENABLED;
mi.wID = iItem;
mi.dwItemData = pasT->mcsID;
mi.dwTypeData = pasT->scName;
mi.cch = lstrlen(pasT->scName);
//
// Append this to the menu
//
InsertMenuItem(hPassMenu, -1, TRUE, &mi);
iItem++;
}
pasT = pasT->pasNext;
}
//
// Enable the Pass Control submenu if there's somebody on the
// menu.
//
if (iItem != CMD_FORWARDCONTROLSTART)
{
EnableMenuItem(hSubMenu, POS_FORWARDCONTROLCMD, MF_ENABLED | MF_BYPOSITION);
}
}
//
// APPLICATIONS MENU
//
if ((pasHost->hetCount != HET_DESKTOPSHARED) &&
(pasHost->m_caControlledBy == m_pasLocal) &&
!pasHost->m_caControlPaused)
{
PWNDBAR_ITEM pItem;
hSubMenu = GetSubMenu(hMenu, IDSM_WINDOW);
//
// Delete existing items.
//
iItem = GetMenuItemCount(hSubMenu);
while (iItem > 0)
{
iItem--;
DeleteMenu(hSubMenu, iItem, MF_BYPOSITION);
}
//
// Add window bar items.
//
iItem = CMD_APPSTART;
pItem = (PWNDBAR_ITEM)COM_BasedListFirst(&(pasHost->m_pView->m_viewWindowBarItems),
FIELD_OFFSET(WNDBAR_ITEM, chain));
while (pItem && (iItem < CMD_APPMAX))
{
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE | MIIM_DATA;
mi.fType = MFT_STRING;
mi.fState = MFS_ENABLED;
if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem)
{
mi.fState |= MFS_CHECKED;
}
mi.wID = iItem;
mi.dwItemData = pItem->winIDRemote;
mi.dwTypeData = pItem->szText;
mi.cch = lstrlen(pItem->szText);
//
// Append this to the menu
//
InsertMenuItem(hSubMenu, -1, TRUE, &mi);
iItem++;
pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems),
pItem, FIELD_OFFSET(WNDBAR_ITEM, chain));
}
if (iItem == CMD_APPSTART)
{
char szBlank[128];
//
// Append a disabled, blank item
//
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
mi.fType = MFT_STRING;
mi.fState = MFS_DISABLED;
mi.wID = iItem;
LoadString(g_asInstance, IDS_CMD_BLANKPROGRAM, szBlank, sizeof(szBlank));
mi.dwTypeData = szBlank;
mi.cch = lstrlen(szBlank);
InsertMenuItem(hSubMenu, -1, TRUE, &mi);
}
}
//
// VIEW MENU
//
// Status bar
ASSERT(::IsWindow(pasHost->m_pView->m_viewStatusBar));
if (pasHost->m_pView->m_viewStatusBarOn)
{
::CheckMenuItem(hMenu, CMD_VIEWSTATUSBAR, MF_CHECKED | MF_BYCOMMAND);
}
else
{
::CheckMenuItem(hMenu, CMD_VIEWSTATUSBAR, MF_UNCHECKED | MF_BYCOMMAND);
}
// Window bar
if (!pasHost->m_pView->m_viewWindowBar)
{
::EnableMenuItem(hMenu, CMD_VIEWWINDOWBAR, MF_GRAYED | MF_BYCOMMAND);
}
else if (pasHost->m_pView->m_viewWindowBarOn)
{
::CheckMenuItem(hMenu, CMD_VIEWWINDOWBAR, MF_CHECKED | MF_BYCOMMAND);
}
else
{
::CheckMenuItem(hMenu, CMD_VIEWWINDOWBAR, MF_UNCHECKED | MF_BYCOMMAND);
}
DebugExitVOID(ASShare::VIEWFrameInitMenu);
}
//
// VIEWFrameOnMenuSelect()
//
void ASShare::VIEWFrameOnMenuSelect
(
ASPerson * pasHost,
WPARAM wParam,
LPARAM lParam
)
{
HMENU hMenu;
int uItem;
UINT flags;
UINT idsStatus = IDS_STATUS_NONE;
DebugEntry(ASShare::VIEWFrameOnMenuSelect);
//
// Extract the params out (menuselect is messy)
//
hMenu = (HMENU)lParam;
uItem = (int)LOWORD(wParam);
if ((short)HIWORD(wParam) == -1)
{
flags = 0xFFFFFFFF;
}
else
{
flags = HIWORD(wParam);
}
if ((LOWORD(flags) == 0xFFFF) && !hMenu)
{
// Menu mode is ending. Put back original status.
idsStatus = pasHost->m_pView->m_viewStatus;
DC_QUIT;
}
if (!(flags & MF_POPUP))
{
if (flags & MF_SEPARATOR)
{
// No status
}
else if (flags & MF_SYSMENU)
{
// No status
}
else if ((uItem >= CMD_APPSTART) && (uItem < CMD_APPMAX))
{
// One of an unbounded set of items in the Window popup
idsStatus = IDS_STATUS_CMDS_APP;
}
else if ((uItem >= CMD_FORWARDCONTROLSTART) && (uItem < CMD_FORWARDCONTROLMAX))
{
// One of an unbounded set of items in the Forward Control popup
idsStatus = IDS_STATUS_CMDS_FORWARD;
}
else
{
// A normal command, just add offset to CMD id
idsStatus = uItem + IDS_STATUS_CMD_START;
}
}
else
{
// This is a popup menu
if (hMenu == pasHost->m_pView->m_viewMenuBar)
{
// It's a popup from the top level menu bar. uItem is the index
switch (uItem)
{
case IDSM_CONTROL:
idsStatus = IDS_STATUS_MENU_CONTROL;
break;
case IDSM_VIEW:
idsStatus = IDS_STATUS_MENU_VIEW;
break;
case IDSM_WINDOW:
idsStatus = IDS_STATUS_MENU_WINDOW;
break;
case IDSM_HELP:
idsStatus = IDS_STATUS_MENU_HELP;
break;
default:
ERROR_OUT(("AS: Unknown submenu index %d of frame", uItem));
break;
}
}
else if (hMenu == GetSubMenu(pasHost->m_pView->m_viewMenuBar, IDSM_CONTROL))
{
// This is a popup off the Control menu. The only one we have is Forward
idsStatus = IDS_STATUS_MENU_FORWARDCONTROL;
}
else if (flags & MF_SYSMENU)
{
// System menu
}
}
DC_EXIT_POINT:
VIEWFrameSetStatus(pasHost, idsStatus);
DebugEntry(ASShare::VIEWFrameOnMenuSelect);
}
//
// VIEWFrameHelp()
//
void ASShare::VIEWFrameHelp(ASPerson * pasHost)
{
DebugEntry(ASShare::VIEWFrameHelp);
ShowNmHelp(s_cszHtmlHelpFile);
DebugExitVOID(ASShare::VIEWFrameHelp);
}
//
// VIEWFrameAbout()
//
void ASShare::VIEWFrameAbout(ASPerson * pasHost)
{
DebugEntry(ASShare::VIEWFrameAbout);
//
// We make use of the standard centered-disabled-goes-away properly
// VIEW_Message() stuff.
//
VIEW_Message(pasHost, IDS_ABOUT);
DebugExitVOID(ASShare::VIEWFrameAbout);
}
//
// VIEWFrameGetSize()
// This returns back a rectangle for the ideal size of the frame. It will
// fit the view, menu, tools, tray, status, etc.
//
void ASShare::VIEWFrameGetSize(ASPerson * pasPerson, LPRECT lprc)
{
DebugEntry(ASShare::VIEWFrameGetSize);
ValidateView(pasPerson);
VIEWClientGetSize(pasPerson, lprc);
//
// Add in space for tray.
// NOTE that for DESKTOP SHARING we don't have a tray
//
if (pasPerson->m_pView->m_viewWindowBarOn)
{
lprc->bottom += m_viewWindowBarCY + m_viewEdgeCY;
}
//
// Add in space for statusbar if it's on, etc.
//
if (pasPerson->m_pView->m_viewStatusBarOn)
{
lprc->bottom += m_viewStatusBarCY + m_viewEdgeCY;
}
if (!pasPerson->m_pView->m_viewFullScreen)
{
//
// Adjust for frame styles including menu bar.
//
AdjustWindowRectEx(lprc, WS_OVERLAPPEDWINDOW, TRUE, WS_EX_WINDOWEDGE);
}
DebugExitVOID(ASShare::VIEWFrameGetSize);
}
//
// VIEWFrameFullScreen()
//
// This puts into or out of screen mode. We remove all the frame goop
// including scrollbars, so that the view area is identical to the screen.
//
void ASShare::VIEWFrameFullScreen(ASPerson * pasPerson, BOOL fFull)
{
LONG lStyle;
RECT rcNew;
DebugEntry(ASShare::VIEWFrameFullScreen);
//
// Turn redraw OFF
//
::SendMessage(pasPerson->m_pView->m_viewFrame, WM_SETREDRAW, FALSE, 0);
if (fFull)
{
//
// We're going into full screen mode.
//
ASSERT(!pasPerson->m_pView->m_viewFullScreen);
pasPerson->m_pView->m_viewFullScreen = TRUE;
//
// Save old window rect
//
::GetWindowRect(pasPerson->m_pView->m_viewFrame,
&pasPerson->m_pView->m_viewSavedWindowRect);
//
// Save old scroll pos and set to the origin. Do this BEFORE
// clearing style bits.
//
pasPerson->m_pView->m_viewSavedPos = pasPerson->m_pView->m_viewPos;
VIEWClientScroll(pasPerson, 0, 0);
//
// Save current status bar state before turning it off temporarily.
//
if (pasPerson->m_pView->m_viewStatusBarOn)
{
pasPerson->m_pView->m_viewSavedStatusBarOn = TRUE;
pasPerson->m_pView->m_viewStatusBarOn = FALSE;
::ShowWindow(pasPerson->m_pView->m_viewStatusBar, SW_HIDE);
}
else
{
pasPerson->m_pView->m_viewSavedStatusBarOn = FALSE;
}
//
// Save current window bar state before turning it off temporarily.
//
if (pasPerson->m_pView->m_viewWindowBarOn)
{
pasPerson->m_pView->m_viewSavedWindowBarOn = TRUE;
pasPerson->m_pView->m_viewWindowBarOn = FALSE;
::ShowWindow(pasPerson->m_pView->m_viewWindowBar, SW_HIDE);
}
else
{
pasPerson->m_pView->m_viewSavedWindowBarOn = FALSE;
}
//
// Remove all frame and client bits.
//
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE);
lStyle &= ~WS_EX_WINDOWEDGE;
::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME);
lStyle |= WS_POPUP;
::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE);
lStyle &= ~WS_EX_CLIENTEDGE;
::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE);
lStyle &= ~(WS_HSCROLL | WS_VSCROLL);
::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE, lStyle);
//
// Remove the menu bar
//
::SetMenu(pasPerson->m_pView->m_viewFrame, NULL);
//
// Set up to size window the size of the screen.
//
rcNew.left = 0;
rcNew.top = 0;
rcNew.right = m_pasLocal->cpcCaps.screen.capsScreenWidth;
rcNew.bottom = m_pasLocal->cpcCaps.screen.capsScreenHeight;
//
// Create the moveable escape-out button in the lower right corner.
//
::CreateWindowEx(0, VIEW_FULLEXIT_CLASS_NAME, NULL,
WS_CHILD | WS_VISIBLE,
rcNew.right - m_viewFullScreenCX - 2*m_viewEdgeCX,
rcNew.top + 2*m_viewEdgeCY,
m_viewFullScreenCX, m_viewFullScreenCY,
pasPerson->m_pView->m_viewClient,
(HMENU)0,
g_asInstance,
pasPerson);
}
else
{
//
// We're coming out of full screen mode.
//
//
// Destroy the escape-out button
//
::DestroyWindow(::GetDlgItem(pasPerson->m_pView->m_viewClient, 0));
//
// Put back the menu bar. Do this BEFORE clearing the full screen bit
//
::SetMenu(pasPerson->m_pView->m_viewFrame, pasPerson->m_pView->m_viewMenuBar);
ASSERT(pasPerson->m_pView->m_viewFullScreen);
pasPerson->m_pView->m_viewFullScreen = FALSE;
//
// Put back old status bar state.
//
if (pasPerson->m_pView->m_viewSavedStatusBarOn)
{
pasPerson->m_pView->m_viewStatusBarOn = TRUE;
::ShowWindow(pasPerson->m_pView->m_viewStatusBar, SW_SHOW);
}
//
// Put back old window bar state.
//
if (pasPerson->m_pView->m_viewSavedWindowBarOn)
{
pasPerson->m_pView->m_viewWindowBarOn = TRUE;
::ShowWindow(pasPerson->m_pView->m_viewWindowBar, SW_SHOW);
}
//
// Add back all frame and client bits.
//
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE);
lStyle |= WS_EX_WINDOWEDGE;
::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE);
lStyle &= ~(WS_POPUP);
lStyle |= (WS_CAPTION | WS_THICKFRAME);
::SetWindowLong(pasPerson->m_pView->m_viewFrame, GWL_STYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE);
lStyle |= WS_EX_CLIENTEDGE;
::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_EXSTYLE, lStyle);
lStyle = ::GetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE);
lStyle |= (WS_HSCROLL | WS_VSCROLL);
::SetWindowLong(pasPerson->m_pView->m_viewClient, GWL_STYLE, lStyle);
//
// Put back old scroll pos AFTER style bits restore.
//
VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewSavedPos.x,
pasPerson->m_pView->m_viewSavedPos.y);
//
// Restore the window back to where it started.
//
rcNew = pasPerson->m_pView->m_viewSavedWindowRect;
}
//
// Resize, reframe, and repaint from scratch.
//
::SendMessage(pasPerson->m_pView->m_viewFrame, WM_SETREDRAW, TRUE, 0);
::SetWindowPos(pasPerson->m_pView->m_viewFrame, NULL, rcNew.left,
rcNew.top, rcNew.right - rcNew.left, rcNew.bottom - rcNew.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOCOPYBITS);
DebugExitVOID(ASShare::VIEWFrameFullScreen);
}
//
// VIEWClientGetSize()
// This returns back a rectangle for the ideal size of the view part of the
// frame client. It will fit the extent of what we're viewing on the remote
// plus scrollbars.
//
void ASShare::VIEWClientGetSize(ASPerson * pasPerson, LPRECT lprc)
{
DebugEntry(ASShare::VIEWClientGetSize);
ValidateView(pasPerson);
lprc->left = 0;
lprc->top = 0;
lprc->right = pasPerson->viewExtent.x;
lprc->bottom = pasPerson->viewExtent.y;
if (!pasPerson->m_pView->m_viewFullScreen)
{
AdjustWindowRectEx(lprc, WS_CHILD, FALSE, WS_EX_CLIENTEDGE);
lprc->right += GetSystemMetrics(SM_CXVSCROLL);
lprc->bottom += GetSystemMetrics(SM_CYHSCROLL);
}
DebugExitVOID(ASShare::VIEWClientGetSize);
}
//
// VIEWClientWindowProc()
// Handles messages for the view window, a child in the client of the frame
// which displays the contents of the remote host's shared apps.
//
LRESULT CALLBACK VIEWClientWindowProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
return(g_asSession.pShare->VIEW_ViewWindowProc(hwnd, message, wParam, lParam));
}
LRESULT ASShare::VIEW_ViewWindowProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT rc = 0;
RECT rcl;
POINT mousePos;
SCROLLINFO si;
ASPerson * pasPerson;
DebugEntry(ASShare::VIEW_ViewWindowProc);
pasPerson = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pasPerson)
{
ValidateView(pasPerson);
}
switch (message)
{
case WM_NCCREATE:
{
// Get the passed in host pointer, and set in our window long
pasPerson = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasPerson);
pasPerson->m_pView->m_viewClient = hwnd;
goto DefWndProc;
break;
}
case WM_NCDESTROY:
{
if (pasPerson != NULL)
{
pasPerson->m_pView->m_viewClient = NULL;
}
goto DefWndProc;
break;
}
case WM_ERASEBKGND:
{
//
// BOGUS LAURABU: Paint on erase then validate for faster
// response.
//
rc = TRUE;
break;
}
case WM_PAINT:
{
VIEWClientPaint(pasPerson);
break;
}
case WM_SETFOCUS:
{
pasPerson->m_pView->m_viewFocus = TRUE;
pasPerson->m_pView->m_viewMouseWheelDelta = 0;
break;
}
case WM_KILLFOCUS:
{
pasPerson->m_pView->m_viewFocus = FALSE;
pasPerson->m_pView->m_viewMouseWheelDelta = 0;
break;
}
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
{
VIEWClientMouseDown(pasPerson, message, wParam, lParam);
break;
}
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
{
VIEWClientMouseUp(pasPerson, message, wParam, lParam, TRUE);
break;
}
case WM_MOUSEMOVE:
{
VIEWClientMouseMove(pasPerson, message, wParam, lParam);
break;
}
case WM_MOUSEWHEEL:
{
//
// We've handled it no matter what, don't pass it up the chain.
//
rc = TRUE;
//
// If we're not controlling this dude, try to use the mousewheel
// to scroll.
//
if ((pasPerson->m_caControlledBy != m_pasLocal) ||
pasPerson->m_caControlPaused)
{
VIEWClientMouseWheel(pasPerson, wParam, lParam);
break;
}
//
// FALL THROUGH
// Otherwise, we send the MOUSEWHEEL message to the host.
//
}
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
{
VIEWClientMouseMsg(pasPerson, message, wParam, lParam);
break;
}
case WM_TIMER:
{
if (wParam == IDT_AUTOSCROLL)
{
VIEWClientAutoScroll(pasPerson);
}
break;
}
case WM_CAPTURECHANGED:
{
//
// Check if capture got stolen away from us, if we think the
// buttons are down fake a button up.
//
if (pasPerson->m_pView->m_viewMouseFlags != 0)
{
VIEWClientCaptureStolen(pasPerson);
}
break;
}
case WM_KEYDOWN:
{
WPARAM wScrollNotify;
UINT uMsg;
if ((pasPerson->m_caControlledBy == m_pasLocal) &&
!pasPerson->m_caControlPaused)
{
goto KeyInput;
}
if (pasPerson->m_pView->m_viewFullScreen)
{
if (wParam == VK_ESCAPE)
{
//
// Kick out of full screen mode.
//
VIEWFrameFullScreen(pasPerson, FALSE);
}
goto DefWndProc;
}
//
// UP, DOWN, LEFT, and RIGHT are unambiguous about which
// scrollbar is intended.
//
// For the others, unmodified is vertical and SHIFT is
// horizontal.
//
if (::GetKeyState(VK_SHIFT) < 0)
{
uMsg = WM_HSCROLL;
}
else
{
uMsg = WM_VSCROLL;
}
switch (wParam)
{
//
// These aren't ambiguous, we know which scrollbar is meant
// by the direction.
//
case VK_UP:
wScrollNotify = SB_LINEUP;
uMsg = WM_VSCROLL;
break;
case VK_DOWN:
wScrollNotify = SB_LINEDOWN;
uMsg = WM_VSCROLL;
break;
case VK_LEFT:
wScrollNotify = SB_LINEUP;
uMsg = WM_HSCROLL;
break;
case VK_RIGHT:
wScrollNotify = SB_LINEDOWN;
uMsg = WM_HSCROLL;
break;
//
// These are ambiguous, hence the SHIFT key as a
// modifier.
//
case VK_PRIOR:
wScrollNotify = SB_PAGEUP;
break;
case VK_NEXT:
wScrollNotify = SB_PAGEDOWN;
break;
case VK_HOME:
wScrollNotify = SB_TOP;
break;
case VK_END:
wScrollNotify = SB_BOTTOM;
break;
default:
goto DefWndProc;
break;
}
SendMessage(hwnd, uMsg, MAKELONG(wScrollNotify, 0), 0L);
break;
}
case WM_SYSKEYDOWN:
{
if ((pasPerson->m_caControlledBy == m_pasLocal) &&
!pasPerson->m_caControlPaused)
{
goto KeyInput;
}
//
// ALT-ENTER toggles full screen state, if it's available
//
if ((wParam == VK_RETURN) &&
!(::GetMenuState(pasPerson->m_pView->m_viewMenuBar,
CMD_VIEWFULLSCREEN, MF_BYCOMMAND) & MF_DISABLED))
{
VIEWFrameFullScreen(pasPerson,
(pasPerson->m_pView->m_viewFullScreen == 0));
}
goto DefWndProc;
break;
}
case WM_KEYUP:
case WM_SYSKEYUP:
{
//
// If we're controlling this node, pass it along. Otherwise,
// call DefWindowProc() so key accels like Alt+Space for system
// menu will kick in.
//
if ((pasPerson->m_caControlledBy == m_pasLocal) &&
!pasPerson->m_caControlPaused)
{
KeyInput:
IM_OutgoingKeyboardInput(pasPerson, (UINT)wParam, (UINT)lParam);
}
else
{
goto DefWndProc;
}
break;
}
case WM_SETCURSOR:
{
if ((LOWORD(lParam) == HTCLIENT) && ((HWND)wParam == hwnd))
{
HCURSOR hCursor;
POINT cursorPoint;
if ((pasPerson->m_caControlledBy == m_pasLocal) &&
!pasPerson->m_caControlPaused)
{
hCursor = m_cmArrowCursor;
//
// Only set the remote cursor if we're over shared space.
//
if (pasPerson->m_pView->m_viewFocus)
{
GetCursorPos(&cursorPoint);
ScreenToClient(hwnd, &cursorPoint);
if (VIEW_IsPointShared(pasPerson, cursorPoint))
{
hCursor = pasPerson->cmhRemoteCursor;
}
}
}
else
{
// NoDrop
hCursor = m_viewNotInControl;
}
SetCursor(hCursor);
rc = TRUE;
}
else
{
// Let defwindowproc handle it
goto DefWndProc;
}
break;
}
case WM_SIZE:
{
//
// If we're in full screen mode, there are no scrollbars.
//
if (!pasPerson->m_pView->m_viewFullScreen)
{
int xNewPos;
int yNewPos;
xNewPos = pasPerson->m_pView->m_viewPos.x;
yNewPos = pasPerson->m_pView->m_viewPos.y;
GetClientRect(hwnd, &rcl);
pasPerson->m_pView->m_viewPage.x = rcl.right - rcl.left;
pasPerson->m_pView->m_viewPage.y = rcl.bottom - rcl.top;
TRACE_OUT(("WM_SIZE: Set page size (%04d, %04d)",
pasPerson->m_pView->m_viewPage.x, pasPerson->m_pView->m_viewPage.y));
//
// Scroll window if necessary.
//
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE|SIF_DISABLENOSCROLL;
// Set new HORIZONTAL proportional scroll button size
si.nPage = pasPerson->m_pView->m_viewPage.x;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE );
// Set new VERTICAL proportional scroll button size
si.nPage = pasPerson->m_pView->m_viewPage.y;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE );
//
// This will make sure the scroll pos is pinned properly
//
VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewPos.x, pasPerson->m_pView->m_viewPos.y);
}
break;
}
case WM_HSCROLL:
{
int xNewPos; // new position
switch (GET_WM_HSCROLL_CODE(wParam, lParam))
{
case SB_PAGEUP:
xNewPos = pasPerson->m_pView->m_viewPos.x - pasPerson->m_pView->m_viewPgSize.x;
break;
case SB_PAGEDOWN:
xNewPos = pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_viewPgSize.x;
break;
case SB_LINEUP:
xNewPos = pasPerson->m_pView->m_viewPos.x - pasPerson->m_pView->m_viewLnSize.x;
break;
case SB_LINEDOWN:
xNewPos = pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_viewLnSize.x;
break;
case SB_TOP:
xNewPos = 0;
break;
case SB_BOTTOM:
xNewPos = pasPerson->viewExtent.x;
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
xNewPos = GET_WM_HSCROLL_POS(wParam, lParam);
break;
default:
xNewPos = pasPerson->m_pView->m_viewPos.x;
break;
}
//
// This will pin the desired scroll pos in the range, and if
// nothing has changed, won't scroll.
//
VIEWClientScroll(pasPerson, xNewPos, pasPerson->m_pView->m_viewPos.y);
break;
}
case WM_VSCROLL:
{
int yNewPos; // new position
switch (GET_WM_VSCROLL_CODE(wParam, lParam))
{
case SB_PAGEUP:
yNewPos = pasPerson->m_pView->m_viewPos.y - pasPerson->m_pView->m_viewPgSize.y;
break;
case SB_PAGEDOWN:
yNewPos = pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_viewPgSize.y;
break;
case SB_LINEUP:
yNewPos = pasPerson->m_pView->m_viewPos.y - pasPerson->m_pView->m_viewLnSize.y;
break;
case SB_LINEDOWN:
yNewPos = pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_viewLnSize.y;
break;
case SB_TOP:
yNewPos = 0;
break;
case SB_BOTTOM:
yNewPos = pasPerson->viewExtent.y;
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
yNewPos = GET_WM_VSCROLL_POS(wParam, lParam);
break;
default:
yNewPos = pasPerson->m_pView->m_viewPos.y;
break;
}
//
// This will pin the desired scroll pos in the range, and if
// nothing has changed, won't scroll.
//
VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewPos.x, yNewPos);
break;
}
default:
DefWndProc:
rc = DefWindowProc(hwnd, message, wParam, lParam);
break;
}
DebugExitDWORD(ASShare::VIEW_ViewWindowProc, rc);
return(rc);
}
//
// VIEWClientPaint()
//
// This paints the client area of the view frame. We paint
// (1) The obscured area, in the obscured pattern
// * parts of shared regions that are covered up
// * parts of shared regions that are offscreen/off the VD
// (2) The shared area, from the bitmap
// (3) The deadspace, in COLOR_APPWORKSPACE
//
void ASShare::VIEWClientPaint(ASPerson * pasPerson)
{
PAINTSTRUCT ps;
HDC hdcView;
HPALETTE hOldPal;
HPALETTE hOldPal2;
RECT rcT;
DebugEntry(ASShare::VIEWClientPaint);
ValidateView(pasPerson);
hdcView = BeginPaint(pasPerson->m_pView->m_viewClient, &ps);
if (hdcView == NULL)
{
WARNING_OUT(( "Failed to get hdc for frame window %08X", pasPerson->m_pView->m_viewClient));
DC_QUIT;
}
if (IsRectEmpty(&ps.rcPaint))
{
TRACE_OUT(("Nothing to paint but got WM_PAINT message"));
DC_QUIT;
}
TRACE_OUT(("VIEWClientPaint: Painting total client area {%04d, %04d, %04d, %04d}",
ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom));
//
// In desktop sharing, viewSharedRgn is NULL
//
if (pasPerson->m_pView->m_viewSharedRgn != NULL)
{
POINT ptOrigin;
HBRUSH hbrT;
//
// First, create paint area region
//
SetRectRgn(pasPerson->m_pView->m_viewPaintRgn, ps.rcPaint.left, ps.rcPaint.top,
ps.rcPaint.right, ps.rcPaint.bottom);
//
// Second, compute the VD area not currently on screen. Do this
// in CLIENT coords.
//
SetRectRgn(pasPerson->m_pView->m_viewExtentRgn,
-pasPerson->m_pView->m_viewPos.x,
-pasPerson->m_pView->m_viewPos.y,
-pasPerson->m_pView->m_viewPos.x + pasPerson->viewExtent.x,
-pasPerson->m_pView->m_viewPos.y + pasPerson->viewExtent.y);
SetRectRgn(pasPerson->m_pView->m_viewScreenRgn,
-pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_dsScreenOrigin.x,
-pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_dsScreenOrigin.y,
-pasPerson->m_pView->m_viewPos.x + pasPerson->m_pView->m_dsScreenOrigin.x + pasPerson->cpcCaps.screen.capsScreenWidth,
-pasPerson->m_pView->m_viewPos.y + pasPerson->m_pView->m_dsScreenOrigin.y + pasPerson->cpcCaps.screen.capsScreenHeight);
SubtractRgn(pasPerson->m_pView->m_viewExtentRgn, pasPerson->m_pView->m_viewExtentRgn, pasPerson->m_pView->m_viewScreenRgn);
//
// pasPerson->m_pView->m_viewExtentRgn is now the offscreen parts of the VD, and therefore
// any shared areas lying in them should be treated as obscured.
//
//
// Now, compute the real obscured area. It's the covered up bits
// plus open parts of shared stuff not currently on screen.
//
IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewExtentRgn, pasPerson->m_pView->m_viewSharedRgn);
UnionRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewObscuredRgn);
// Calc what part of the obscured region to actually paint
IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewPaintRgn);
if (GetRgnBox(pasPerson->m_pView->m_viewScratchRgn, &rcT) > NULLREGION)
{
TRACE_OUT(("VIEWClientPaint: Painting obscured client area {%04d, %04d, %04d, %04d}",
rcT.left, rcT.top, rcT.right, rcT.bottom));
//
// Remove this area so we have what's left to paint.
//
SubtractRgn(pasPerson->m_pView->m_viewPaintRgn, pasPerson->m_pView->m_viewPaintRgn, pasPerson->m_pView->m_viewScratchRgn);
//
// We do NOT want to use FillRgn; it ignores the brush origin.
// So we select this in as the clip region and PatBlt instead.
//
SelectClipRgn(hdcView, pasPerson->m_pView->m_viewScratchRgn);
#ifdef _DEBUG
//
// NOTE: Do NOT move this--we're using ptOrigin for scratch.
//
GetDCOrgEx(hdcView, &ptOrigin);
TRACE_OUT(("VIEWClientPaint: Setting brush origin to {%04d, %04d}, screen {%04d, %04d}",
-pasPerson->m_pView->m_viewPos.x, -pasPerson->m_pView->m_viewPos.y,
ptOrigin.x - pasPerson->m_pView->m_viewPos.x,
ptOrigin.y - pasPerson->m_pView->m_viewPos.y));
#endif
//
// Align the brush with where the view's real origin would be, in
// client coords. We do that by accounting for being scrolled over.
//
SetBrushOrgEx(hdcView, -pasPerson->m_pView->m_viewPos.x,
-pasPerson->m_pView->m_viewPos.y, &ptOrigin);
UnrealizeObject(m_viewObscuredBrush);
hbrT = SelectBrush(hdcView, m_viewObscuredBrush);
PatBlt(hdcView,
rcT.left, rcT.top,
rcT.right - rcT.left,
rcT.bottom - rcT.top,
PATCOPY);
SelectBrush(hdcView, hbrT);
SetBrushOrgEx(hdcView, ptOrigin.x, ptOrigin.y, NULL);
SelectClipRgn(hdcView, NULL);
}
//
// Paint the deadspace area, set up clipping for app sharing.
// This also works for desktop sharing, where there are no obscured or
// shared regions, the whole area paints.
//
//
// The deadspace is whatever's left over in the paint region
// (already subtracted the obscured region) after subtracting the
// shared area
//
SubtractRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewPaintRgn, pasPerson->m_pView->m_viewSharedRgn);
if (GetRgnBox(pasPerson->m_pView->m_viewScratchRgn, &rcT) > NULLREGION)
{
TRACE_OUT(("VIEWClientPaint: Painting dead client area {%04d, %04d, %04d, %04d}",
rcT.left, rcT.top, rcT.right, rcT.bottom));
FillRgn(hdcView, pasPerson->m_pView->m_viewScratchRgn, GetSysColorBrush(COLOR_APPWORKSPACE));
}
//
// Compute what part of the shared area needs painting (the part
// that lies on the remote screen actually).
//
IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewSharedRgn, pasPerson->m_pView->m_viewScreenRgn);
IntersectRgn(pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewScratchRgn, pasPerson->m_pView->m_viewPaintRgn);
// Now select in the piece of what we're painting as the clip region
SelectClipRgn(hdcView, pasPerson->m_pView->m_viewScratchRgn);
}
//
// Blt the shared region
//
if (GetClipBox(hdcView, &rcT) > NULLREGION)
{
TRACE_OUT(("VIEWClientPaint: Painting shared client area {%04x, %04x, %04x, %04x}",
rcT.left, rcT.top, rcT.right, rcT.bottom));
if (g_usrPalettized)
{
ASSERT(pasPerson->pmPalette != NULL);
//
// Select and realize the current remote palette into the
// screen and shadow bitmap DCs.
//
hOldPal = SelectPalette(pasPerson->m_pView->m_usrDC, pasPerson->pmPalette, FALSE);
RealizePalette(pasPerson->m_pView->m_usrDC);
hOldPal2 = SelectPalette( hdcView, pasPerson->pmPalette, FALSE);
RealizePalette(hdcView);
}
//
// The host bitmap is in screen coords, not VD coords, so
// adjust for being scrolled over...
//
BitBlt(hdcView,
rcT.left, rcT.top, rcT.right - rcT.left, rcT.bottom - rcT.top,
pasPerson->m_pView->m_usrDC,
rcT.left + pasPerson->m_pView->m_viewPos.x - pasPerson->m_pView->m_dsScreenOrigin.x,
rcT.top + pasPerson->m_pView->m_viewPos.y - pasPerson->m_pView->m_dsScreenOrigin.y,
SRCCOPY);
if (g_usrPalettized)
{
ASSERT(pasPerson->pmPalette != NULL);
SelectPalette(pasPerson->m_pView->m_usrDC, hOldPal, FALSE);
SelectPalette(hdcView, hOldPal2, FALSE);
}
}
//
// Deselect the clip region, or we won't be able to draw shadow cursors
// that lie outside the shared area.
//
if (pasPerson->m_pView->m_viewSharedRgn != NULL)
{
SelectClipRgn(hdcView, NULL);
}
//
// Draw the shadow cursor.
//
CM_DrawShadowCursor(pasPerson, hdcView);
DC_EXIT_POINT:
if (hdcView != NULL)
EndPaint(pasPerson->m_pView->m_viewClient, &ps);
DebugExitVOID(ASShare::VIEWClientPaint);
}
//
// VIEWClientScroll()
//
// This is the common place where the scroll position is altered. If
// necessary the contents are scrolled over, the regions (always in client
// coords) are adjusted, and new info about our origin is sent to remotes.
//
// We first make sure the scroll position is pinned properly within the
// range.
//
// The return value is whether scrolling happened or not.
//
BOOL ASShare::VIEWClientScroll
(
ASPerson * pasPerson,
int xNew,
int yNew
)
{
int dx;
int dy;
DebugEntry(ASShare::VIEWClientScroll);
//
// First, pin the requested new position within the range
//
//
// Pin x pos
//
if (xNew < 0)
xNew = 0;
if (xNew + pasPerson->m_pView->m_viewPage.x > pasPerson->viewExtent.x)
xNew = pasPerson->viewExtent.x - pasPerson->m_pView->m_viewPage.x;
//
// Pin y pos
//
if (yNew < 0)
yNew = 0;
if (yNew + pasPerson->m_pView->m_viewPage.y > pasPerson->viewExtent.y)
yNew = pasPerson->viewExtent.y - pasPerson->m_pView->m_viewPage.y;
//
// How much are we going to scroll by?
//
dx = pasPerson->m_pView->m_viewPos.x - xNew;
dy = pasPerson->m_pView->m_viewPos.y - yNew;
// Updates
if (dx || dy)
{
//
// Adjust regions
//
if (pasPerson->m_pView->m_viewObscuredRgn != NULL)
OffsetRgn(pasPerson->m_pView->m_viewObscuredRgn, dx, dy);
if (pasPerson->m_pView->m_viewSharedRgn != NULL)
OffsetRgn(pasPerson->m_pView->m_viewSharedRgn, dx, dy);
pasPerson->m_pView->m_viewPos.x = xNew;
pasPerson->m_pView->m_viewPos.y = yNew;
ScrollWindowEx(pasPerson->m_pView->m_viewClient,
dx,
dy,
NULL,
NULL,
NULL,
NULL,
SW_SCROLLCHILDREN | SW_INVALIDATE);
if (dx)
{
SetScrollPos(pasPerson->m_pView->m_viewClient, SB_HORZ, xNew, TRUE);
}
if (dy)
{
SetScrollPos(pasPerson->m_pView->m_viewClient, SB_VERT, yNew, TRUE);
}
}
DebugExitBOOL(ASShare::VIEWClientScroll, (dx || dy));
return(dx || dy);
}
//
// VIEWClientMouseDown()
//
void ASShare::VIEWClientMouseDown
(
ASPerson * pasPerson,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
DebugEntry(ASShare::VIEWClientMouseDown);
ValidateView(pasPerson);
//
// On the first button down, set capture so all mouse messages come
// to us until capture is released or stolen.
//
if (!pasPerson->m_pView->m_viewMouseFlags)
{
//
// If this is RBUTTONDOWN, track the Collaborate pop up...
//
ASSERT(!pasPerson->m_pView->m_viewMouseOutside);
SetCapture(pasPerson->m_pView->m_viewClient);
}
//
// Remember what button is down.
//
switch (message)
{
case WM_LBUTTONDOWN:
pasPerson->m_pView->m_viewMouseFlags |= MK_LBUTTON;
break;
case WM_RBUTTONDOWN:
pasPerson->m_pView->m_viewMouseFlags |= MK_RBUTTON;
break;
case WM_MBUTTONDOWN:
pasPerson->m_pView->m_viewMouseFlags |= MK_MBUTTON;
break;
}
//
// Save the current mouse position
//
pasPerson->m_pView->m_viewMouse.x = GET_X_LPARAM(lParam);
pasPerson->m_pView->m_viewMouse.y = GET_Y_LPARAM(lParam);
VIEWClientMouseMsg(pasPerson, message, wParam, lParam);
DebugExitVOID(ASShare::VIEWClientMouseDown);
}
//
// VIEWClientMouseUp()
//
void ASShare::VIEWClientMouseUp
(
ASPerson * pasPerson,
UINT message,
WPARAM wParam,
LPARAM lParam,
BOOL fReleaseCapture
)
{
DebugEntry(ASShare::VIEWClientMouseUp);
switch (message)
{
case WM_LBUTTONUP:
if (pasPerson->m_pView->m_viewMouseFlags & MK_LBUTTON)
pasPerson->m_pView->m_viewMouseFlags &= ~MK_LBUTTON;
else
fReleaseCapture = FALSE; // From dbl-click
break;
case WM_RBUTTONUP:
if (pasPerson->m_pView->m_viewMouseFlags & MK_RBUTTON)
pasPerson->m_pView->m_viewMouseFlags &= ~MK_RBUTTON;
else
fReleaseCapture = FALSE; // From dbl-click
break;
case WM_MBUTTONUP:
if (pasPerson->m_pView->m_viewMouseFlags & MK_MBUTTON)
pasPerson->m_pView->m_viewMouseFlags &= ~MK_MBUTTON;
else
fReleaseCapture = FALSE; // From dbl-click
break;
}
//
// Should we release capture?
// We don't just want to release capture on a button up. The user may
// press one button down then another; we don't want to release capture
// until all buttons are up.
//
if (!pasPerson->m_pView->m_viewMouseFlags)
{
if (pasPerson->m_pView->m_viewMouseOutside)
{
pasPerson->m_pView->m_viewMouseOutside = FALSE;
KillTimer(pasPerson->m_pView->m_viewClient, IDT_AUTOSCROLL);
}
if (fReleaseCapture)
ReleaseCapture();
}
//
// Save the current mouse position
//
pasPerson->m_pView->m_viewMouse.x = GET_X_LPARAM(lParam);
pasPerson->m_pView->m_viewMouse.y = GET_Y_LPARAM(lParam);
VIEWClientMouseMsg(pasPerson, message, wParam, lParam);
DebugExitVOID(ASShare::VIEWClientMouseUp);
}
//
// VIEWClientCaptureStolen()
// Called when capture gets stolen away from us, like by Alt-Tab.
//
void ASShare::VIEWClientCaptureStolen(ASPerson * pasPerson)
{
DebugEntry(ASShare::VIEWClientCaptureStolen);
//
// We need to fake a button up for each button we think is down.
// Use the current cursor pos.
//
if (pasPerson->m_pView->m_viewMouseFlags & MK_MBUTTON)
{
VIEWClientMouseUp(pasPerson, WM_MBUTTONUP, pasPerson->m_pView->m_viewMouseFlags,
MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y),
FALSE);
}
if (pasPerson->m_pView->m_viewMouseFlags & MK_RBUTTON)
{
VIEWClientMouseUp(pasPerson, WM_RBUTTONUP, pasPerson->m_pView->m_viewMouseFlags,
MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y),
FALSE);
}
if (pasPerson->m_pView->m_viewMouseFlags & MK_LBUTTON)
{
VIEWClientMouseUp(pasPerson, WM_LBUTTONUP, pasPerson->m_pView->m_viewMouseFlags,
MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y),
FALSE);
}
DebugExitVOID(ASShare::VIEWClientCaptureStolen);
}
//
// VIEWClientMouseMove()
//
void ASShare::VIEWClientMouseMove
(
ASPerson * pasPerson,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
RECT rcClient;
DebugEntry(ASShare::VIEWClientMouseMove);
if (!pasPerson->m_pView->m_viewFocus)
{
// Ignore mouse moves over windows that don't have the focus
DC_QUIT;
}
//
// Save the current mouse position
//
pasPerson->m_pView->m_viewMouse.x = GET_X_LPARAM(lParam);
pasPerson->m_pView->m_viewMouse.y = GET_Y_LPARAM(lParam);
GetClientRect(pasPerson->m_pView->m_viewClient, &rcClient);
//
// If any button is down, check whether we should kick in
// autoscroll detection.
//
if (pasPerson->m_pView->m_viewMouseFlags)
{
// Is the mouse inside or outside the client for the first time?
if (PtInRect(&rcClient, pasPerson->m_pView->m_viewMouse))
{
//
// Was the mouse outside the client before? If so, kill our
// autoscroll timer, we're not dragging outside.
//
if (pasPerson->m_pView->m_viewMouseOutside)
{
pasPerson->m_pView->m_viewMouseOutside = FALSE;
KillTimer(pasPerson->m_pView->m_viewClient, IDT_AUTOSCROLL);
}
}
else
{
//
// Is the first time the mouse is outside the client? If so,
// set our autoscroll timer to the default value. When it goes
// off, the autoscroll code will scroll by some multiple of
// how far away the mouse is from the client.
//
if (!pasPerson->m_pView->m_viewMouseOutside)
{
//
// The Windows scrollbar code uses 1/8 of the double-click
// time, so we do also.
//
pasPerson->m_pView->m_viewMouseOutside = TRUE;
SetTimer(pasPerson->m_pView->m_viewClient, IDT_AUTOSCROLL,
GetDoubleClickTime() / 8, NULL);
}
//
// LAURABU BOGUS!
// When IM_Periodic goop is gone for controlling, do NOT
// pass along mouse outside messages. Only the autoscroll
// timer will fake a mouse move in this case. Either that,
// or clip the position to the nearest client area equivalent.
//
}
}
VIEWClientMouseMsg(pasPerson, message, wParam, lParam);
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEWClientMouseMove);
}
//
// VIEWClientMouseMsg()
//
void ASShare::VIEWClientMouseMsg
(
ASPerson * pasPerson,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
POINT mousePos;
DebugEntry(ASShare::VIEWClientMouseMsg);
//
// Extract the mouse position from <lParam> and package it
// in a POINT structure. These coordinates are relative to our
// client area. So convert to remote's desktop by adjusting for
// scroll position.
//
// Be careful when converting the LOWORD and HIWORD values
// because the positions are signed values.
//
mousePos.x = GET_X_LPARAM(lParam) + pasPerson->m_pView->m_viewPos.x;
mousePos.y = GET_Y_LPARAM(lParam) + pasPerson->m_pView->m_viewPos.y;
//
// These coords represent the SCREEN coords on the host.
//
if (pasPerson->m_caControlledBy == m_pasLocal)
{
if (!pasPerson->m_caControlPaused)
{
IM_OutgoingMouseInput(pasPerson, &mousePos, message, (UINT)wParam);
}
}
else if (pasPerson->m_caAllowControl && !pasPerson->m_caControlledBy &&
(message == WM_LBUTTONDBLCLK))
{
//
// If we're already waiting for control of this person, don't bother
// trying to take control again.
//
if ((m_caWaitingForReplyFrom != pasPerson) &&
(m_caWaitingForReplyMsg != CA_REPLY_REQUEST_TAKECONTROL))
{
CA_TakeControl(pasPerson);
}
}
DebugExitVOID(ASShare::VIEWClientMouse);
}
//
// VIEWClientMouseWheel()
//
// Unbelievably complicated, messy, nonsensical Intellimouse wheel handling
// to scroll the client. Since the Intellimouse makes no distinction for
// which direction to scroll in, we basically have to guess. We don't want
// to be unpredictable and decide which direction to scroll based on how
// much is visible in each dimenion.
//
// So instead, we assume horizontal. If the horizontal scrollbar is disabled,
// then we try vertical. If that's disabled, we do nothing.
//
// We do NOT handle zoom and datazoom flavors.
//
// Note that this code comes from the listbox/sample source.
//
void ASShare::VIEWClientMouseWheel
(
ASPerson * pasHost,
WPARAM wParam,
LPARAM lParam
)
{
int cDetants;
DebugEntry(ASShare::VIEWClientMouseWheel);
//
// The LOWORD of wParam has key state information.
// The HIWORD of wParam is the number of mouse wheel clicks.
//
//
// We don't do zoom/datazoom
//
if (wParam & (MK_SHIFT | MK_CONTROL))
{
DC_QUIT;
}
pasHost->m_pView->m_viewMouseWheelDelta -= (int)(short)HIWORD(wParam);
cDetants = pasHost->m_pView->m_viewMouseWheelDelta / WHEEL_DELTA;
if (cDetants && (m_viewMouseWheelScrollLines > 0))
{
POINT ptPos;
pasHost->m_pView->m_viewMouseWheelDelta %= WHEEL_DELTA;
//
// The basic idea is that we scroll some number of lines, the
// number being cDetants.
//
ptPos = pasHost->m_pView->m_viewPos;
//
// To be consistent with other apps, and with our keyboard
// accelerators, try the vertical direction first.
//
if (pasHost->m_pView->m_viewPage.y < pasHost->viewExtent.y)
{
ptPos.y += cDetants * pasHost->m_pView->m_viewLnSize.y;
}
else if (pasHost->m_pView->m_viewPage.x < pasHost->viewExtent.x)
{
ptPos.x += cDetants * pasHost->m_pView->m_viewLnSize.x;
}
else
{
// Nothing to scroll, the whole view fits in the client area.
}
VIEWClientScroll(pasHost, ptPos.x, ptPos.y);
}
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEWClientMouseWheel);
}
//
// VIEWClientAutoScroll()
//
void ASShare::VIEWClientAutoScroll(ASPerson * pasPerson)
{
int dx;
int dy;
RECT rcClient;
DebugEntry(ASShare::VIEWClientAutoScroll);
ValidateView(pasPerson);
ASSERT(pasPerson->m_pView->m_viewMouseOutside);
//
// Do scrolling. The amount is dependent on how far outside the
// client area we are.
//
GetClientRect(pasPerson->m_pView->m_viewClient, &rcClient);
// Horizontal scrolling?
if (pasPerson->m_pView->m_viewMouse.x < rcClient.left)
{
dx = pasPerson->m_pView->m_viewMouse.x - rcClient.left;
}
else if (pasPerson->m_pView->m_viewMouse.x >= rcClient.right)
{
dx = pasPerson->m_pView->m_viewMouse.x - rcClient.right + 1;
}
else
{
dx = 0;
}
// Vertical scrolling?
if (pasPerson->m_pView->m_viewMouse.y < rcClient.top)
{
dy = pasPerson->m_pView->m_viewMouse.y - rcClient.top;
}
else if (pasPerson->m_pView->m_viewMouse.y >= rcClient.bottom)
{
dy = pasPerson->m_pView->m_viewMouse.y - rcClient.bottom + 1;
}
else
{
dy = 0;
}
// For every 32 pixel blocks outside the client, scroll one line amount
if (dx)
dx = MulDiv(pasPerson->m_pView->m_viewLnSize.x, dx, 32);
if (dy)
dy = MulDiv(pasPerson->m_pView->m_viewLnSize.y, dy, 32);
// Do scrolling.
if (VIEWClientScroll(pasPerson, pasPerson->m_pView->m_viewPos.x + dx,
pasPerson->m_pView->m_viewPos.y + dy))
{
//
// The scroll position actually changed. So fake a mouse move
// to the current location so that the remote's
// cursor will be in the same spot as ours. If our scroll pos has
// changed, we're mapping to a different place on the remote.
//
VIEWClientMouseMsg(pasPerson, WM_MOUSEMOVE, pasPerson->m_pView->m_viewMouseFlags,
MAKELPARAM(pasPerson->m_pView->m_viewMouse.x, pasPerson->m_pView->m_viewMouse.y));
}
DebugExitVOID(ASShare::VIEWClientAutoScroll);
}
//
// VIEW_SyncCursorPos()
//
// This is called when we see a CM_SYNC pos packet broadcasted from a
// host. It means that we should sync our cursor to the corresponding
// position in our view. This happens when the cursor is moved by
// an app, constrained by clipping, or we're too out of whack because it's
// taking too long.
//
// This will only do something if the frame is active and our cursor is
// currently over the client area. If we need to, we will scroll the
// client over to make the corresponding point visible.
//
void ASShare::VIEW_SyncCursorPos
(
ASPerson * pasHost,
int xRemote,
int yRemote
)
{
POINT ptCursor;
RECT rcClient;
int xNewPos;
int yNewPos;
int xMargin;
int yMargin;
DebugEntry(ASShare::VIEW_SyncCursorPos);
ValidateView(pasHost);
if (!pasHost->m_pView->m_viewFocus)
{
// The frame isn't active, do nothing
DC_QUIT;
}
//
// Is our mouse currently over the client area?
//
GetCursorPos(&ptCursor);
ScreenToClient(pasHost->m_pView->m_viewClient, &ptCursor);
GetClientRect(pasHost->m_pView->m_viewClient, &rcClient);
if (!PtInRect(&rcClient, ptCursor))
{
// No sense in snapping cursor
DC_QUIT;
}
//
// Is the remote point in range of our view? If not, we must scroll it.
//
// The margin is the page size if there's room, nothing if not
xMargin = pasHost->m_pView->m_viewPgSize.x;
if (xMargin >= rcClient.right - rcClient.left)
xMargin = 0;
xNewPos = pasHost->m_pView->m_viewPos.x;
if ((xRemote < pasHost->m_pView->m_viewPos.x) ||
(xRemote >= pasHost->m_pView->m_viewPos.x + (rcClient.right - rcClient.left)))
{
//
// Scroll over more than just enough to pin the point on the left
// side.
//
xNewPos = xRemote - xMargin;
}
yMargin = pasHost->m_pView->m_viewPgSize.y;
if (yMargin >= rcClient.bottom - rcClient.top)
yMargin = 0;
yNewPos = pasHost->m_pView->m_viewPos.y;
if ((yRemote < pasHost->m_pView->m_viewPos.y) ||
(yRemote >= yNewPos + (rcClient.bottom - rcClient.top)))
{
//
// Scroll over more than just enough to pin the point on the top
// side.
//
yNewPos = yRemote - yMargin;
}
VIEWClientScroll(pasHost, xNewPos, yNewPos);
ptCursor.x = xRemote - pasHost->m_pView->m_viewPos.x;
ptCursor.y = yRemote - pasHost->m_pView->m_viewPos.y;
ClientToScreen(pasHost->m_pView->m_viewClient, &ptCursor);
SetCursorPos(ptCursor.x, ptCursor.y);
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEW_SyncCursorPos);
}
//
// VIEWWindowBarProc()
//
LRESULT CALLBACK VIEWWindowBarProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
return(g_asSession.pShare->VIEW_WindowBarProc(hwnd, message, wParam, lParam));
}
LRESULT ASShare::VIEW_WindowBarProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT rc = 0;
ASPerson * pasHost;
DebugEntry(ASShare::VIEW_WindowBarProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pasHost)
{
ValidateView(pasHost);
}
switch (message)
{
case WM_NCCREATE:
{
// Get & save the person this view is for.
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams;
ValidateView(pasHost);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
pasHost->m_pView->m_viewWindowBar = hwnd;
goto DefWndProc;
break;
}
case WM_NCDESTROY:
{
if (pasHost != NULL)
{
pasHost->m_pView->m_viewWindowBar = NULL;
}
goto DefWndProc;
break;
}
case WM_CREATE:
{
if (!VIEWWindowBarCreate(pasHost, hwnd))
{
ERROR_OUT(("VIEWWndBarProc: couldn't create more item"));
rc = -1;
}
break;
}
case WM_SIZE:
{
VIEWWindowBarResize(pasHost, hwnd);
break;
}
case WM_HSCROLL:
{
VIEWWindowBarItemsScroll(pasHost, wParam, lParam);
break;
}
default:
DefWndProc:
{
rc = DefWindowProc(hwnd, message, wParam, lParam);
break;
}
}
DebugExitDWORD(ASShare::VIEW_WindowBarProc, rc);
return(rc);
}
//
// VIEWWindowBarCreate()
// Handles creation for the window bar. We make the next/prev buttons on
// the right side, which stay there always. They are disabled if all the
// window bar items fit, and one or both are enabled if not.
//
BOOL ASShare::VIEWWindowBarCreate
(
ASPerson * pasHost,
HWND hwndBar
)
{
BOOL rc = FALSE;
RECT rect;
DebugEntry(ASShare::VIEWWindowBarCreate);
::GetClientRect(hwndBar, &rect);
rect.top += m_viewEdgeCY;
rect.right -= m_viewItemScrollCX;
//
// Create the scrollbar, vertically centered, right-justified.
//
if (!::CreateWindowEx(0, "ScrollBar", NULL,
WS_CHILD | WS_VISIBLE | SBS_HORZ | WS_CLIPSIBLINGS | WS_DISABLED,
rect.right,
(rect.top + rect.bottom - m_viewItemScrollCY) / 2,
m_viewItemScrollCX, m_viewItemScrollCY,
hwndBar, (HMENU)IDVIEW_SCROLL,
g_asInstance, NULL))
{
ERROR_OUT(("VIEWWindowBarCreate: Unable to create scroll ctrl"));
DC_QUIT;
}
//
// Create the windowbar, an integral number of items wide (including
// trailing margin).
//
pasHost->m_pView->m_viewWindowBarItemFitCount =
(rect.right - rect.left) / (m_viewItemCX + m_viewEdgeCX);
if (!::CreateWindowEx(0, VIEW_WINDOWBARITEMS_CLASS_NAME, NULL,
WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CLIPSIBLINGS,
rect.left, rect.top,
pasHost->m_pView->m_viewWindowBarItemFitCount * (m_viewItemCX + m_viewEdgeCX),
m_viewItemCY,
hwndBar, (HMENU)IDVIEW_ITEMS,
g_asInstance, pasHost))
{
ERROR_OUT(("VIEWWindowBarCreate: Unable to create window bar item list"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(ASShare::VIEWWindowBarCreate, rc);
return(rc);
}
//
// VIEWWindowBarResize()
//
// This is called when the window bar is resized, due to the frame being
// sized horizontally.
//
// It right-justifies the scroll control, then resizes the window list to
// hold however many integral items fit across.
//
void ASShare::VIEWWindowBarResize
(
ASPerson * pasHost,
HWND hwndBar
)
{
RECT rc;
DebugEntry(ASShare::VIEWWindowBarResize);
ValidateView(pasHost);
//
// Recalculate the page size, the # of items that fit across.
// If it's different, invalidate the right side of the window bar client.
// Move the scrollbar control, and update the scroll info.
//
// What might change is the number that fit across.
::GetClientRect(hwndBar, &rc);
rc.top += m_viewEdgeCY;
rc.right -= m_viewItemScrollCX;
// Move the scroll control, right justified.
::MoveWindow(::GetDlgItem(hwndBar, IDVIEW_SCROLL), rc.right,
(rc.top + rc.bottom - m_viewItemScrollCY) / 2,
m_viewItemScrollCX, m_viewItemScrollCY, TRUE);
//
// Resize the window items list to fit an integral # of items again.
//
pasHost->m_pView->m_viewWindowBarItemFitCount =
(rc.right - rc.left) / (m_viewItemCX + m_viewEdgeCX);
::MoveWindow(::GetDlgItem(hwndBar, IDVIEW_ITEMS), rc.left, rc.top,
pasHost->m_pView->m_viewWindowBarItemFitCount * (m_viewItemCX + m_viewEdgeCX),
m_viewItemCY, TRUE);
//
// Update the scroll page and pos if necessary.
//
VIEWWindowBarItemsScroll(pasHost, GET_WM_HSCROLL_MPS(SB_ENDSCROLL, 0, NULL));
DebugExitVOID(ASShare::VIEWWindowBarResize);
}
//
// VIEW_WindowBarUpdateItem()
//
// This is ONLY called for items, in the new SWL packet, that are window
// bar items. We don't call it with non-windowbar items. When done
// looping through the SWL entries, we can then remove the items on the
// window bar that were NOT seen in the new SWL packet.
//
// We will either create a new item on the window bar, or update an existing
// one. In the first case, that is always a change. In the latter, there's
// a change only if the item text changed.
//
BOOL ASShare::VIEW_WindowBarUpdateItem
(
ASPerson * pasHost,
PSWLWINATTRIBUTES pWinNew,
LPSTR pText
)
{
PWNDBAR_ITEM pItem;
BOOL viewAnyChanges = FALSE;
DebugEntry(ASView::VIEW_WindowBarUpdateItem);
ValidateView(pasHost);
ASSERT(pWinNew->flags & SWL_FLAG_WINDOW_HOSTED);
ASSERT(pWinNew->flags & SWL_FLAG_WINDOW_TASKBAR);
//
// NOTE:
// aswlLast holds the _previous_ attributes for the windows, from
// the previous SWL packet. pWinNew holds the _new_ attributes for
// the window, from the SWL packet being processed, and these
// haven't taken effect yet.
//
// Does this new item already exist on the tray?
COM_BasedListFind(LIST_FIND_FROM_FIRST, &(pasHost->m_pView->m_viewWindowBarItems),
(void**)&pItem, FIELD_OFFSET(WNDBAR_ITEM, chain),
FIELD_OFFSET(WNDBAR_ITEM, winIDRemote),
pWinNew->winID, FIELD_SIZE(WNDBAR_ITEM, winIDRemote));
if (pItem)
{
//
// Update this item, and mark it as seen.
//
ASSERT(pItem->winIDRemote == pWinNew->winID);
pItem->flags = pWinNew->flags | SWL_FLAG_INTERNAL_SEEN;
//
// Is anything going to result in a visual change? That's only
// the text currently. And we only display VIEW_MAX_ITEM_CHARS at
// most, an end ellipsis if there's too much.
//
//
// NOTE that the items are always created with maximum space for
// text, since we cannot realloc.
//
if (lstrcmp(pItem->szText, pText))
{
lstrcpyn(pItem->szText, pText, sizeof(pItem->szText));
viewAnyChanges = TRUE;
}
}
else
{
//
// Create a new item.
//
//
// A WNDBAR_ITEM also includes maximum space for text that we will
// store.
//
pItem = (PWNDBAR_ITEM) new WNDBAR_ITEM;
if (!pItem)
{
ERROR_OUT(("VIEW_WindowBarUpdateItem: no memory to create new item for remote hwnd 0x%08x",
pWinNew->winID));
}
else
{
::ZeroMemory(pItem, sizeof(*pItem));
SET_STAMP(pItem, WNDITEM);
pItem->winIDRemote = pWinNew->winID;
//
// Add SEEN to the flags; when we're done we'll remove items we haven't
// seen.
//
pItem->flags = pWinNew->flags | SWL_FLAG_INTERNAL_SEEN;
lstrcpyn(pItem->szText, pText, sizeof(pItem->szText));
// Append to end of list
COM_BasedListInsertBefore(&(pasHost->m_pView->m_viewWindowBarItems),
&(pItem->chain));
// Success!
pasHost->m_pView->m_viewWindowBarItemCount++;
viewAnyChanges = TRUE;
}
}
DebugExitBOOL(ASShare::VIEW_UpdateWindowItem, viewAnyChanges);
return(viewAnyChanges);
}
//
// VIEW_WindowBarEndUpdateItems()
//
// This turns redraw on and invalidates the window bar so it will repaint.
//
void ASShare::VIEW_WindowBarEndUpdateItems
(
ASPerson * pasHost,
BOOL viewAnyChanges
)
{
PWNDBAR_ITEM pItem;
PWNDBAR_ITEM pNext;
DebugEntry(ASShare::VIEW_WindowBarEndUpdateItems);
ValidateView(pasHost);
//
// Walk the window bar item list. Keep the ones marked as seen, but
// remove the ones we haven't seen.
//
pItem = (PWNDBAR_ITEM)COM_BasedListFirst(&(pasHost->m_pView->m_viewWindowBarItems),
FIELD_OFFSET(WNDBAR_ITEM, chain));
while (pItem)
{
pNext = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems),
pItem, FIELD_OFFSET(WNDBAR_ITEM, chain));
//
// If this item wasn't seen (existing & still existing, or new)
// during processing, it's gone. Delete it.
//
if (pItem->flags & SWL_FLAG_INTERNAL_SEEN)
{
//
// This was just added or is still around, keep it.
// But of course clear the flag, so we are clear for
// processing the next SWL packet.
//
pItem->flags &= ~SWL_FLAG_INTERNAL_SEEN;
}
else
{
//
// Remove it.
//
// We're killing the active item, clear it out.
if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem)
{
pasHost->m_pView->m_viewWindowBarActiveItem = NULL;
}
COM_BasedListRemove(&(pItem->chain));
delete pItem;
--pasHost->m_pView->m_viewWindowBarItemCount;
ASSERT(pasHost->m_pView->m_viewWindowBarItemCount >= 0);
//
// Something changed in our list
//
viewAnyChanges = TRUE;
}
pItem = pNext;
}
//
// No need to check for changes here--they would only occur if
// an item was removed in the middle, caused by Destroy which we already
// account for, or if items were appended to the end, which we account
// for in Update.
//
if (viewAnyChanges)
{
// Turn off redraw on window list
::SendDlgItemMessage(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS,
WM_SETREDRAW, FALSE, 0);
// Adjust pos
VIEWWindowBarItemsScroll(pasHost, GET_WM_HSCROLL_MPS(SB_ENDSCROLL, 0, NULL));
// Figure out active window again.
VIEW_WindowBarChangedActiveWindow(pasHost);
// Turn back on redraw
::SendDlgItemMessage(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS,
WM_SETREDRAW, TRUE, 0);
// Repaint the items.
::InvalidateRect(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS),
NULL, TRUE);
}
else
{
//
// ALWAYS do this -- our real SWL list has changed, regardless of whether
// the window bar has. And therefore we may have a different ancestor
// relationship.
//
VIEW_WindowBarChangedActiveWindow(pasHost);
}
DebugExitVOID(ASShare::VIEW_EndUpdateWindowList);
}
//
// VIEW_WindowBarChangedActiveWindow()
//
// This is called when the active window has changed, as discovered via an
// AWC packet from the host, or when we get a new SWL packet and the shared
// list is different so the window bar items may have changed.
//
// It's quite common for the active window to be (a) nothing, meaning no
// shared app window is active or (b) not something relating to what's on
// the window bar currently. The latter is a transitory condition, caused
// because SWL packets come before AWC packets.
//
void ASShare::VIEW_WindowBarChangedActiveWindow(ASPerson * pasHost)
{
PWNDBAR_ITEM pItem;
PSWLWINATTRIBUTES pWin;
int iWin;
UINT_PTR activeWinID;
TSHR_UINT32 ownerWinID;
DebugEntry(ASShare::VIEW_WindowBarChangedActiveWindow);
ValidateView(pasHost);
//
// Map this remote window to the closest window bar item in the
// ancestor hierarchy.
//
pItem = NULL;
activeWinID = pasHost->awcActiveWinID;
while (activeWinID != 0)
{
//
// Is this on the window bar?
//
COM_BasedListFind(LIST_FIND_FROM_FIRST,
&(pasHost->m_pView->m_viewWindowBarItems),
(void**)&pItem, FIELD_OFFSET(WNDBAR_ITEM, chain),
FIELD_OFFSET(WNDBAR_ITEM, winIDRemote),
activeWinID, FIELD_SIZE(WNDBAR_ITEM, winIDRemote));
if (pItem)
{
// Yes.
TRACE_OUT(("VIEW_UpdateActiveWindow: Window 0x%08x found", activeWinID));
break;
}
//
// Try to go up the chain to this window's owner. Find this item,
// then grab the owner of it, and try again.
//
ownerWinID = 0;
for (iWin = 0, pWin = pasHost->m_pView->m_aswlLast;
iWin < pasHost->m_pView->m_swlCount;
iWin++, pWin++)
{
if (pWin->winID == activeWinID)
{
// Found it.
ownerWinID = pWin->ownerWinID;
break;
}
}
activeWinID = ownerWinID;
}
//
// Now see if the active item is different.
//
VIEWWindowBarChangeActiveItem(pasHost, pItem);
DebugExitVOID(ASShare::VIEW_WindowBarChangedActiveWindow);
}
//
// VIEWWindowBarFirstVisibleItem()
//
// This returns a pointer to the first visible item. We must loop through
// the invisible items first. Since this doesn't happen with a lot of
// frequence, and the size of the list is rarely that big, this is fine.
//
// We return NULL if the list is empty.
//
PWNDBAR_ITEM ASShare::VIEWWindowBarFirstVisibleItem(ASPerson * pasHost)
{
PWNDBAR_ITEM pItem;
int iItem;
ValidateView(pasHost);
if (!pasHost->m_pView->m_viewWindowBarItemCount)
{
pItem = NULL;
DC_QUIT;
}
ASSERT(pasHost->m_pView->m_viewWindowBarItemFirst < pasHost->m_pView->m_viewWindowBarItemCount);
pItem = (PWNDBAR_ITEM)COM_BasedListFirst(&(pasHost->m_pView->m_viewWindowBarItems),
FIELD_OFFSET(WNDBAR_ITEM, chain));
for (iItem = 0; iItem < pasHost->m_pView->m_viewWindowBarItemFirst; iItem++)
{
ASSERT(pItem);
pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems),
pItem, FIELD_OFFSET(WNDBAR_ITEM, chain));
}
ASSERT(pItem);
DC_EXIT_POINT:
DebugExitPVOID(ASShare::VIEWWindowBarFirstVisibleItem, pItem);
return(pItem);
}
//
// VIEWWindowBarChangeActiveItem()
//
// Updates the active item on the window bar. This happens when either
// we get a new AWC packet telling us there's a new active window on the host,
// or when we get a SWL packet, which may have added/removed items. This
// also happens when one is clicked on and the user is in control of the host.
//
void ASShare::VIEWWindowBarChangeActiveItem
(
ASPerson * pasHost,
PWNDBAR_ITEM pItem
)
{
DebugEntry(ASShare::VIEWWindowBarChangeActiveItem);
//
// If it's the active one already, nothing to do.
//
if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem)
{
TRACE_OUT(("VIEWWindowBarChangeActiveItem: activating current item, nothing to do"));
DC_QUIT;
}
//
// Now make the visual change
//
if (pasHost->m_pView->m_viewWindowBarActiveItem)
{
VIEWWindowBarItemsInvalidate(pasHost, pasHost->m_pView->m_viewWindowBarActiveItem);
}
pasHost->m_pView->m_viewWindowBarActiveItem = pItem;
if (pItem)
{
VIEWWindowBarItemsInvalidate(pasHost, pItem);
}
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEWWindowBarChangeActiveItem);
}
//
// VIEWWindowBarItemsScroll()
//
// This is called when the end user presses a scroll button to shuffle over
// the visible window bar items. And also when items are added/removed
// so that scroll stuff is adjusted.
//
void ASShare::VIEWWindowBarItemsScroll
(
ASPerson * pasHost,
WPARAM wParam,
LPARAM lParam
)
{
int oldPos;
int newPos;
SCROLLINFO si;
DebugEntry(ASShare::VIEWWindowBarItemsScroll);
ValidateView(pasHost);
oldPos = pasHost->m_pView->m_viewWindowBarItemFirst;
switch (GET_WM_HSCROLL_CODE(wParam, lParam))
{
case SB_LINEUP:
case SB_PAGEUP:
newPos = oldPos - 1;
break;
case SB_LINEDOWN:
case SB_PAGEDOWN:
newPos = oldPos + 1;
break;
case SB_TOP:
newPos = 0;
break;
case SB_BOTTOM:
newPos = pasHost->m_pView->m_viewWindowBarItemCount;
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
newPos = GET_WM_HSCROLL_POS(wParam, lParam);
break;
default:
newPos = oldPos;
break;
}
//
// Pin position into range, taking care to show the maximum number
// of items that will fit in the space.
//
if (newPos + pasHost->m_pView->m_viewWindowBarItemFitCount >
pasHost->m_pView->m_viewWindowBarItemCount)
{
newPos = pasHost->m_pView->m_viewWindowBarItemCount -
pasHost->m_pView->m_viewWindowBarItemFitCount;
}
if (newPos < 0)
newPos = 0;
//
// Has the position changed?
//
if (newPos != oldPos)
{
pasHost->m_pView->m_viewWindowBarItemFirst = newPos;
//
// Scroll the item area over. This will do nothing if redraw is off.
// Conveniently!
//
::ScrollWindowEx(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS),
(oldPos - newPos) * (m_viewItemCX + m_viewEdgeCX),
0,
NULL, NULL, NULL, NULL,
SW_INVALIDATE | SW_ERASE);
}
//
// If nothing's changed, no big deal.
//
::ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_DISABLENOSCROLL | SIF_POS | SIF_PAGE | SIF_RANGE;
si.nMin = 0;
si.nMax = pasHost->m_pView->m_viewWindowBarItemCount - 1;
si.nPage = pasHost->m_pView->m_viewWindowBarItemFitCount;
si.nPos = pasHost->m_pView->m_viewWindowBarItemFirst;
::SetScrollInfo(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_SCROLL),
SB_CTL, &si, TRUE);
DebugExitVOID(ASShare::VIEWWindowBarItemsScroll);
}
//
// VIEWWindowBarItemsProc()
//
LRESULT CALLBACK VIEWWindowBarItemsProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
return(g_asSession.pShare->VIEW_WindowBarItemsProc(hwnd, message, wParam, lParam));
}
LRESULT ASShare::VIEW_WindowBarItemsProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT rc = 0;
ASPerson * pasHost;
DebugEntry(ASShare::VIEW_WindowBarItemsProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pasHost)
{
ValidateView(pasHost);
}
switch (message)
{
case WM_NCCREATE:
{
// Get & save the person this view is for.
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams;
ValidateView(pasHost);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
COM_BasedListInit(&(pasHost->m_pView->m_viewWindowBarItems));
goto DefWndProc;
break;
}
case WM_NCDESTROY:
{
if (pasHost != NULL)
{
// Loop through the items, killing the head, until done.
PWNDBAR_ITEM pItem;
while (pItem = (PWNDBAR_ITEM)COM_BasedListFirst(
&(pasHost->m_pView->m_viewWindowBarItems),
FIELD_OFFSET(WNDBAR_ITEM, chain)))
{
COM_BasedListRemove(&(pItem->chain));
delete pItem;
}
//
// Zero these out for safety. Yes, we're about to free
// m_pView altogether, so find out if we're referencing
// stuff that's gone.
//
pasHost->m_pView->m_viewWindowBarItemCount = 0;
pasHost->m_pView->m_viewWindowBarActiveItem = NULL;
}
goto DefWndProc;
break;
}
case WM_ENABLE:
{
// Repaint the items, disabled or pressable.
::InvalidateRect(hwnd, NULL, FALSE);
break;
}
case WM_PAINT:
{
VIEWWindowBarItemsPaint(pasHost, hwnd);
break;
}
case WM_LBUTTONDOWN:
{
VIEWWindowBarItemsClick(pasHost, hwnd,
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
}
default:
DefWndProc:
{
rc = DefWindowProc(hwnd, message, wParam, lParam);
break;
}
}
DebugExitDWORD(ASShare::VIEW_WindowBarItemsProc, rc);
return(rc);
}
//
// VIEWWindowBarItemsPaint()
//
void ASShare::VIEWWindowBarItemsPaint
(
ASPerson * pasHost,
HWND hwndItems
)
{
HFONT hfnT;
COLORREF clrText;
int bkMode;
PWNDBAR_ITEM pItem;
PAINTSTRUCT ps;
int xT;
RECT rcItem;
DebugEntry(ASShare::VIEWWindowBarItemsPaint);
ValidateView(pasHost);
::BeginPaint(hwndItems, &ps);
//
// Skip over the visible items to the left of the paint area.
//
xT = 0;
pItem = VIEWWindowBarFirstVisibleItem(pasHost);
while (pItem && (xT + m_viewItemCX < ps.rcPaint.left))
{
pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems),
pItem, FIELD_OFFSET(WNDBAR_ITEM, chain));
xT += m_viewItemCX + m_viewEdgeCX;
}
//
// Setup painting objects, etc.
//
hfnT = SelectFont(ps.hdc, ::GetStockObject(DEFAULT_GUI_FONT));
if ((pasHost->m_caControlledBy != m_pasLocal) || pasHost->m_caControlPaused)
{
clrText = ::GetSysColor(COLOR_GRAYTEXT);
}
else
{
clrText = ::GetSysColor(COLOR_BTNTEXT);
}
clrText = ::SetTextColor(ps.hdc, clrText);
bkMode = ::SetBkMode(ps.hdc, TRANSPARENT);
//
// Now paint the visible items within the paint area.
//
while (pItem && (xT < ps.rcPaint.right))
{
rcItem.left = xT;
rcItem.top = 0;
rcItem.right = rcItem.left + m_viewItemCX;
rcItem.bottom = rcItem.top + m_viewItemCY;
//
// Draw button area, pressed in & checked for current tray item.
//
DrawFrameControl(ps.hdc, &rcItem, DFC_BUTTON,
DFCS_BUTTONPUSH | DFCS_ADJUSTRECT |
((pItem == pasHost->m_pView->m_viewWindowBarActiveItem) ? (DFCS_PUSHED | DFCS_CHECKED) : 0));
// Subtract some margin.
::InflateRect(&rcItem, -m_viewEdgeCX, -m_viewEdgeCY);
if (pItem == pasHost->m_pView->m_viewWindowBarActiveItem)
{
// Offset one for pushed effect
::OffsetRect(&rcItem, 1, 1);
}
//
// Draw icon
//
::DrawIconEx(ps.hdc, rcItem.left,
(rcItem.top + rcItem.bottom - ::GetSystemMetrics(SM_CYSMICON)) / 2,
g_hetASIconSmall,
::GetSystemMetrics(SM_CXSMICON),
::GetSystemMetrics(SM_CYSMICON),
0, NULL, DI_NORMAL);
rcItem.left += ::GetSystemMetrics(SM_CXSMICON) + m_viewEdgeCX;
//
// Draw item text
//
::DrawText(ps.hdc, pItem->szText, -1, &rcItem, DT_NOCLIP | DT_EXPANDTABS |
DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
pItem = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems),
pItem, FIELD_OFFSET(WNDBAR_ITEM, chain));
xT += m_viewItemCX + m_viewEdgeCX;
}
::SetBkMode(ps.hdc, bkMode);
::SetTextColor(ps.hdc, clrText);
SelectFont(ps.hdc, hfnT);
::EndPaint(hwndItems, &ps);
DebugExitVOID(ASShare::VIEWWindowBarItemsPaint);
}
//
// VIEWWindowBarItemsClick()
//
// Handles a left click on the window bar area. When we are in control, this
// will try to activate/restore the remote window the clicked item represents.
//
void ASShare::VIEWWindowBarItemsClick
(
ASPerson * pasHost,
HWND hwndItems,
int x,
int y
)
{
RECT rc;
PWNDBAR_ITEM pItemT;
DebugEntry(ASShare::VIEWWindowBarClick);
ValidateView(pasHost);
//
// If we're not in control of this host, or there aren't any items, we're
// done.
//
if ((pasHost->m_caControlledBy != m_pasLocal) ||
pasHost->m_caControlPaused ||
(!pasHost->m_pView->m_viewWindowBarItemCount))
{
DC_QUIT;
}
::GetClientRect(hwndItems, &rc);
//
// Start at first visible item.
//
pItemT = VIEWWindowBarFirstVisibleItem(pasHost);
while (pItemT && (rc.left < rc.right))
{
// Is x in range?
if ((x >= rc.left) && (x < rc.left + m_viewItemCX))
{
// YES! We've found the item. If it's different than the
// current one, send a packet to the host.
//
// LAURABU BUGBUG:
// Should we do this always? Is it possible to have an active
// item whose z-order would change if the active button was
// pressed again?
//
// We're trying to avoid sending a ton of requests from somebody
// who clicks repeatedly on the same button, when we haven't
// received an AWC notification back.
//
VIEWWindowBarDoActivate(pasHost, pItemT);
break;
}
pItemT = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems),
pItemT, FIELD_OFFSET(WNDBAR_ITEM, chain));
rc.left += m_viewItemCX + m_viewEdgeCX;
}
DC_EXIT_POINT:
DebugExitVOID(ASShare::VIEWWindowBarItemsClick);
}
//
// VIEWWindowBarDoActivate()
//
// Sends command to remote host requesting the window be activated and
// maybe unminimized.
//
// This is used when clicking on a button or choosing the window's item in
// the Applications menu.
//
void ASShare::VIEWWindowBarDoActivate
(
ASPerson * pasHost,
PWNDBAR_ITEM pItem
)
{
DebugEntry(ASShare::VIEWWindowBarDoActivate);
ValidateView(pasHost);
if (pItem != pasHost->m_pView->m_viewWindowBarActiveItem)
{
// Activate it. If we can't send an activate request,
// do not update the active item.
//
if (!AWC_SendMsg(pasHost->mcsID, AWC_MSG_ACTIVATE_WINDOW,
pItem->winIDRemote, 0))
{
ERROR_OUT(("VIEWWindowBarDoActivate: can't send AWC packet so failing"));
}
else
{
VIEWWindowBarChangeActiveItem(pasHost, pItem);
}
}
// Try to restore if minimized no matter what.
if (pItem->flags & SWL_FLAG_WINDOW_MINIMIZED)
{
AWC_SendMsg(pasHost->mcsID, AWC_MSG_RESTORE_WINDOW, pItem->winIDRemote, 0);
}
DebugExitVOID(ASShare::VIEWWindowBarDoActivate);
}
//
// VIEWWindowBarItemsInvalidate()
//
// This invalidates the window bar item, if it's visible in the window bar
// list currently.
//
void ASShare::VIEWWindowBarItemsInvalidate
(
ASPerson * pasHost,
PWNDBAR_ITEM pItem
)
{
PWNDBAR_ITEM pItemT;
RECT rc;
DebugEntry(ASShare::VIEWWindowBarItemsInvalidate);
ValidateView(pasHost);
ASSERT(pItem);
::GetClientRect(::GetDlgItem(pasHost->m_pView->m_viewWindowBar, IDVIEW_ITEMS),
&rc);
//
// Start at the first visible item, and see if any in the visible range
// are this one. There will never be that many items visible across,
// it's not heinous to do this.
//
pItemT = VIEWWindowBarFirstVisibleItem(pasHost);
while (pItemT && (rc.left < rc.right))
{
if (pItemT == pItem)
{
// Found it, it's in the visible range. Invalidate it.
rc.right = rc.left + m_viewItemCX;
::InvalidateRect(::GetDlgItem(pasHost->m_pView->m_viewWindowBar,
IDVIEW_ITEMS), &rc, TRUE);
break;
}
pItemT = (PWNDBAR_ITEM)COM_BasedListNext(&(pasHost->m_pView->m_viewWindowBarItems),
pItemT, FIELD_OFFSET(WNDBAR_ITEM, chain));
rc.left += m_viewItemCX + m_viewEdgeCX;
}
DebugExitVOID(ASShare::VIEWWindowBarItemsInvalidate);
}
//
// VIEWFullScreenExitProc()
//
// Window handler for full screen exit button.
//
LRESULT CALLBACK VIEWFullScreenExitProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
return(g_asSession.pShare->VIEW_FullScreenExitProc(hwnd, message, wParam, lParam));
}
//
// VIEW_FullScreenExitProc()
//
LRESULT ASShare::VIEW_FullScreenExitProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT rc = 0;
ASPerson * pasHost;
DebugEntry(VIEW_FullScreenExitProc);
pasHost = (ASPerson *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pasHost)
{
ValidateView(pasHost);
}
switch (message)
{
case WM_NCCREATE:
{
// Get the passed in host pointer, and set in our window long
pasHost = (ASPerson *)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pasHost);
goto DefWndProc;
break;
}
case WM_NCDESTROY:
{
//
// Make sure tracking is stopped.
//
pasHost->m_pView->m_viewFullScreenExitTrack = FALSE;
break;
}
case WM_ERASEBKGND:
{
rc = TRUE;
break;
}
case WM_PAINT:
{
VIEWFullScreenExitPaint(pasHost, hwnd);
break;
}
case WM_LBUTTONDOWN:
{
//
// Start tracking to move or click button.
//
pasHost->m_pView->m_viewFullScreenExitTrack = TRUE;
pasHost->m_pView->m_viewFullScreenExitMove = FALSE;
// Original click, relative to our client
pasHost->m_pView->m_viewFullScreenExitStart.x =
GET_X_LPARAM(lParam);
pasHost->m_pView->m_viewFullScreenExitStart.y =
GET_Y_LPARAM(lParam);
// Set capture, and wait for moves/button up
SetCapture(hwnd);
break;
}
case WM_MOUSEMOVE:
{
if (pasHost->m_pView->m_viewFullScreenExitTrack)
{
POINT ptMove;
ptMove.x = GET_X_LPARAM(lParam);
ptMove.y = GET_Y_LPARAM(lParam);
//
// If we're not in move mode, see if this has pushed us over
// the tolerance.
//
if (!pasHost->m_pView->m_viewFullScreenExitMove)
{
if ((abs(ptMove.x - pasHost->m_pView->m_viewFullScreenExitStart.x) >
GetSystemMetrics(SM_CXDRAG)) ||
(abs(ptMove.y - pasHost->m_pView->m_viewFullScreenExitStart.y) >
GetSystemMetrics(SM_CYDRAG)))
{
//
// User has moved out of tolerance zone, must be
// dragging to move the button out of the way.
//
pasHost->m_pView->m_viewFullScreenExitMove = TRUE;
}
}
if (pasHost->m_pView->m_viewFullScreenExitMove)
{
RECT rcWindow;
//
// Move the button so that the cursor is over the
// same point as originally clicked on.
//
// Get our current position, in parent coordsinates.
GetWindowRect(hwnd, &rcWindow);
MapWindowPoints(NULL, GetParent(hwnd), (LPPOINT)&rcWindow, 2);
// Offset it by the amount of the move.
OffsetRect(&rcWindow,
ptMove.x - pasHost->m_pView->m_viewFullScreenExitStart.x,
ptMove.y - pasHost->m_pView->m_viewFullScreenExitStart.y);
SetWindowPos(hwnd, NULL, rcWindow.left, rcWindow.top, 0, 0,
SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
}
}
break;
}
case WM_LBUTTONUP:
{
if (pasHost->m_pView->m_viewFullScreenExitTrack)
{
//
// This will send us CAPTURECHANGED, causing us to clear
// the ExitTrack flag.
//
ReleaseCapture();
//
// If we never transitioned into move mode, then this was
// a click on the button.
//
if (!pasHost->m_pView->m_viewFullScreenExitMove)
{
//
// This was a click, send a command.
//
PostMessage(pasHost->m_pView->m_viewFrame, WM_COMMAND, CMD_VIEWFULLSCREEN, 0);
}
}
break;
}
case WM_CAPTURECHANGED:
{
//
// If we're tracking, something happened, so cancel out.
//
if (pasHost->m_pView->m_viewFullScreenExitTrack)
{
pasHost->m_pView->m_viewFullScreenExitTrack = FALSE;
}
break;
}
default:
DefWndProc:
rc = DefWindowProc(hwnd, message, wParam, lParam);
break;
}
DebugExitDWORD(VIEW_FullScreenExitProc, rc);
return(rc);
}
//
// VIEWFullScreenExitPaint()
//
// Paints the full screen button.
//
void ASShare::VIEWFullScreenExitPaint
(
ASPerson * pasHost,
HWND hwnd
)
{
RECT rc;
PAINTSTRUCT ps;
char szRestore[256];
HFONT hfnOld;
COLORREF txtColor;
COLORREF bkColor;
DebugEntry(ASShare::VIEWFullScreenExitPaint);
BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
DrawFrameControl(ps.hdc, &rc, DFC_BUTTON, DFCS_BUTTONPUSH |
DFCS_ADJUSTRECT);
// Margin adjustments...
InflateRect(&rc, -m_viewEdgeCX, -m_viewEdgeCY);
DrawIconEx(ps.hdc, rc.left,
(rc.top + rc.bottom - GetSystemMetrics(SM_CYSMICON)) / 2,
m_viewFullScreenExitIcon,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
0, NULL, DI_NORMAL);
rc.left += GetSystemMetrics(SM_CXSMICON) + m_viewEdgeCX;
hfnOld = SelectFont(ps.hdc, GetStockObject(DEFAULT_GUI_FONT));
txtColor = SetTextColor(ps.hdc, GetSysColor(COLOR_BTNTEXT));
bkColor = SetBkColor(ps.hdc, GetSysColor(COLOR_BTNFACE));
LoadString(g_asInstance, IDS_RESTORE, szRestore, sizeof(szRestore));
DrawText(ps.hdc, szRestore, -1, &rc, DT_NOCLIP | DT_EXPANDTABS |
DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE);
SetBkColor(ps.hdc, bkColor);
SetTextColor(ps.hdc, txtColor);
SelectFont(ps.hdc, hfnOld);
EndPaint(hwnd, &ps);
DebugExitVOID(ASShare::VIEWFullScreenExitPaint);
}