Windows2000/private/ntos/w32/ntuser/rtl/mmrtl.c
2020-09-30 17:12:32 +02:00

545 lines
24 KiB
C

/****************************** Module Header ******************************\
* Module Name: mmrtl.c
* Copyright (c) 1985 - 1999, Microsoft Corporation
* Multimonitor APIs.
* History:
* 29-Mar-1997 adams Created.
*/
#define Int32x32To32(x, y) ((x) * (y))
/*
* There is no object locking in the client, so the monitor object can
* go away at any time. Therefore to be safe, we need an exception handler.
*/
#ifdef _USERK_
#define BEGIN_EXCEPTION_HANDLER
#define END_EXCEPTION_HANDLER
#define END_EXCEPTION_HANDLER_EMPTY
#else // _USERK_
#define BEGIN_EXCEPTION_HANDLER try {
#define END_EXCEPTION_HANDLER \
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) { \
pMonitorResult = NULL; \
}
#define END_EXCEPTION_HANDLER_EMPTY \
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) { \
}
#endif // _USERK_
/*
* _MonitorFromPoint
* Calculate the monitor that a point is in or is nearest to.
* Arguments:
* pt - The point.
* dwFlags - One of:
* MONITOR_DEFAULTTONULL - If the point isn't in a monitor,
* return NULL.
* MONITOR_DEFAULTTOPRIMARY - If the point isn't in a monitor,
* return the primary monitor.
* MONITOR_DEFAULTTONEAREST - Return the monitor nearest the point.
* History:
* 22-Sep-1996 adams Created.
* 29-Mar-1997 adams Moved to rtl.
*/
PMONITOR
_MonitorFromPoint(POINT pt, DWORD dwFlags)
{
PMONITOR pMonitor, pMonitorResult;
int dx;
int dy;
UserAssert(dwFlags == MONITOR_DEFAULTTONULL ||
dwFlags == MONITOR_DEFAULTTOPRIMARY ||
dwFlags == MONITOR_DEFAULTTONEAREST);
if (GetDispInfo()->cMonitors == 1 && dwFlags != MONITOR_DEFAULTTONULL)
return GetPrimaryMonitor();
switch (dwFlags) {
case MONITOR_DEFAULTTONULL:
case MONITOR_DEFAULTTOPRIMARY:
/*
* Return the monitor the point is in.
*/
BEGIN_EXCEPTION_HANDLER
for (pMonitor = REBASESHAREDPTRALWAYS(GetDispInfo()->pMonitorFirst);
pMonitor;
pMonitor = REBASESHAREDPTR(pMonitor->pMonitorNext)) {
if (!(pMonitor->dwMONFlags & MONF_VISIBLE))
continue;
if (PtInRect(&((MONITOR*)pMonitor)->rcMonitor, pt)) {
return pMonitor;
}
}
END_EXCEPTION_HANDLER_EMPTY
/*
* Return what the user wants if it's not found.
*/
switch (dwFlags) {
case MONITOR_DEFAULTTONULL:
return NULL;
case MONITOR_DEFAULTTOPRIMARY:
return GetPrimaryMonitor();
default:
UserAssertMsg0(FALSE, "Logic error in _MonitorFromPoint");
break;
}
case MONITOR_DEFAULTTONEAREST:
#define MONITORFROMPOINTALGORITHM(SUMSQUARESMAX, SUMSQUARESTYPE, POINTMULTIPLY) \
SUMSQUARESTYPE sumsquare; \
SUMSQUARESTYPE leastsumsquare; \
leastsumsquare = SUMSQUARESMAX; \
for ( pMonitor = REBASESHAREDPTRALWAYS(GetDispInfo()->pMonitorFirst); \
pMonitor; \
pMonitor = REBASESHAREDPTR(pMonitor->pMonitorNext)) { \
\
if (!(pMonitor->dwMONFlags & MONF_VISIBLE)) \
continue; \
\
/* \
* Determine distance from monitor along x axis. \
*/ \
if (pt.x < pMonitor->rcMonitor.left) { \
dx = pMonitor->rcMonitor.left - pt.x; \
} else if (pt.x < pMonitor->rcMonitor.right) { \
dx = 0; \
} else { \
/* \
* Monitor rectangles do not include the rightmost edge. \
*/ \
dx = pt.x - (pMonitor->rcMonitor.right - 1); \
} \
\
/* \
* Skip this monitor if dx is greater than dx^2 + dy^2. \
* We do this check to avoid multiplication operations. \
*/ \
if ((SUMSQUARESTYPE) dx >= leastsumsquare) \
continue; \
\
/* \
* Determine distance from monitor along y axis. \
*/ \
if (pt.y < pMonitor->rcMonitor.top) { \
dy = pMonitor->rcMonitor.top - pt.y; \
} else if (pt.y < pMonitor->rcMonitor.bottom) { \
/* \
* The point is in the monitor and we're done \
* if both dx and dy are zero. \
*/ \
if (dx == 0) \
return pMonitor; \
\
dy = 0; \
} else { \
dy = pt.y - (pMonitor->rcMonitor.bottom - 1); \
} \
\
/* \
* Calculate dx^2. Skip this monitor if dx is greater \
* than dx^2 + dy^2. We do this check to avoid \
* multiplication operations. \
*/ \
sumsquare = POINTMULTIPLY(dx, dx); \
if (sumsquare >= leastsumsquare) \
continue; \
\
/* \
* Skip this monitor if dx^2 + y is greater than dx^2 + dy^2. \
* We do this check to avoid multiplication operations. \
*/ \
if (sumsquare + (SUMSQUARESTYPE) dy >= leastsumsquare) \
continue; \
\
/* \
* Compute dx^2 + dy^2. Skip this monitor if it's not the least. \
*/ \
sumsquare += (SUMSQUARESTYPE) POINTMULTIPLY(dy, dy); \
if (sumsquare >= leastsumsquare) \
continue; \
\
/* \
* This is the closest monitor so far. \
*/ \
leastsumsquare = sumsquare; \
pMonitorResult = pMonitor; \
}
#if DBG
pMonitorResult = (PMONITOR)-1;
#endif
if (pt.x < SHRT_MIN || SHRT_MAX < pt.x ||
pt.y < SHRT_MIN || SHRT_MAX < pt.y) {
BEGIN_EXCEPTION_HANDLER
MONITORFROMPOINTALGORITHM(_UI64_MAX, ULONGLONG, Int32x32To64)
END_EXCEPTION_HANDLER
} else {
BEGIN_EXCEPTION_HANDLER
MONITORFROMPOINTALGORITHM(UINT_MAX, UINT, Int32x32To32)
END_EXCEPTION_HANDLER
}
UserAssert(pMonitorResult != (PMONITOR)-1);
return pMonitorResult;
default:
UserAssert(0 && "Logic error in _MonitorFromPoint, shouldn't have gotten here.");
break;
}
UserAssert(0 && "Logic error in _MonitorFromPoint, shouldn't have gotten here.");
return NULL;
}
/*
* _MonitorFromRect
* Calculate the monitor that a rect is in or is nearest to.
* Arguments:
* lprc - The rect.
* dwFlags - One of:
* MONITOR_DEFAULTTONULL - If the rect doesn't intersect a monitor,
* return NULL.
* MONITOR_DEFAULTTOPRIMARY - If the rect doesn't intersect a monitor,
* return the primary monitor.
* MONITOR_DEFAULTTONEAREST - Return the monitor nearest the rect.
* History:
* 22-Sep-1996 adams Created.
* 29-Mar-1997 adams Moved to rtl.
*/
PMONITOR
_MonitorFromRect(LPCRECT lprc, DWORD dwFlags)
{
PDISPLAYINFO pDispInfo;
PMONITOR pMonitor, pMonitorResult;
RECT rc;
int area, areaMost;
UserAssert(dwFlags == MONITOR_DEFAULTTONULL ||
dwFlags == MONITOR_DEFAULTTOPRIMARY ||
dwFlags == MONITOR_DEFAULTTONEAREST);
/*
* Special case the most common case - 1 monitor.
*/
pDispInfo = GetDispInfo();
if (pDispInfo->cMonitors == 1 && dwFlags != MONITOR_DEFAULTTONULL)
return GetPrimaryMonitor();
/*
* If rect is empty, use topleft point.
*/
if (IsRectEmpty(lprc)) {
return _MonitorFromPoint(*(LPPOINT)lprc, dwFlags);
}
/*
* Return the primary monitor if the rectangle covers the desktop.
*/
if (lprc->left <= pDispInfo->rcScreen.left &&
lprc->top <= pDispInfo->rcScreen.top &&
lprc->right >= pDispInfo->rcScreen.right &&
lprc->bottom >= pDispInfo->rcScreen.bottom) {
return GetPrimaryMonitor();
}
/*
* Calculate the nearest rectangle by determining which
* monitor has the greatest intersection with the rectangle.
*/
BEGIN_EXCEPTION_HANDLER
areaMost = 0;
for (pMonitor = REBASESHAREDPTRALWAYS(GetDispInfo()->pMonitorFirst);
pMonitor;
pMonitor = REBASESHAREDPTR(pMonitor->pMonitorNext)) {
if (!(pMonitor->dwMONFlags & MONF_VISIBLE))
continue;
if (IntersectRect(&rc, lprc, &((MONITOR*)pMonitor)->rcMonitor)) {
if (EqualRect(&rc, lprc))
return pMonitor;
/*
* Calculate the area of the intersection. Note that
* the intersection must be in 16bit coordinats, since
* we limit monitor rects to 16bit coordinate space.
* So the result of any area calculation will fit in
* in an int.
*/
area = (rc.right - rc.left) * (rc.bottom - rc.top);
if (area > areaMost) {
areaMost = area;
pMonitorResult = pMonitor;
}
}
}
END_EXCEPTION_HANDLER
UserAssert(areaMost >= 0);
if (areaMost > 0)
return pMonitorResult;
switch (dwFlags) {
case MONITOR_DEFAULTTONULL:
return NULL;
case MONITOR_DEFAULTTOPRIMARY:
return GetPrimaryMonitor();
case MONITOR_DEFAULTTONEAREST:
{
int dx, dy;
#define MONITORFROMRECTALGORITHM(SUMSQUARESMAX, SUMSQUARESTYPE, POINTMULTIPLY) \
SUMSQUARESTYPE sumsquare; \
SUMSQUARESTYPE leastsumsquare; \
leastsumsquare = SUMSQUARESMAX; \
for ( pMonitor = REBASESHAREDPTRALWAYS(GetDispInfo()->pMonitorFirst); \
pMonitor; \
pMonitor = REBASESHAREDPTR(pMonitor->pMonitorNext)) { \
\
if (!(pMonitor->dwMONFlags & MONF_VISIBLE)) \
continue; \
\
/* \
* Determine distance from monitor along x axis. \
*/ \
if (lprc->right <= pMonitor->rcMonitor.left) { \
/* \
* Add 1 because rectangles do not include the rightmost edge. \
*/ \
dx = pMonitor->rcMonitor.left - lprc->right + 1; \
} else if (lprc->left < pMonitor->rcMonitor.right) { \
dx = 0; \
} else { \
/* \
* Add 1 because rectangles do not include the rightmost edge. \
*/ \
dx = lprc->left - (pMonitor->rcMonitor.right - 1); \
} \
\
/* \
* Skip this monitor if dx is greater than dx^2 + dy^2. \
* We do this check to avoid multiplication operations. \
*/ \
if ((SUMSQUARESTYPE) dx >= leastsumsquare) \
continue; \
\
/* \
* Determine distance from monitor along y axis. \
*/ \
if (lprc->bottom <= pMonitor->rcMonitor.top) { \
/* \
* Add 1 because rectangles do not include the bottommost edge. \
*/ \
dy = pMonitor->rcMonitor.top - lprc->bottom + 1; \
} else if (lprc->top < pMonitor->rcMonitor.bottom) { \
UserAssert(dx != 0 && "This rectangle intersects a monitor, so we shouldn't be here."); \
dy = 0; \
} else { \
/* \
* Add 1 because rectangles do not include the bottommost edge. \
*/ \
dy = lprc->top - pMonitor->rcMonitor.bottom + 1; \
} \
\
/* \
* Calculate dx^2. Skip this monitor if dx is greater \
* than dx^2 + dy^2. We do this check to avoid \
* multiplication operations. \
*/ \
sumsquare = POINTMULTIPLY(dx, dx); \
if (sumsquare >= leastsumsquare) \
continue; \
\
/* \
* Skip this monitor if dx^2 + y is greater than dx^2 + dy^2. \
* We do this check to avoid multiplication operations. \
*/ \
if (sumsquare + (SUMSQUARESTYPE) dy >= leastsumsquare) \
continue; \
\
/* \
* Compute dx^2 + dy^2. Skip this monitor if it's not the least. \
*/ \
sumsquare += (SUMSQUARESTYPE) POINTMULTIPLY(dy, dy); \
if (sumsquare >= leastsumsquare) \
continue; \
\
/* \
* This is the closest monitor so far. \
*/ \
leastsumsquare = sumsquare; \
pMonitorResult = pMonitor; \
}
#if DBG
pMonitorResult = (PMONITOR)-1;
#endif
if (lprc->left < SHRT_MIN || SHRT_MAX < lprc->left ||
lprc->top < SHRT_MIN || SHRT_MAX < lprc->top ||
lprc->right < SHRT_MIN || SHRT_MAX < lprc->right ||
lprc->bottom < SHRT_MIN || SHRT_MAX < lprc->bottom) {
BEGIN_EXCEPTION_HANDLER
MONITORFROMRECTALGORITHM(_UI64_MAX, ULONGLONG, Int32x32To64)
END_EXCEPTION_HANDLER
} else {
BEGIN_EXCEPTION_HANDLER
MONITORFROMRECTALGORITHM(UINT_MAX, UINT, Int32x32To32)
END_EXCEPTION_HANDLER
}
UserAssert(pMonitorResult != (PMONITOR)-1);
return pMonitorResult;
}
default:
UserAssertMsg0(0, "Logic error in _MonitorFromWindow, shouldn't have gotten here.");
break;
}
UserAssertMsg0(0, "Logic error in _MonitorFromWindow, shouldn't have gotten here.");
return NULL;
}
/*
* _MonitorFromWindow
* Calculate the monitor that a window is in or is nearest to. We use
* the center of the window to determine its location. If the window
* is minimized, use its normal position.
* Arguments:
* pwnd - The window.
* dwFlags - One of:
* MONITOR_DEFAULTTONULL - If the window doesn't intersect a monitor,
* return NULL.
* MONITOR_DEFAULTTOPRIMARY - If the window doesn't intersect a monitor,
* return the primary monitor.
* MONITOR_DEFAULTTONEAREST - Return the monitor nearest the window.
* History:
* 22-Sep-1996 adams Created.
* 29-Mar-1997 adams Moved to rtl.
*/
PMONITOR
_MonitorFromWindow(PWND pwnd, DWORD dwFlags)
{
PWND pwndParent;
UserAssert(dwFlags == MONITOR_DEFAULTTONULL ||
dwFlags == MONITOR_DEFAULTTOPRIMARY ||
dwFlags == MONITOR_DEFAULTTONEAREST);
if (GetDispInfo()->cMonitors == 1 && dwFlags != MONITOR_DEFAULTTONULL) {
return GetPrimaryMonitor();
}
if (!pwnd)
goto NoWindow;
/*
* Handle minimized windows.
*/
if (TestWF(pwnd, WFMINIMIZED)) {
#ifdef _USERK_
CHECKPOINT* pcp;
pcp = (CHECKPOINT*)_GetProp(pwnd, PROP_CHECKPOINT, PROPF_INTERNAL);
if (pcp) {
return _MonitorFromRect(&pcp->rcNormal, dwFlags);
}
#else
WINDOWPLACEMENT wp;
HWND hwnd;
wp.length = sizeof(wp);
hwnd = (HWND)PtoH(pwnd);
if (GetWindowPlacement(hwnd, &wp)) {
return _MonitorFromRect(&wp.rcNormalPosition, dwFlags);
}
/*
* (adams) If GetWindowPlacement fails, then either there was not enough
* memory to allocate a CHECKPOINT, or the window was destroyed
* and the API failed. If the later, the following code my be
* playing with invalid memory. Although on the client side we
* can never guarantee that a window is valid, it seems especially
* likely that it is invalid here. So do another revalidation
* by calling IsWindow.
*/
if (!IsWindow(hwnd))
goto NoWindow;
#endif
UserAssert(GETFNID(pwnd) != FNID_DESKTOP);
pwndParent = REBASEPWND(pwnd, spwndParent);
if (GETFNID(pwndParent) == FNID_DESKTOP) {
return GetPrimaryMonitor();
}
/*
* Otherwise, if we are a child window, fall thru below to use the
* window rect, which actually means something for non-toplevel dudes.
*/
}
return _MonitorFromRect(&((WND*)pwnd)->rcWindow, dwFlags);
NoWindow:
if (dwFlags & (MONITOR_DEFAULTTOPRIMARY | MONITOR_DEFAULTTONEAREST)) {
return GetPrimaryMonitor();
}
return NULL;
}