mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4360 lines
122 KiB
4360 lines
122 KiB
#include "precomp.h"
|
|
#include <version.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 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_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 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_viewFullScreen);
|
|
|
|
pasHost->m_pView->m_viewStatusBarOn = 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.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);
|
|
}
|
|
|
|
//
|
|
// 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_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));
|
|
|
|
pasHost->viewExtent.x = pasHost->cpcCaps.screen.capsScreenWidth;
|
|
pasHost->viewExtent.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);
|
|
}
|
|
|
|
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_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 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 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_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_FORWARDCONTROLSTART) && (cmd < CMD_FORWARDCONTROLMAX))
|
|
{
|
|
if ((pasHost->m_caControlledBy == m_pasLocal))
|
|
{
|
|
//
|
|
// 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)
|
|
{
|
|
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)
|
|
{
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
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);
|
|
|
|
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 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;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
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))
|
|
{
|
|
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))
|
|
{
|
|
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))
|
|
{
|
|
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))
|
|
{
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|