/****************************** Module Header ******************************\ * Module Name: multimon.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Multimonitor APIs. * * History: * 27-Sep-1996 adams Stub implementation for NT 5. * 20-Feb-1997 adams Port from NT4 SP3. \***************************************************************************/ #include "precomp.h" #pragma hdrstop /* last monitor the cursor was clipped to */ PMONITOR gpMonitorMouse; /***************************************************************************\ * ClipPointToDesktop * * Clips the point the nearest monitor on the desktop. * * Arguments: * lppt - The point to clip. * * History: * 22-Sep-1996 adams Created. * 04-Sep-1998 MCostea Use _MonitorFromPoint() \***************************************************************************/ void ClipPointToDesktop(LPPOINT lppt) { PMONITOR pMonitor; UserAssert(!gpDispInfo->fDesktopIsRect && "You shouldn't call this function if the desktop is a rectangle.\n" "Just clip to gpsi->rcScreen instead."); /* * Optimization: The cursor is likely to be on the monitor it was last on, * so check for that case. */ if (gpMonitorMouse != NULL && PtInRect(&gpMonitorMouse->rcMonitor, *lppt)) { return; } pMonitor = _MonitorFromPoint(*lppt, MONITOR_DEFAULTTONEAREST); /* * Remember the monitor the cursor is on. */ gpMonitorMouse = pMonitor; if (lppt->x < pMonitor->rcMonitor.left) { lppt->x = pMonitor->rcMonitor.left; } else if (lppt->x >= pMonitor->rcMonitor.right) { lppt->x = pMonitor->rcMonitor.right-1; } if (lppt->y < pMonitor->rcMonitor.top) { lppt->y = pMonitor->rcMonitor.top; } else if (lppt->y >= pMonitor->rcMonitor.bottom) { lppt->y = pMonitor->rcMonitor.bottom-1; } } /***************************************************************************\ * xxxEnumDisplayMonitors * * Enumerates the monitors in a display. * * Arguments: * hdcPaint - An HDC with a particular visible region. The HDC * passed to lpfnEnum will have the capabilities of that monitor, * with its visible region clipped to the monitor and hdcPaint. * If hdcPaint is NULL, the hdcMonitor passed to lpfnEnum will be NULL. * * lprcClip - A rectangle to clip the area to. If hdcPaint is non-NULL, * the coordinates have the origin of hdcPaint. If hdcPaint is NULL, * the coordinates are virtual screen coordinates. If lprcClip is NULL, * no clipping is performed. * * lpfnEnum - The enumeration function. * * dwData - Application-defined data that is passed through to the * enumeration function. * * fInternal - TRUE if the callback is in the kernel, FALSE otherwise. * * History: * 22-Sep-1996 adams Created. \***************************************************************************/ BOOL xxxEnumDisplayMonitors( HDC hdcPaint, LPRECT lprcPaint, MONITORENUMPROC lpfnEnum, LPARAM lData, BOOL fInternal) { RECT rcPaint; POINT ptOrg; RECT rcMonitorPaint; BOOL fReturn; PMONITOR pMonitor; TL tlpMonitor; PTHREADINFO ptiCurrent = PtiCurrent(); PDCE pdcePaint; HDC hdcMonitor; PWND pwndOrg; /* * Validate the DC passed in. */ if (hdcPaint) { if ((pdcePaint = LookupDC(hdcPaint)) == NULL) { RIPMSG0(RIP_WARNING, "EnumDisplayMonitors: LookupDC failed"); return FALSE; } pwndOrg = pdcePaint->pwndOrg; /* * Intersect the painting area with the clipbox. If there * isn't anything, bail out now. */ if (GreGetClipBox(hdcPaint, &rcPaint, FALSE) == NULLREGION) return TRUE; if (lprcPaint && !IntersectRect(&rcPaint, &rcPaint, lprcPaint)) return TRUE; /* * rcPaint is in dc coordinates. We must convert to screen * coords so we can intersect with monitors. */ GreGetDCOrg(hdcPaint, &ptOrg); OffsetRect(&rcPaint, ptOrg.x, ptOrg.y); } else { CopyRect(&rcPaint, &gpDispInfo->rcScreen); if (lprcPaint && !IntersectRect(&rcPaint, &rcPaint, lprcPaint)) return TRUE; } fReturn = TRUE; for (pMonitor = gpDispInfo->pMonitorFirst; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext) { /* * Note: the check for MONF_VISIBLE was removed to allow mirror drivers * to see monitor specific updates. */ if (!IntersectRect(&rcMonitorPaint, &rcPaint, &pMonitor->rcMonitor)) { continue; } if (hdcPaint) { if ((hdcMonitor = GetMonitorDC(pdcePaint, pMonitor)) == NULL) { RIPMSG0(RIP_WARNING, "EnumDisplayMonitors: GetMonitorDC failed"); return FALSE; } OffsetRect(&rcMonitorPaint, -ptOrg.x, -ptOrg.y); GreIntersectClipRect( hdcMonitor, rcMonitorPaint.left, rcMonitorPaint.top, rcMonitorPaint.right, rcMonitorPaint.bottom); } else { hdcMonitor = NULL; } ThreadLockAlwaysWithPti(ptiCurrent, pMonitor, &tlpMonitor); if (fInternal) { fReturn = (*lpfnEnum) ( (HMONITOR) pMonitor, hdcMonitor, &rcMonitorPaint, lData); } else { fReturn = xxxClientMonitorEnumProc( PtoH(pMonitor), hdcMonitor, &rcMonitorPaint, lData, lpfnEnum); } /* * We just called back and the monitor has been freed if * ThreadUnlock returns NULL. The entire monitor configuration may * have changed, the monitors may have been rearranged, so just stop * enumerating at this point. */ if (ThreadUnlock(&tlpMonitor) == NULL || HMIsMarkDestroy(pMonitor)) { /* * During the callback, pMonitor was destroyed * so we have to bail out. pMonitor->pNext may also * have been ruined. * Windows Bug #488330. */ RIPMSGF1(RIP_WARNING, "pMonitor %p has been destroyed during the callback", pMonitor); #if DBG { /* * Double check to see pMonitor does not * appear in the monitor list. */ PMONITOR pMonitorTmp = gpDispInfo->pMonitorFirst; while (pMonitorTmp) { UserAssert(pMonitorTmp != pMonitor); pMonitorTmp = pMonitorTmp->pMonitorNext; } } #endif fReturn = FALSE; } if (hdcMonitor) ReleaseCacheDC(hdcMonitor, FALSE); if (!fReturn) { /* * Something went wrong, we have to bail out. */ break; } /* * Revalidate hdcPaint, since it could have been messed with * in the callback. */ if (hdcPaint) { if ((pdcePaint = LookupDC(hdcPaint)) == NULL) { RIPMSG0(RIP_WARNING, "EnumDisplayMonitors: LookupDC failed"); return FALSE; } if (pdcePaint->pwndOrg != pwndOrg) { RIPMSG0(RIP_WARNING, "EnumDisplayMonitors: wrong window"); return FALSE; } } } return fReturn; } /***************************************************************************\ * DestroyMonitor * * This function doesn't keep track of the visible monitors count because * it assumes that after it was called the count will be recalculated, such * as the case during mode changes. For a final unlock the monitor will have * been removed from the monitor list already, so the count doesn't need to * be recalculated. * * 5-May-1998 vadimg created \***************************************************************************/ void DestroyMonitor(PMONITOR pMonitor) { UserAssert(pMonitor); /* * Remove references to this monitor from the global data. */ if (pMonitor == gpMonitorMouse) { gpMonitorMouse = NULL; } /* * Remove from the monitor list. */ REMOVE_FROM_LIST(MONITOR, gpDispInfo->pMonitorFirst, pMonitor, pMonitorNext); /* * Make sure the primary monitor points to a valid monitor. During the * mode changes the primary monitor will be recalculated as appropriate. */ if (pMonitor == gpDispInfo->pMonitorPrimary) { gpDispInfo->pMonitorPrimary = gpDispInfo->pMonitorFirst; } /* * Clean up the next link here... */ pMonitor->pMonitorNext = NULL; /* * To make sure memory write operations are retired * before really destroying the monitor object: */ Win32MemoryBarrier(); if (HMMarkObjectDestroy(pMonitor)) { if (pMonitor->hrgnMonitor) { GreDeleteObject(pMonitor->hrgnMonitor); } HMFreeObject(pMonitor); } }