1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ScreenManager.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "mozilla/dom/ContentParent.h"
11 #include "mozilla/dom/DOMTypes.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/StaticPtr.h"
15 static mozilla::LazyLogModule
sScreenLog("WidgetScreen");
20 NS_IMPL_ISUPPORTS(ScreenManager
, nsIScreenManager
)
22 ScreenManager::ScreenManager() {}
24 ScreenManager::~ScreenManager() {}
26 static StaticRefPtr
<ScreenManager
> sSingleton
;
28 ScreenManager
& ScreenManager::GetSingleton() {
30 sSingleton
= new ScreenManager();
31 ClearOnShutdown(&sSingleton
);
36 already_AddRefed
<ScreenManager
> ScreenManager::GetAddRefedSingleton() {
37 RefPtr
<ScreenManager
> sm
= &GetSingleton();
41 void ScreenManager::SetHelper(UniquePtr
<Helper
> aHelper
) {
42 mHelper
= std::move(aHelper
);
45 void ScreenManager::Refresh(nsTArray
<RefPtr
<Screen
>>&& aScreens
) {
46 MOZ_LOG(sScreenLog
, LogLevel::Debug
, ("Refresh screens"));
48 mScreenList
= std::move(aScreens
);
50 CopyScreensToAllRemotesIfIsParent();
53 void ScreenManager::Refresh(nsTArray
<mozilla::dom::ScreenDetails
>&& aScreens
) {
54 MOZ_LOG(sScreenLog
, LogLevel::Debug
, ("Refresh screens from IPC"));
57 for (auto& screen
: aScreens
) {
58 mScreenList
.AppendElement(new Screen(screen
));
61 CopyScreensToAllRemotesIfIsParent();
64 template <class Range
>
65 void ScreenManager::CopyScreensToRemoteRange(Range aRemoteRange
) {
66 AutoTArray
<dom::ScreenDetails
, 4> screens
;
67 for (auto& screen
: mScreenList
) {
68 screens
.AppendElement(screen
->ToScreenDetails());
70 for (auto cp
: aRemoteRange
) {
71 MOZ_LOG(sScreenLog
, LogLevel::Debug
,
72 ("Send screens to [Pid %d]", cp
->Pid()));
73 if (!cp
->SendRefreshScreens(screens
)) {
74 MOZ_LOG(sScreenLog
, LogLevel::Error
,
75 ("SendRefreshScreens to [Pid %d] failed", cp
->Pid()));
80 void ScreenManager::CopyScreensToRemote(dom::ContentParent
* aContentParent
) {
81 MOZ_ASSERT(aContentParent
);
82 MOZ_ASSERT(XRE_IsParentProcess());
84 auto range
= {aContentParent
};
85 CopyScreensToRemoteRange(range
);
88 void ScreenManager::CopyScreensToAllRemotesIfIsParent() {
89 if (XRE_IsContentProcess()) {
93 MOZ_LOG(sScreenLog
, LogLevel::Debug
, ("Refreshing all ContentParents"));
95 CopyScreensToRemoteRange(
96 dom::ContentParent::AllProcesses(dom::ContentParent::eLive
));
99 // Returns the screen that contains the rectangle. If the rect overlaps
100 // multiple screens, it picks the screen with the greatest area of intersection.
102 // The coordinates are in desktop pixels.
105 ScreenManager::ScreenForRect(int32_t aX
, int32_t aY
, int32_t aWidth
,
106 int32_t aHeight
, nsIScreen
** aOutScreen
) {
107 if (mScreenList
.IsEmpty()) {
108 MOZ_LOG(sScreenLog
, LogLevel::Warning
,
109 ("No screen available. This can happen in xpcshell."));
110 RefPtr
<Screen
> ret
= new Screen(
111 LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0,
112 DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */);
113 ret
.forget(aOutScreen
);
117 // Optimize for the common case. If the number of screens is only
118 // one then just return the primary screen.
119 if (mScreenList
.Length() == 1) {
120 return GetPrimaryScreen(aOutScreen
);
123 // which screen should we return?
124 Screen
* which
= mScreenList
[0].get();
126 // walk the list of screens and find the one that has the most
129 DesktopIntRect
windowRect(aX
, aY
, aWidth
, aHeight
);
130 for (auto& screen
: mScreenList
) {
131 int32_t x
, y
, width
, height
;
132 x
= y
= width
= height
= 0;
133 screen
->GetRectDisplayPix(&x
, &y
, &width
, &height
);
134 // calculate the surface area
135 DesktopIntRect
screenRect(x
, y
, width
, height
);
136 screenRect
.IntersectRect(screenRect
, windowRect
);
137 uint32_t tempArea
= screenRect
.Area();
138 if (tempArea
> area
) {
139 which
= screen
.get();
144 // If the rect intersects one or more screen,
145 // return the screen that has the largest intersection.
147 RefPtr
<Screen
> ret
= which
;
148 ret
.forget(aOutScreen
);
152 // If the rect does not intersect a screen, find
153 // a screen that is nearest to the rect.
154 uint32_t distance
= UINT32_MAX
;
155 for (auto& screen
: mScreenList
) {
156 int32_t x
, y
, width
, height
;
157 x
= y
= width
= height
= 0;
158 screen
->GetRectDisplayPix(&x
, &y
, &width
, &height
);
160 uint32_t distanceX
= 0;
161 if (aX
> (x
+ width
)) {
162 distanceX
= aX
- (x
+ width
);
163 } else if ((aX
+ aWidth
) < x
) {
164 distanceX
= x
- (aX
+ aWidth
);
167 uint32_t distanceY
= 0;
168 if (aY
> (y
+ height
)) {
169 distanceY
= aY
- (y
+ height
);
170 } else if ((aY
+ aHeight
) < y
) {
171 distanceY
= y
- (aY
+ aHeight
);
174 uint32_t tempDistance
= distanceX
* distanceX
+ distanceY
* distanceY
;
175 if (tempDistance
< distance
) {
176 which
= screen
.get();
177 distance
= tempDistance
;
184 RefPtr
<Screen
> ret
= which
;
185 ret
.forget(aOutScreen
);
189 // The screen with the menubar/taskbar. This shouldn't be needed very
193 ScreenManager::GetPrimaryScreen(nsIScreen
** aPrimaryScreen
) {
194 if (mScreenList
.IsEmpty()) {
195 MOZ_LOG(sScreenLog
, LogLevel::Warning
,
196 ("No screen available. This can happen in xpcshell."));
197 RefPtr
<Screen
> ret
= new Screen(
198 LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0,
199 DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */);
200 ret
.forget(aPrimaryScreen
);
204 RefPtr
<Screen
> ret
= mScreenList
[0];
205 ret
.forget(aPrimaryScreen
);
209 } // namespace widget
210 } // namespace mozilla