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