/****************************** 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(KPRECT_TO_PRECT(&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, KPRECT_TO_PRECT(&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(KPRECT_TO_PRECT(&pwnd->rcWindow), dwFlags); NoWindow: if (dwFlags & (MONITOR_DEFAULTTOPRIMARY | MONITOR_DEFAULTTONEAREST)) { return GetPrimaryMonitor(); } return NULL; }