/src/mozilla-central/widget/ScreenManager.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "ScreenManager.h" |
8 | | |
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" |
14 | | |
15 | | static mozilla::LazyLogModule sScreenLog("WidgetScreen"); |
16 | | |
17 | | namespace mozilla { |
18 | | namespace widget { |
19 | | |
20 | | NS_IMPL_ISUPPORTS(ScreenManager, nsIScreenManager) |
21 | | |
22 | | ScreenManager::ScreenManager() |
23 | 0 | { |
24 | 0 | } |
25 | | |
26 | | ScreenManager::~ScreenManager() |
27 | 0 | { |
28 | 0 | } |
29 | | |
30 | | static StaticRefPtr<ScreenManager> sSingleton; |
31 | | |
32 | | ScreenManager& |
33 | | ScreenManager::GetSingleton() |
34 | 0 | { |
35 | 0 | if (!sSingleton) { |
36 | 0 | sSingleton = new ScreenManager(); |
37 | 0 | ClearOnShutdown(&sSingleton); |
38 | 0 | } |
39 | 0 | return *sSingleton; |
40 | 0 | } |
41 | | |
42 | | already_AddRefed<ScreenManager> |
43 | | ScreenManager::GetAddRefedSingleton() |
44 | 0 | { |
45 | 0 | RefPtr<ScreenManager> sm = &GetSingleton(); |
46 | 0 | return sm.forget(); |
47 | 0 | } |
48 | | |
49 | | void |
50 | | ScreenManager::SetHelper(UniquePtr<Helper> aHelper) |
51 | 0 | { |
52 | 0 | mHelper = std::move(aHelper); |
53 | 0 | } |
54 | | |
55 | | void |
56 | | ScreenManager::Refresh(nsTArray<RefPtr<Screen>>&& aScreens) |
57 | 0 | { |
58 | 0 | MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens")); |
59 | 0 |
|
60 | 0 | mScreenList = std::move(aScreens); |
61 | 0 |
|
62 | 0 | CopyScreensToAllRemotesIfIsParent(); |
63 | 0 | } |
64 | | |
65 | | void |
66 | | ScreenManager::Refresh(nsTArray<mozilla::dom::ScreenDetails>&& aScreens) |
67 | 0 | { |
68 | 0 | MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens from IPC")); |
69 | 0 |
|
70 | 0 | mScreenList.Clear(); |
71 | 0 | for (auto& screen : aScreens) { |
72 | 0 | mScreenList.AppendElement(new Screen(screen)); |
73 | 0 | } |
74 | 0 |
|
75 | 0 | CopyScreensToAllRemotesIfIsParent(); |
76 | 0 | } |
77 | | |
78 | | template<class Range> |
79 | | void |
80 | | ScreenManager::CopyScreensToRemoteRange(Range aRemoteRange) |
81 | 0 | { |
82 | 0 | AutoTArray<dom::ScreenDetails, 4> screens; |
83 | 0 | for (auto& screen : mScreenList) { |
84 | 0 | screens.AppendElement(screen->ToScreenDetails()); |
85 | 0 | } |
86 | 0 | for (auto cp : aRemoteRange) { |
87 | 0 | MOZ_LOG(sScreenLog, LogLevel::Debug, ("Send screens to [Pid %d]", cp->Pid())); |
88 | 0 | if (!cp->SendRefreshScreens(screens)) { |
89 | 0 | MOZ_LOG(sScreenLog, LogLevel::Error, |
90 | 0 | ("SendRefreshScreens to [Pid %d] failed", cp->Pid())); |
91 | 0 | } |
92 | 0 | } |
93 | 0 | } Unexecuted instantiation: void mozilla::widget::ScreenManager::CopyScreensToRemoteRange<std::initializer_list<mozilla::dom::ContentParent*> >(std::initializer_list<mozilla::dom::ContentParent*>) Unexecuted instantiation: void mozilla::widget::ScreenManager::CopyScreensToRemoteRange<mozilla::dom::ContentParent::ContentParentIterator>(mozilla::dom::ContentParent::ContentParentIterator) |
94 | | |
95 | | void |
96 | | ScreenManager::CopyScreensToRemote(dom::ContentParent* aContentParent) |
97 | 0 | { |
98 | 0 | MOZ_ASSERT(aContentParent); |
99 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
100 | 0 |
|
101 | 0 | auto range = { aContentParent }; |
102 | 0 | CopyScreensToRemoteRange(range); |
103 | 0 | } |
104 | | |
105 | | void |
106 | | ScreenManager::CopyScreensToAllRemotesIfIsParent() |
107 | 0 | { |
108 | 0 | if (XRE_IsContentProcess()) { |
109 | 0 | return; |
110 | 0 | } |
111 | 0 | |
112 | 0 | MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing all ContentParents")); |
113 | 0 |
|
114 | 0 | CopyScreensToRemoteRange(dom::ContentParent::AllProcesses(dom::ContentParent::eLive)); |
115 | 0 | } |
116 | | |
117 | | // Returns the screen that contains the rectangle. If the rect overlaps |
118 | | // multiple screens, it picks the screen with the greatest area of intersection. |
119 | | // |
120 | | // The coordinates are in desktop pixels. |
121 | | // |
122 | | NS_IMETHODIMP |
123 | | ScreenManager::ScreenForRect(int32_t aX, int32_t aY, |
124 | | int32_t aWidth, int32_t aHeight, |
125 | | nsIScreen** aOutScreen) |
126 | 0 | { |
127 | 0 | if (mScreenList.IsEmpty()) { |
128 | 0 | MOZ_LOG(sScreenLog, LogLevel::Warning, |
129 | 0 | ("No screen available. This can happen in xpcshell.")); |
130 | 0 | RefPtr<Screen> ret = new Screen(LayoutDeviceIntRect(), LayoutDeviceIntRect(), |
131 | 0 | 0, 0, |
132 | 0 | DesktopToLayoutDeviceScale(), |
133 | 0 | CSSToLayoutDeviceScale(), |
134 | 0 | 96 /* dpi */); |
135 | 0 | ret.forget(aOutScreen); |
136 | 0 | return NS_OK; |
137 | 0 | } |
138 | 0 |
|
139 | 0 | // Optimize for the common case. If the number of screens is only |
140 | 0 | // one then just return the primary screen. |
141 | 0 | if (mScreenList.Length() == 1) { |
142 | 0 | return GetPrimaryScreen(aOutScreen); |
143 | 0 | } |
144 | 0 | |
145 | 0 | // which screen should we return? |
146 | 0 | Screen* which = mScreenList[0].get(); |
147 | 0 |
|
148 | 0 | // walk the list of screens and find the one that has the most |
149 | 0 | // surface area. |
150 | 0 | uint32_t area = 0; |
151 | 0 | DesktopIntRect windowRect(aX, aY, aWidth, aHeight); |
152 | 0 | for (auto& screen : mScreenList) { |
153 | 0 | int32_t x, y, width, height; |
154 | 0 | x = y = width = height = 0; |
155 | 0 | screen->GetRectDisplayPix(&x, &y, &width, &height); |
156 | 0 | // calculate the surface area |
157 | 0 | DesktopIntRect screenRect(x, y, width, height); |
158 | 0 | screenRect.IntersectRect(screenRect, windowRect); |
159 | 0 | uint32_t tempArea = screenRect.Area(); |
160 | 0 | if (tempArea > area) { |
161 | 0 | which = screen.get(); |
162 | 0 | area = tempArea; |
163 | 0 | } |
164 | 0 | } |
165 | 0 |
|
166 | 0 | // If the rect intersects one or more screen, |
167 | 0 | // return the screen that has the largest intersection. |
168 | 0 | if (area > 0) { |
169 | 0 | RefPtr<Screen> ret = which; |
170 | 0 | ret.forget(aOutScreen); |
171 | 0 | return NS_OK; |
172 | 0 | } |
173 | 0 | |
174 | 0 | // If the rect does not intersect a screen, find |
175 | 0 | // a screen that is nearest to the rect. |
176 | 0 | uint32_t distance = UINT32_MAX; |
177 | 0 | for (auto& screen : mScreenList) { |
178 | 0 | int32_t x, y, width, height; |
179 | 0 | x = y = width = height = 0; |
180 | 0 | screen->GetRectDisplayPix(&x, &y, &width, &height); |
181 | 0 |
|
182 | 0 | uint32_t distanceX = 0; |
183 | 0 | if (aX > (x + width)) { |
184 | 0 | distanceX = aX - (x + width); |
185 | 0 | } else if ((aX + aWidth) < x) { |
186 | 0 | distanceX = x - (aX + aWidth); |
187 | 0 | } |
188 | 0 |
|
189 | 0 | uint32_t distanceY = 0; |
190 | 0 | if (aY > (y + height)) { |
191 | 0 | distanceY = aY - (y + height); |
192 | 0 | } else if ((aY + aHeight) < y) { |
193 | 0 | distanceY = y - (aY + aHeight); |
194 | 0 | } |
195 | 0 |
|
196 | 0 | uint32_t tempDistance = distanceX * distanceX + distanceY * distanceY; |
197 | 0 | if (tempDistance < distance) { |
198 | 0 | which = screen.get(); |
199 | 0 | distance = tempDistance; |
200 | 0 | if (distance == 0) { |
201 | 0 | break; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | } |
205 | 0 |
|
206 | 0 | RefPtr<Screen> ret = which; |
207 | 0 | ret.forget(aOutScreen); |
208 | 0 | return NS_OK; |
209 | 0 | } |
210 | | |
211 | | // The screen with the menubar/taskbar. This shouldn't be needed very |
212 | | // often. |
213 | | // |
214 | | NS_IMETHODIMP |
215 | | ScreenManager::GetPrimaryScreen(nsIScreen** aPrimaryScreen) |
216 | 0 | { |
217 | 0 | if (mScreenList.IsEmpty()) { |
218 | 0 | MOZ_LOG(sScreenLog, LogLevel::Warning, |
219 | 0 | ("No screen available. This can happen in xpcshell.")); |
220 | 0 | RefPtr<Screen> ret = new Screen(LayoutDeviceIntRect(), LayoutDeviceIntRect(), |
221 | 0 | 0, 0, |
222 | 0 | DesktopToLayoutDeviceScale(), |
223 | 0 | CSSToLayoutDeviceScale(), |
224 | 0 | 96 /* dpi */); |
225 | 0 | ret.forget(aPrimaryScreen); |
226 | 0 | return NS_OK; |
227 | 0 | } |
228 | 0 |
|
229 | 0 | RefPtr<Screen> ret = mScreenList[0]; |
230 | 0 | ret.forget(aPrimaryScreen); |
231 | 0 | return NS_OK; |
232 | 0 | } |
233 | | |
234 | | } // namespace widget |
235 | | } // namespace mozilla |