forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			235 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "ScreenManager.h"
 | |
| 
 | |
| #include "mozilla/ClearOnShutdown.h"
 | |
| #include "mozilla/dom/ContentParent.h"
 | |
| #include "mozilla/dom/DOMTypes.h"
 | |
| #include "mozilla/Logging.h"
 | |
| #include "mozilla/StaticPtr.h"
 | |
| 
 | |
| static mozilla::LazyLogModule sScreenLog("WidgetScreen");
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace widget {
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(ScreenManager, nsIScreenManager)
 | |
| 
 | |
| ScreenManager::ScreenManager()
 | |
| {
 | |
| }
 | |
| 
 | |
| ScreenManager::~ScreenManager()
 | |
| {
 | |
| }
 | |
| 
 | |
| static StaticRefPtr<ScreenManager> sSingleton;
 | |
| 
 | |
| ScreenManager&
 | |
| ScreenManager::GetSingleton()
 | |
| {
 | |
|   if (!sSingleton) {
 | |
|     sSingleton = new ScreenManager();
 | |
|     ClearOnShutdown(&sSingleton);
 | |
|   }
 | |
|   return *sSingleton;
 | |
| }
 | |
| 
 | |
| already_AddRefed<ScreenManager>
 | |
| ScreenManager::GetAddRefedSingleton()
 | |
| {
 | |
|   RefPtr<ScreenManager> sm = &GetSingleton();
 | |
|   return sm.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| ScreenManager::SetHelper(UniquePtr<Helper> aHelper)
 | |
| {
 | |
|   mHelper = Move(aHelper);
 | |
| }
 | |
| 
 | |
| void
 | |
| ScreenManager::Refresh(nsTArray<RefPtr<Screen>>&& aScreens)
 | |
| {
 | |
|   MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens"));
 | |
| 
 | |
|   mScreenList = Move(aScreens);
 | |
| 
 | |
|   CopyScreensToAllRemotesIfIsParent();
 | |
| }
 | |
| 
 | |
| void
 | |
| ScreenManager::Refresh(nsTArray<mozilla::dom::ScreenDetails>&& aScreens)
 | |
| {
 | |
|   MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens from IPC"));
 | |
| 
 | |
|   mScreenList.Clear();
 | |
|   for (auto& screen : aScreens) {
 | |
|     mScreenList.AppendElement(new Screen(screen));
 | |
|   }
 | |
| 
 | |
|   CopyScreensToAllRemotesIfIsParent();
 | |
| }
 | |
| 
 | |
| template<class Range>
 | |
| void
 | |
| ScreenManager::CopyScreensToRemoteRange(Range aRemoteRange)
 | |
| {
 | |
|   AutoTArray<dom::ScreenDetails, 4> screens;
 | |
|   for (auto& screen : mScreenList) {
 | |
|     screens.AppendElement(screen->ToScreenDetails());
 | |
|   }
 | |
|   for (auto cp : aRemoteRange) {
 | |
|     MOZ_LOG(sScreenLog, LogLevel::Debug, ("Send screens to [Pid %d]", cp->Pid()));
 | |
|     if (!cp->SendRefreshScreens(screens)) {
 | |
|       MOZ_LOG(sScreenLog, LogLevel::Error,
 | |
|               ("SendRefreshScreens to [Pid %d] failed", cp->Pid()));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| ScreenManager::CopyScreensToRemote(dom::ContentParent* aContentParent)
 | |
| {
 | |
|   MOZ_ASSERT(aContentParent);
 | |
|   MOZ_ASSERT(XRE_IsParentProcess());
 | |
| 
 | |
|   auto range = { aContentParent };
 | |
|   CopyScreensToRemoteRange(range);
 | |
| }
 | |
| 
 | |
| void
 | |
| ScreenManager::CopyScreensToAllRemotesIfIsParent()
 | |
| {
 | |
|   if (XRE_IsContentProcess()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing all ContentParents"));
 | |
| 
 | |
|   CopyScreensToRemoteRange(dom::ContentParent::AllProcesses(dom::ContentParent::eLive));
 | |
| }
 | |
| 
 | |
| // Returns the screen that contains the rectangle. If the rect overlaps
 | |
| // multiple screens, it picks the screen with the greatest area of intersection.
 | |
| //
 | |
| // The coordinates are in desktop pixels.
 | |
| //
 | |
| NS_IMETHODIMP
 | |
| ScreenManager::ScreenForRect(int32_t aX, int32_t aY,
 | |
|                              int32_t aWidth, int32_t aHeight,
 | |
|                              nsIScreen** aOutScreen)
 | |
| {
 | |
|   if (mScreenList.IsEmpty()) {
 | |
|     MOZ_LOG(sScreenLog, LogLevel::Warning,
 | |
|             ("No screen available. This can happen in xpcshell."));
 | |
|     RefPtr<Screen> ret = new Screen(LayoutDeviceIntRect(), LayoutDeviceIntRect(),
 | |
|                                     0, 0,
 | |
|                                     DesktopToLayoutDeviceScale(),
 | |
|                                     CSSToLayoutDeviceScale(),
 | |
|                                     96 /* dpi */);
 | |
|     ret.forget(aOutScreen);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // Optimize for the common case. If the number of screens is only
 | |
|   // one then just return the primary screen.
 | |
|   if (mScreenList.Length() == 1) {
 | |
|     return GetPrimaryScreen(aOutScreen);
 | |
|   }
 | |
| 
 | |
|   // which screen should we return?
 | |
|   Screen* which = mScreenList[0].get();
 | |
| 
 | |
|   // walk the list of screens and find the one that has the most
 | |
|   // surface area.
 | |
|   uint32_t area = 0;
 | |
|   DesktopIntRect windowRect(aX, aY, aWidth, aHeight);
 | |
|   for (auto& screen : mScreenList) {
 | |
|     int32_t  x, y, width, height;
 | |
|     x = y = width = height = 0;
 | |
|     screen->GetRectDisplayPix(&x, &y, &width, &height);
 | |
|     // calculate the surface area
 | |
|     DesktopIntRect screenRect(x, y, width, height);
 | |
|     screenRect.IntersectRect(screenRect, windowRect);
 | |
|     uint32_t tempArea = screenRect.width * screenRect.height;
 | |
|     if (tempArea > area) {
 | |
|       which = screen.get();
 | |
|       area = tempArea;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If the rect intersects one or more screen,
 | |
|   // return the screen that has the largest intersection.
 | |
|   if (area > 0) {
 | |
|     RefPtr<Screen> ret = which;
 | |
|     ret.forget(aOutScreen);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // If the rect does not intersect a screen, find
 | |
|   // a screen that is nearest to the rect.
 | |
|   uint32_t distance = UINT32_MAX;
 | |
|   for (auto& screen : mScreenList) {
 | |
|     int32_t  x, y, width, height;
 | |
|     x = y = width = height = 0;
 | |
|     screen->GetRectDisplayPix(&x, &y, &width, &height);
 | |
| 
 | |
|     uint32_t distanceX = 0;
 | |
|     if (aX > (x + width)) {
 | |
|       distanceX = aX - (x + width);
 | |
|     } else if ((aX + aWidth) < x) {
 | |
|       distanceX = x - (aX + aWidth);
 | |
|     }
 | |
| 
 | |
|     uint32_t distanceY = 0;
 | |
|     if (aY > (y + height)) {
 | |
|       distanceY = aY - (y + height);
 | |
|     } else if ((aY + aHeight) < y) {
 | |
|       distanceY = y - (aY + aHeight);
 | |
|     }
 | |
| 
 | |
|     uint32_t tempDistance = distanceX * distanceX + distanceY * distanceY;
 | |
|     if (tempDistance < distance) {
 | |
|       which = screen.get();
 | |
|       distance = tempDistance;
 | |
|       if (distance == 0) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   RefPtr<Screen> ret = which;
 | |
|   ret.forget(aOutScreen);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // The screen with the menubar/taskbar. This shouldn't be needed very
 | |
| // often.
 | |
| //
 | |
| NS_IMETHODIMP
 | |
| ScreenManager::GetPrimaryScreen(nsIScreen** aPrimaryScreen)
 | |
| {
 | |
|   if (mScreenList.IsEmpty()) {
 | |
|     MOZ_LOG(sScreenLog, LogLevel::Warning,
 | |
|             ("No screen available. This can happen in xpcshell."));
 | |
|     RefPtr<Screen> ret = new Screen(LayoutDeviceIntRect(), LayoutDeviceIntRect(),
 | |
|                                     0, 0,
 | |
|                                     DesktopToLayoutDeviceScale(),
 | |
|                                     CSSToLayoutDeviceScale(),
 | |
|                                     96 /* dpi */);
 | |
|     ret.forget(aPrimaryScreen);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   RefPtr<Screen> ret = mScreenList[0];
 | |
|   ret.forget(aPrimaryScreen);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| } // namespace widget
 | |
| } // namespace mozilla
 | 
