Bug 1874684 - Part 20: Tag stack classes with MOZ_STACK_CLASS. r=allstarschh
[gecko.git] / widget / ScreenManager.cpp
blob84d7316af7e54149c83cf1566cae9b6a909be2b5
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"
14 #ifdef MOZ_WAYLAND
15 # include "mozilla/WidgetUtilsGtk.h"
16 #endif /* MOZ_WAYLAND */
18 static mozilla::LazyLogModule sScreenLog("WidgetScreen");
20 namespace mozilla::widget {
22 NS_IMPL_ISUPPORTS(ScreenManager, nsIScreenManager)
24 ScreenManager::ScreenManager() = default;
26 ScreenManager::~ScreenManager() = default;
28 static StaticRefPtr<ScreenManager> sSingleton;
30 ScreenManager& ScreenManager::GetSingleton() {
31 if (!sSingleton) {
32 sSingleton = new ScreenManager();
33 ClearOnShutdown(&sSingleton);
35 return *sSingleton;
38 already_AddRefed<ScreenManager> ScreenManager::GetAddRefedSingleton() {
39 RefPtr<ScreenManager> sm = &GetSingleton();
40 return sm.forget();
43 void ScreenManager::SetHelper(UniquePtr<Helper> aHelper) {
44 mHelper = std::move(aHelper);
47 // static
48 void ScreenManager::Refresh(nsTArray<RefPtr<Screen>>&& aScreens) {
49 if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
50 // We don't refresh screen data if starting XPCOM shutdown path.
51 // GetSingleton returns invalid data since it is freed.
52 return;
54 MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens"));
55 GetSingleton().RefreshInternal(std::move(aScreens));
58 void ScreenManager::Refresh(nsTArray<mozilla::dom::ScreenDetails>&& aScreens) {
59 MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens from IPC"));
61 AutoTArray<RefPtr<Screen>, 4> screens;
62 for (auto& screen : aScreens) {
63 screens.AppendElement(new Screen(screen));
65 RefreshInternal(std::move(screens));
68 void ScreenManager::RefreshInternal(nsTArray<RefPtr<Screen>>&& aScreens) {
69 mScreenList = std::move(aScreens);
71 CopyScreensToAllRemotesIfIsParent();
72 if (nsCOMPtr<nsIObserverService> s = services::GetObserverService()) {
73 s->NotifyObservers(nullptr, "screen-information-changed", nullptr);
77 template <class Range>
78 void ScreenManager::CopyScreensToRemoteRange(Range aRemoteRange) {
79 AutoTArray<dom::ScreenDetails, 4> screens;
80 for (auto& screen : mScreenList) {
81 screens.AppendElement(screen->ToScreenDetails());
83 for (auto cp : aRemoteRange) {
84 MOZ_LOG(sScreenLog, LogLevel::Debug,
85 ("Send screens to [Pid %d]", cp->Pid()));
86 if (!cp->SendRefreshScreens(screens)) {
87 MOZ_LOG(sScreenLog, LogLevel::Error,
88 ("SendRefreshScreens to [Pid %d] failed", cp->Pid()));
93 void ScreenManager::CopyScreensToRemote(dom::ContentParent* aContentParent) {
94 MOZ_ASSERT(aContentParent);
95 MOZ_ASSERT(XRE_IsParentProcess());
97 auto range = {aContentParent};
98 CopyScreensToRemoteRange(range);
101 void ScreenManager::CopyScreensToAllRemotesIfIsParent() {
102 if (XRE_IsContentProcess()) {
103 return;
106 MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing all ContentParents"));
108 CopyScreensToRemoteRange(
109 dom::ContentParent::AllProcesses(dom::ContentParent::eLive));
112 // Returns the screen that contains the rectangle. If the rect overlaps
113 // multiple screens, it picks the screen with the greatest area of intersection.
115 // The coordinates are in desktop pixels.
117 NS_IMETHODIMP
118 ScreenManager::ScreenForRect(int32_t aX, int32_t aY, int32_t aWidth,
119 int32_t aHeight, nsIScreen** aOutScreen) {
120 DesktopIntRect rect(aX, aY, aWidth, aHeight);
121 nsCOMPtr<nsIScreen> screen = ScreenForRect(rect);
122 screen.forget(aOutScreen);
123 return NS_OK;
126 already_AddRefed<Screen> ScreenManager::ScreenForRect(
127 const DesktopIntRect& aRect) {
128 #if defined(MOZ_WAYLAND) && defined(MOZ_LOGGING)
129 static bool inWayland = GdkIsWaylandDisplay();
130 if (inWayland) {
131 MOZ_LOG(sScreenLog, LogLevel::Warning,
132 ("Getting screen in wayland, primary display will be returned."));
134 #endif
136 if (mScreenList.IsEmpty()) {
137 MOZ_LOG(sScreenLog, LogLevel::Warning,
138 ("No screen available. This can happen in xpcshell."));
139 auto screen = MakeRefPtr<Screen>(
140 LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0, 0,
141 DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */,
142 Screen::IsPseudoDisplay::No, Screen::IsHDR::No,
143 hal::ScreenOrientation::None, 0);
144 return screen.forget();
147 // Optimize for the common case. If the number of screens is only
148 // one then just return the primary screen.
149 if (mScreenList.Length() == 1) {
150 return GetPrimaryScreen();
153 // which screen should we return?
154 Screen* which = mScreenList[0].get();
156 // walk the list of screens and find the one that has the most
157 // surface area.
158 uint32_t area = 0;
159 for (auto& screen : mScreenList) {
160 int32_t x, y, width, height;
161 x = y = width = height = 0;
162 screen->GetRectDisplayPix(&x, &y, &width, &height);
163 // calculate the surface area
164 DesktopIntRect screenRect(x, y, width, height);
165 screenRect.IntersectRect(screenRect, aRect);
166 uint32_t tempArea = screenRect.Area();
167 if (tempArea > area) {
168 which = screen.get();
169 area = tempArea;
173 // If the rect intersects one or more screen,
174 // return the screen that has the largest intersection.
175 if (area > 0) {
176 RefPtr<Screen> ret = which;
177 return ret.forget();
180 // If the rect does not intersect a screen, find
181 // a screen that is nearest to the rect.
182 uint32_t distance = UINT32_MAX;
183 for (auto& screen : mScreenList) {
184 int32_t x, y, width, height;
185 x = y = width = height = 0;
186 screen->GetRectDisplayPix(&x, &y, &width, &height);
188 uint32_t distanceX = 0;
189 if (aRect.x > (x + width)) {
190 distanceX = aRect.x - (x + width);
191 } else if (aRect.XMost() < x) {
192 distanceX = x - aRect.XMost();
195 uint32_t distanceY = 0;
196 if (aRect.y > (y + height)) {
197 distanceY = aRect.y - (y + height);
198 } else if (aRect.YMost() < y) {
199 distanceY = y - aRect.YMost();
202 uint32_t tempDistance = distanceX * distanceX + distanceY * distanceY;
203 if (tempDistance < distance) {
204 which = screen.get();
205 distance = tempDistance;
206 if (distance == 0) {
207 break;
212 RefPtr<Screen> ret = which;
213 return ret.forget();
216 // The screen with the menubar/taskbar. This shouldn't be needed very
217 // often.
219 already_AddRefed<Screen> ScreenManager::GetPrimaryScreen() {
220 if (mScreenList.IsEmpty()) {
221 MOZ_LOG(sScreenLog, LogLevel::Warning,
222 ("No screen available. This can happen in xpcshell."));
223 return MakeAndAddRef<Screen>(LayoutDeviceIntRect(), LayoutDeviceIntRect(),
224 0, 0, 0, DesktopToLayoutDeviceScale(),
225 CSSToLayoutDeviceScale(), 96 /* dpi */,
226 Screen::IsPseudoDisplay::No, Screen::IsHDR::No,
227 hal::ScreenOrientation::None, 0);
230 return do_AddRef(mScreenList[0]);
233 NS_IMETHODIMP
234 ScreenManager::GetPrimaryScreen(nsIScreen** aPrimaryScreen) {
235 nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
236 screen.forget(aPrimaryScreen);
237 return NS_OK;
240 NS_IMETHODIMP
241 ScreenManager::GetTotalScreenPixels(int64_t* aTotalScreenPixels) {
242 MOZ_ASSERT(aTotalScreenPixels);
244 if (mScreenList.IsEmpty()) {
245 MOZ_LOG(sScreenLog, LogLevel::Warning,
246 ("No screen available. This can happen in xpcshell."));
247 *aTotalScreenPixels = 0;
248 return NS_OK;
251 int64_t pixels = 0;
252 for (auto& screen : mScreenList) {
253 int32_t x, y, width, height;
254 x = y = width = height = 0;
255 screen->GetRect(&x, &y, &width, &height);
256 pixels += width * height;
259 *aTotalScreenPixels = pixels;
260 return NS_OK;
263 } // namespace mozilla::widget