From 73252e88b89746a7b3164e70768766371ee2fff4 Mon Sep 17 00:00:00 2001 From: "pinkerton%netscape.com" Date: Wed, 11 Jan 2006 21:28:03 +0000 Subject: [PATCH] Updating device context to work with multiple monitors (r=hyatt). fixes bugs 21942, 32611. --- widget/src/windows/nsScreenManagerWin.cpp | 136 ++++++++++++++++++---- widget/src/windows/nsScreenManagerWin.h | 19 ++- widget/src/windows/nsScreenWin.cpp | 130 +++++++++++++++++---- widget/src/windows/nsScreenWin.h | 10 +- 4 files changed, 249 insertions(+), 46 deletions(-) diff --git a/widget/src/windows/nsScreenManagerWin.cpp b/widget/src/windows/nsScreenManagerWin.cpp index 9d9037126c3..621dca2ed72 100644 --- a/widget/src/windows/nsScreenManagerWin.cpp +++ b/widget/src/windows/nsScreenManagerWin.cpp @@ -20,14 +20,61 @@ * Contributor(s): */ +// +// We have to do this in order to have access to the multiple-monitor +// APIs that are only defined when WINVER is >= 0x0500. Don't worry, +// these won't actually be called unless they are present. +// +#undef WINVER +#define WINVER 0x0500 + #include "nsScreenManagerWin.h" #include "nsScreenWin.h" +// needed because there are unicode/ansi versions of this routine +// and we need to make sure we get the correct one. +#ifdef UNICODE +#define GetMonitorInfoQuoted "GetMonitorInfoW" +#else +#define GetMonitorInfoQuoted "GetMonitorInfoA" +#endif + + +typedef HMONITOR (*MonitorFromRectProc)(LPCRECT inRect, DWORD inFlag); +typedef BOOL (*EnumDisplayMonitorsProc)(HDC, LPCRECT, MONITORENUMPROC, LPARAM); + +BOOL CALLBACK CountMonitors ( HMONITOR, HDC, LPRECT, LPARAM ioCount ) ; + + +class ScreenListItem +{ +public: + ScreenListItem ( HMONITOR inMon, nsIScreen* inScreen ) + : mMon(inMon), mScreen(inScreen) { } ; + + HMONITOR mMon; + nsCOMPtr mScreen; +}; + nsScreenManagerWin :: nsScreenManagerWin ( ) +: mHasMultiMonitorAPIs(PR_FALSE), mGetMonitorInfoProc(nsnull), mMonitorFromRectProc(nsnull), + mEnumDisplayMonitorsProc(nsnull), mNumberOfScreens(0) { NS_INIT_REFCNT(); + // figure out if we can call the multiple monitor APIs that are only + // available on Win98/2000. + HMODULE lib = GetModuleHandle("user32.dll"); + if ( lib ) { + mGetMonitorInfoProc = GetProcAddress ( lib, GetMonitorInfoQuoted ); + mMonitorFromRectProc = GetProcAddress ( lib, "MonitorFromRect" ); + mEnumDisplayMonitorsProc = GetProcAddress ( lib, "EnumDisplayMonitors" ); + if ( mGetMonitorInfoProc && mMonitorFromRectProc && mEnumDisplayMonitorsProc ) + mHasMultiMonitorAPIs = PR_TRUE; + } + printf("has multiple monitor apis is %ld\n", mHasMultiMonitorAPIs); + // nothing else to do. I guess we could cache a bunch of information // here, but we want to ask the device at runtime in case anything // has changed. @@ -36,7 +83,11 @@ nsScreenManagerWin :: nsScreenManagerWin ( ) nsScreenManagerWin :: ~nsScreenManagerWin() { - // nothing to see here. + // walk our list of cached screens and delete them. + for ( int i = 0; i < mScreenList.Count(); ++i ) { + ScreenListItem* item = NS_REINTERPRET_CAST(ScreenListItem*, mScreenList[i]); + delete item; + } } @@ -53,14 +104,26 @@ NS_IMPL_ISUPPORTS(nsScreenManagerWin, NS_GET_IID(nsIScreenManager)) // screen. This should change when a multi-monitor impl is done. // nsIScreen* -nsScreenManagerWin :: CreateNewScreenObject ( HDC inScreen ) +nsScreenManagerWin :: CreateNewScreenObject ( HDC inContext, void* inScreen ) { - nsIScreen* retval = nsnull; - if ( !mCachedMainScreen ) - mCachedMainScreen = new nsScreenWin ( inScreen ); - NS_IF_ADDREF(retval = mCachedMainScreen.get()); + nsIScreen* retScreen = nsnull; - return retval; + // look through our screen list, hoping to find it. If it's not there, + // add it and return the new one. + for ( int i = 0; i < mScreenList.Count(); ++i ) { + ScreenListItem* curr = NS_REINTERPRET_CAST(ScreenListItem*, mScreenList[i]); + if ( inScreen == curr->mMon ) { + NS_IF_ADDREF(retScreen = curr->mScreen.get()); + return retScreen; + } + } // for each screen. + + retScreen = new nsScreenWin(inContext, inScreen); + ScreenListItem* listItem = new ScreenListItem ( (HMONITOR)inScreen, retScreen ); + mScreenList.AppendElement ( listItem ); + + NS_IF_ADDREF(retScreen); + return retScreen; } @@ -78,23 +141,24 @@ nsScreenManagerWin :: ScreenForRect ( PRInt32 inLeft, PRInt32 inTop, PRInt32 inW { if ( !(inWidth || inHeight) ) { NS_WARNING ( "trying to find screen for sizeless window, using primary monitor" ); - *outScreen = CreateNewScreenObject ( ::GetDC(nsnull) ); // addrefs + *outScreen = CreateNewScreenObject ( ::GetDC(nsnull), nsnull ); // addrefs return NS_OK; } RECT globalWindowBounds = { inLeft, inTop, inLeft + inWidth, inTop + inHeight }; - // we want to use ::MonitorFromRect() but it doesn't exist under 95/NT. For now, just - // always return the primary monitor. + void* genScreen = nsnull; + if ( mHasMultiMonitorAPIs ) { + MonitorFromRectProc proc = (MonitorFromRectProc)mMonitorFromRectProc; + HMONITOR screen = (*proc)( &globalWindowBounds, MONITOR_DEFAULTTOPRIMARY ); +printf("*** found screen %x\n", screen); + genScreen = screen; + + //XXX find the DC for this screen?? + } + + *outScreen = CreateNewScreenObject ( ::GetDC(nsnull), genScreen ); // addrefs - *outScreen = CreateNewScreenObject ( ::GetDC(nsnull) ); // addrefs - -#if 0 - HMONITOR screen = ::MonitorFromRect ( &globalWindowBounds, MONITOR_DEFAULTTOPRIMARY ); - - - *outScreen = CreateNewScreenObject ( deviceWindowIsOn ); // addrefs -#endif return NS_OK; } // ScreenForRect @@ -109,12 +173,30 @@ nsScreenManagerWin :: ScreenForRect ( PRInt32 inLeft, PRInt32 inTop, PRInt32 inW NS_IMETHODIMP nsScreenManagerWin :: GetPrimaryScreen(nsIScreen** aPrimaryScreen) { - *aPrimaryScreen = CreateNewScreenObject ( ::GetDC(nsnull) ); // addrefs + *aPrimaryScreen = CreateNewScreenObject ( ::GetDC(nsnull), nsnull ); // addrefs return NS_OK; } // GetPrimaryScreen +// +// CountMonitors +// +// Will be called once for every monitor in the system. Just +// increments the parameter, which holds a ptr to a PRUin32 holding the +// count up to this point. +// +BOOL CALLBACK +CountMonitors ( HMONITOR, HDC, LPRECT, LPARAM ioParam ) +{ + PRUint32* countPtr = NS_REINTERPRET_CAST(PRUint32*, ioParam); + ++(*countPtr); + + return TRUE; // continue the enumeration + +} // CountMonitors + + // // GetNumberOfScreens // @@ -123,7 +205,21 @@ nsScreenManagerWin :: GetPrimaryScreen(nsIScreen** aPrimaryScreen) NS_IMETHODIMP nsScreenManagerWin :: GetNumberOfScreens(PRUint32 *aNumberOfScreens) { - *aNumberOfScreens = 1; + if ( mNumberOfScreens ) + *aNumberOfScreens = mNumberOfScreens; + else { + if ( mHasMultiMonitorAPIs ) { + // use a rect that spans a HUGE area to pick up all screens + RECT largeArea = { -32767, -32767, 32767, 32767 }; + PRUint32 count = 0; + EnumDisplayMonitorsProc proc = (EnumDisplayMonitorsProc)mEnumDisplayMonitorsProc; + BOOL result = (*proc)(nsnull, &largeArea, (MONITORENUMPROC)CountMonitors, (LPARAM)&count); + *aNumberOfScreens = mNumberOfScreens = count; + } // if there can be > 1 screen + else + *aNumberOfScreens = mNumberOfScreens = 1; + } + printf("****** number of sceens %ld\n", mNumberOfScreens); return NS_OK; } // GetNumberOfScreens diff --git a/widget/src/windows/nsScreenManagerWin.h b/widget/src/windows/nsScreenManagerWin.h index 43dbcbcbef2..4c37e903606 100644 --- a/widget/src/windows/nsScreenManagerWin.h +++ b/widget/src/windows/nsScreenManagerWin.h @@ -24,11 +24,14 @@ #define nsScreenManagerWin_h___ #include "nsIScreenManager.h" + #include #include "nsCOMPtr.h" +#include "nsVoidArray.h" class nsIScreen; + //------------------------------------------------------------------------ class nsScreenManagerWin : public nsIScreenManager @@ -42,10 +45,20 @@ public: private: - nsIScreen* CreateNewScreenObject ( HDC inScreen ) ; + nsIScreen* CreateNewScreenObject ( HDC inContext, void* inScreen ) ; + + PRBool mHasMultiMonitorAPIs; + PRUint32 mNumberOfScreens; + + // function pointers for multi-monitor API system calls that we use. Not + // valid unless |mHasMultiMonitorAPIs| is true + FARPROC mGetMonitorInfoProc; + FARPROC mMonitorFromRectProc; + FARPROC mEnumDisplayMonitorsProc; + + // cache the screens to avoid the memory allocations + nsAutoVoidArray mScreenList; - // cache the primary screen object to avoid memory allocation every time - nsCOMPtr mCachedMainScreen; }; #endif // nsScreenManagerWin_h___ diff --git a/widget/src/windows/nsScreenWin.cpp b/widget/src/windows/nsScreenWin.cpp index 4b6eeb52503..24c30d6f2de 100644 --- a/widget/src/windows/nsScreenWin.cpp +++ b/widget/src/windows/nsScreenWin.cpp @@ -20,17 +20,48 @@ * Contributor(s): */ +// +// We have to do this in order to have access to the multiple-monitor +// APIs that are only defined when WINVER is >= 0x0500. Don't worry, +// these won't actually be called unless they are present. +// +#undef WINVER +#define WINVER 0x0500 + #include "nsScreenWin.h" -nsScreenWin :: nsScreenWin ( HDC inScreen ) - : mScreen(inScreen) +// needed because there are unicode/ansi versions of this routine +// and we need to make sure we get the correct one. +#ifdef UNICODE +#define GetMonitorInfoQuoted "GetMonitorInfoW" +#else +#define GetMonitorInfoQuoted "GetMonitorInfoA" +#endif + + +typedef BOOL (*GetMonitorInfoProc)(HMONITOR inMon, LPMONITORINFO ioInfo); + + +nsScreenWin :: nsScreenWin ( HDC inContext, void* inScreen ) + : mContext(inContext), mScreen(inScreen), mHasMultiMonitorAPIs(PR_FALSE), + mGetMonitorInfoProc(nsnull) { NS_INIT_REFCNT(); - NS_ASSERTION ( inScreen, "Passing null device to nsScreenWin" ); - NS_ASSERTION ( ::GetDeviceCaps(inScreen, TECHNOLOGY) == DT_RASDISPLAY, "Not a display screen"); + NS_ASSERTION ( inContext, "Passing null device to nsScreenWin" ); + NS_ASSERTION ( ::GetDeviceCaps(inContext, TECHNOLOGY) == DT_RASDISPLAY, "Not a display screen"); + // figure out if we can call the multiple monitor APIs that are only + // available on Win98/2000. + HMODULE lib = GetModuleHandle("user32.dll"); + if ( lib ) { + mGetMonitorInfoProc = GetProcAddress ( lib, GetMonitorInfoQuoted ); + if ( mGetMonitorInfoProc ) + mHasMultiMonitorAPIs = PR_TRUE; + } + printf("has multiple monitor apis is %ld\n", mHasMultiMonitorAPIs); + // nothing else to do. I guess we could cache a bunch of information // here, but we want to ask the device at runtime in case anything // has changed. @@ -50,7 +81,16 @@ NS_IMPL_ISUPPORTS(nsScreenWin, NS_GET_IID(nsIScreen)) NS_IMETHODIMP nsScreenWin :: GetWidth(PRInt32 *aWidth) { - *aWidth = ::GetDeviceCaps(mScreen, HORZRES); + if ( mScreen && mHasMultiMonitorAPIs ) { + GetMonitorInfoProc proc = (GetMonitorInfoProc)mGetMonitorInfoProc; + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + BOOL success = (*proc)( (HMONITOR)mScreen, &info ); + if ( success ) + *aWidth = info.rcMonitor.right - info.rcMonitor.left; + } + else + *aWidth = ::GetDeviceCaps(mContext, HORZRES); return NS_OK; } // GetWidth @@ -59,7 +99,16 @@ nsScreenWin :: GetWidth(PRInt32 *aWidth) NS_IMETHODIMP nsScreenWin :: GetHeight(PRInt32 *aHeight) { - *aHeight = ::GetDeviceCaps(mScreen, VERTRES); + if ( mScreen && mHasMultiMonitorAPIs ) { + GetMonitorInfoProc proc = (GetMonitorInfoProc)mGetMonitorInfoProc; + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + BOOL success = (*proc)( (HMONITOR)mScreen, &info ); + if ( success ) + *aHeight = info.rcMonitor.bottom - info.rcMonitor.top; + } + else + *aHeight = ::GetDeviceCaps(mContext, VERTRES); return NS_OK; } // GetHeight @@ -68,7 +117,8 @@ nsScreenWin :: GetHeight(PRInt32 *aHeight) NS_IMETHODIMP nsScreenWin :: GetPixelDepth(PRInt32 *aPixelDepth) { - *aPixelDepth = ::GetDeviceCaps(mScreen, BITSPIXEL); + //XXX not sure how to get this info for multiple monitors, this might be ok... + *aPixelDepth = ::GetDeviceCaps(mContext, BITSPIXEL); return NS_OK; } // GetPixelDepth @@ -85,10 +135,19 @@ nsScreenWin :: GetColorDepth(PRInt32 *aColorDepth) NS_IMETHODIMP nsScreenWin :: GetAvailWidth(PRInt32 *aAvailWidth) { - // XXX Needs to be rewritten for a non-primary monitor? - RECT workArea; - ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); - *aAvailWidth = workArea.right - workArea.left; + if ( mScreen && mHasMultiMonitorAPIs ) { + GetMonitorInfoProc proc = (GetMonitorInfoProc)mGetMonitorInfoProc; + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + BOOL success = (*proc)( (HMONITOR)mScreen, &info ); + if ( success ) + *aAvailWidth = info.rcWork.right - info.rcWork.left; + } + else { + RECT workArea; + ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); + *aAvailWidth = workArea.right - workArea.left; + } return NS_OK; } // GetAvailWidth @@ -97,9 +156,19 @@ nsScreenWin :: GetAvailWidth(PRInt32 *aAvailWidth) NS_IMETHODIMP nsScreenWin :: GetAvailHeight(PRInt32 *aAvailHeight) { - RECT workArea; - ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); - *aAvailHeight = workArea.bottom - workArea.top; + if ( mScreen && mHasMultiMonitorAPIs ) { + GetMonitorInfoProc proc = (GetMonitorInfoProc)mGetMonitorInfoProc; + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + BOOL success = (*proc)( (HMONITOR)mScreen, &info ); + if ( success ) + *aAvailHeight = info.rcWork.bottom - info.rcWork.top; + } + else { + RECT workArea; + ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); + *aAvailHeight = workArea.bottom - workArea.top; + } return NS_OK; } // GetAvailHeight @@ -108,9 +177,19 @@ nsScreenWin :: GetAvailHeight(PRInt32 *aAvailHeight) NS_IMETHODIMP nsScreenWin :: GetAvailLeft(PRInt32 *aAvailLeft) { - RECT workArea; - ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); - *aAvailLeft = workArea.left; + if ( mScreen && mHasMultiMonitorAPIs ) { + GetMonitorInfoProc proc = (GetMonitorInfoProc)mGetMonitorInfoProc; + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + BOOL success = (*proc)( (HMONITOR)mScreen, &info ); + if ( success ) + *aAvailLeft = info.rcWork.left; + } + else { + RECT workArea; + ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); + *aAvailLeft = workArea.left; + } return NS_OK; } // GetAvailLeft @@ -119,10 +198,19 @@ nsScreenWin :: GetAvailLeft(PRInt32 *aAvailLeft) NS_IMETHODIMP nsScreenWin :: GetAvailTop(PRInt32 *aAvailTop) { - RECT workArea; - ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); - *aAvailTop = workArea.top; - + if ( mScreen && mHasMultiMonitorAPIs ) { + GetMonitorInfoProc proc = (GetMonitorInfoProc)mGetMonitorInfoProc; + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + BOOL success = (*proc)( (HMONITOR)mScreen, &info ); + if ( success ) + *aAvailTop = info.rcWork.top; + } + else { + RECT workArea; + ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0); + *aAvailTop = workArea.top; + } return NS_OK; } // GetAvailTop diff --git a/widget/src/windows/nsScreenWin.h b/widget/src/windows/nsScreenWin.h index 1d171892987..2f81ff119c1 100644 --- a/widget/src/windows/nsScreenWin.h +++ b/widget/src/windows/nsScreenWin.h @@ -31,7 +31,7 @@ class nsScreenWin : public nsIScreen { public: - nsScreenWin ( HDC inScreen ); + nsScreenWin ( HDC inContext, void* inScreen ); ~nsScreenWin(); NS_DECL_ISUPPORTS @@ -43,7 +43,13 @@ private: // asked. //PRBool IsPrimaryScreen ( ) const { return (mScreen == ::GetMainDevice()); } - HDC mScreen; // the dc that represents this screen + // function pointers for multi-monitor API system calls that we use. Not + // valid unless |mHasMultiMonitorAPIs| is true + PRBool mHasMultiMonitorAPIs; + FARPROC mGetMonitorInfoProc ; + + HDC mContext; // the dc that represents this screen + void* mScreen; // a |HMONITOR|, can't use this type in header file though. }; #endif // nsScreenWin_h___