/* -*- 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 sSingleton; ScreenManager& ScreenManager::GetSingleton() { if (!sSingleton) { sSingleton = new ScreenManager(); ClearOnShutdown(&sSingleton); } return *sSingleton; } already_AddRefed ScreenManager::GetAddRefedSingleton() { RefPtr sm = &GetSingleton(); return sm.forget(); } void ScreenManager::SetHelper(UniquePtr aHelper) { mHelper = std::move(aHelper); } void ScreenManager::Refresh(nsTArray>&& aScreens) { MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens")); mScreenList = std::move(aScreens); CopyScreensToAllRemotesIfIsParent(); } void ScreenManager::Refresh(nsTArray&& aScreens) { MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens from IPC")); mScreenList.Clear(); for (auto& screen : aScreens) { mScreenList.AppendElement(new Screen(screen)); } CopyScreensToAllRemotesIfIsParent(); } template void ScreenManager::CopyScreensToRemoteRange(Range aRemoteRange) { AutoTArray 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 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.Area(); 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 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 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 ret = new Screen(LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0, DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */); ret.forget(aPrimaryScreen); return NS_OK; } RefPtr ret = mScreenList[0]; ret.forget(aPrimaryScreen); return NS_OK; } } // namespace widget } // namespace mozilla