no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / widget / cocoa / ScreenHelperCocoa.mm
blob2f1408e34553a7ed404e6a5a3cfe51a72806b030
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 "ScreenHelperCocoa.h"
9 #import <Cocoa/Cocoa.h>
11 #include "mozilla/Logging.h"
12 #include "nsCocoaFeatures.h"
13 #include "nsCocoaUtils.h"
14 #include "nsObjCExceptions.h"
16 using namespace mozilla;
18 static LazyLogModule sScreenLog("WidgetScreen");
20 @interface ScreenHelperDelegate : NSObject {
21  @private
22   mozilla::widget::ScreenHelperCocoa* mHelper;
25 - (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper;
26 - (void)didChangeScreenParameters:(NSNotification*)aNotification;
27 @end
29 @implementation ScreenHelperDelegate
30 - (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper {
31   if ((self = [self init])) {
32     mHelper = aScreenHelper;
34     [[NSNotificationCenter defaultCenter]
35         addObserver:self
36            selector:@selector(didChangeScreenParameters:)
37                name:NSApplicationDidChangeScreenParametersNotification
38              object:nil];
39   }
41   return self;
44 - (void)dealloc {
45   [[NSNotificationCenter defaultCenter] removeObserver:self];
46   [super dealloc];
49 - (void)didChangeScreenParameters:(NSNotification*)aNotification {
50   MOZ_LOG(sScreenLog, LogLevel::Debug,
51           ("Received NSApplicationDidChangeScreenParametersNotification"));
53   mHelper->RefreshScreens();
55 @end
57 namespace mozilla {
58 namespace widget {
60 ScreenHelperCocoa::ScreenHelperCocoa() {
61   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
63   MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperCocoa created"));
65   mDelegate = [[ScreenHelperDelegate alloc] initWithScreenHelper:this];
67   RefreshScreens();
69   NS_OBJC_END_TRY_IGNORE_BLOCK;
72 ScreenHelperCocoa::~ScreenHelperCocoa() {
73   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
75   [mDelegate release];
77   NS_OBJC_END_TRY_IGNORE_BLOCK;
80 static already_AddRefed<Screen> MakeScreen(NSScreen* aScreen) {
81   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
83   DesktopToLayoutDeviceScale contentsScaleFactor(
84       nsCocoaUtils::GetBackingScaleFactor(aScreen));
85   CSSToLayoutDeviceScale defaultCssScaleFactor(contentsScaleFactor.scale);
86   NSRect frame = [aScreen frame];
87   LayoutDeviceIntRect rect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(
88       frame, contentsScaleFactor.scale);
89   frame = [aScreen visibleFrame];
90   LayoutDeviceIntRect availRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(
91       frame, contentsScaleFactor.scale);
93   // aScreen may be capable of displaying multiple pixel depths, for example by
94   // transitioning to an HDR-capable depth when required by a window displayed
95   // on the screen. We want to note the maximum capabilities of the screen, so
96   // we use the largest depth it offers.
97   uint32_t pixelDepth = 0;
98   const NSWindowDepth* depths = [aScreen supportedWindowDepths];
99   for (size_t d = 0; NSWindowDepth depth = depths[d]; d++) {
100     uint32_t bpp = NSBitsPerPixelFromDepth(depth);
101     if (bpp > pixelDepth) {
102       pixelDepth = bpp;
103     }
104   }
106   // But it confuses content if we return too-high a value here. Cap depth with
107   // a value that matches what Chrome returns for high bpp screens.
108   static const uint32_t MAX_REPORTED_PIXEL_DEPTH = 30;
109   if (pixelDepth > MAX_REPORTED_PIXEL_DEPTH) {
110     pixelDepth = MAX_REPORTED_PIXEL_DEPTH;
111   }
113   // What's the maximum color component value this screen can display? This
114   // is a reasonable stand-in for measuring peak brightness.
115   CGFloat componentValueMax =
116       aScreen.maximumPotentialExtendedDynamicRangeColorComponentValue;
118   // Should we treat this as HDR? Based on spec at
119   // https://drafts.csswg.org/mediaqueries-5/#dynamic-range, we'll consider it
120   // HDR if it has pixel depth greater than 24, and if has high peak brightness,
121   // which we measure by checking if it can represent component values greater
122   // than 1.0.
123   //
124   // Also, on HDR screens, users may want to force SDR by setting a different
125   // colorspace, for example by using the "Photography (P3 D65)" preset. In that
126   // case, componentValueMax will be 1.0 and we want to treat the display as
127   // SDR.
128   bool isHDR = pixelDepth > 24 && componentValueMax > 1.0;
130   // Double-check HDR against the platform capabilities.
131   isHDR &= nsCocoaFeatures::OnBigSurOrLater();
133   float dpi = 96.0f;
134   CGDirectDisplayID displayID =
135       [[[aScreen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
136   CGFloat heightMM = ::CGDisplayScreenSize(displayID).height;
137   if (heightMM > 0) {
138     dpi = rect.height / (heightMM / MM_PER_INCH_FLOAT);
139   }
140   MOZ_LOG(sScreenLog, LogLevel::Debug,
141           ("New screen [%d %d %d %d (%d %d %d %d) %d %f %f %f]", rect.x, rect.y,
142            rect.width, rect.height, availRect.x, availRect.y, availRect.width,
143            availRect.height, pixelDepth, contentsScaleFactor.scale,
144            defaultCssScaleFactor.scale, dpi));
146   // Getting the refresh rate is a little hard on OS X. We could use
147   // CVDisplayLinkGetNominalOutputVideoRefreshPeriod, but that's a little
148   // involved. Ideally we could query it from vsync. For now, we leave it out.
149   RefPtr<Screen> screen =
150       new Screen(rect, availRect, pixelDepth, pixelDepth, 0,
151                  contentsScaleFactor, defaultCssScaleFactor, dpi,
152                  Screen::IsPseudoDisplay::No, Screen::IsHDR(isHDR));
153   return screen.forget();
155   NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
158 void ScreenHelperCocoa::RefreshScreens() {
159   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
161   MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
163   AutoTArray<RefPtr<Screen>, 4> screens;
165   for (NSScreen* screen in [NSScreen screens]) {
166     NSDictionary* desc = [screen deviceDescription];
167     if ([desc objectForKey:NSDeviceIsScreen] == nil) {
168       continue;
169     }
170     screens.AppendElement(MakeScreen(screen));
171   }
173   ScreenManager::Refresh(std::move(screens));
175   NS_OBJC_END_TRY_IGNORE_BLOCK;
178 NSScreen* ScreenHelperCocoa::CocoaScreenForScreen(nsIScreen* aScreen) {
179   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
181   for (NSScreen* screen in [NSScreen screens]) {
182     NSDictionary* desc = [screen deviceDescription];
183     if ([desc objectForKey:NSDeviceIsScreen] == nil) {
184       continue;
185     }
186     LayoutDeviceIntRect rect;
187     double scale;
188     aScreen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
189     aScreen->GetContentsScaleFactor(&scale);
190     NSRect frame = [screen frame];
191     LayoutDeviceIntRect frameRect =
192         nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, scale);
193     if (rect == frameRect) {
194       return screen;
195     }
196   }
197   return [NSScreen mainScreen];
199   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
202 }  // namespace widget
203 }  // namespace mozilla