Bug 1880227 - Migrate Focus docs into Sphinx. r=owlish,geckoview-reviewers,android...
[gecko.git] / widget / cocoa / ScreenHelperCocoa.mm
blob57e1313320c52bbe5bbf552c8e7bc9e16d10ce24
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 "nsCocoaUtils.h"
13 #include "nsObjCExceptions.h"
15 using namespace mozilla;
17 static LazyLogModule sScreenLog("WidgetScreen");
19 @interface ScreenHelperDelegate : NSObject {
20  @private
21   mozilla::widget::ScreenHelperCocoa* mHelper;
24 - (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper;
25 - (void)didChangeScreenParameters:(NSNotification*)aNotification;
26 @end
28 @implementation ScreenHelperDelegate
29 - (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper {
30   if ((self = [self init])) {
31     mHelper = aScreenHelper;
33     [[NSNotificationCenter defaultCenter]
34         addObserver:self
35            selector:@selector(didChangeScreenParameters:)
36                name:NSApplicationDidChangeScreenParametersNotification
37              object:nil];
38   }
40   return self;
43 - (void)dealloc {
44   [[NSNotificationCenter defaultCenter] removeObserver:self];
45   [super dealloc];
48 - (void)didChangeScreenParameters:(NSNotification*)aNotification {
49   MOZ_LOG(sScreenLog, LogLevel::Debug,
50           ("Received NSApplicationDidChangeScreenParametersNotification"));
52   mHelper->RefreshScreens();
54 @end
56 namespace mozilla {
57 namespace widget {
59 ScreenHelperCocoa::ScreenHelperCocoa() {
60   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
62   MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperCocoa created"));
64   mDelegate = [[ScreenHelperDelegate alloc] initWithScreenHelper:this];
66   RefreshScreens();
68   NS_OBJC_END_TRY_IGNORE_BLOCK;
71 ScreenHelperCocoa::~ScreenHelperCocoa() {
72   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
74   [mDelegate release];
76   NS_OBJC_END_TRY_IGNORE_BLOCK;
79 static already_AddRefed<Screen> MakeScreen(NSScreen* aScreen) {
80   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
82   DesktopToLayoutDeviceScale contentsScaleFactor(
83       nsCocoaUtils::GetBackingScaleFactor(aScreen));
84   CSSToLayoutDeviceScale defaultCssScaleFactor(contentsScaleFactor.scale);
85   NSRect frame = [aScreen frame];
86   LayoutDeviceIntRect rect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(
87       frame, contentsScaleFactor.scale);
88   frame = [aScreen visibleFrame];
89   LayoutDeviceIntRect availRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(
90       frame, contentsScaleFactor.scale);
92   // aScreen may be capable of displaying multiple pixel depths, for example by
93   // transitioning to an HDR-capable depth when required by a window displayed
94   // on the screen. We want to note the maximum capabilities of the screen, so
95   // we use the largest depth it offers.
96   uint32_t pixelDepth = 0;
97   const NSWindowDepth* depths = [aScreen supportedWindowDepths];
98   for (size_t d = 0; NSWindowDepth depth = depths[d]; d++) {
99     uint32_t bpp = NSBitsPerPixelFromDepth(depth);
100     if (bpp > pixelDepth) {
101       pixelDepth = bpp;
102     }
103   }
105   // But it confuses content if we return too-high a value here. Cap depth with
106   // a value that matches what Chrome returns for high bpp screens.
107   static const uint32_t MAX_REPORTED_PIXEL_DEPTH = 30;
108   if (pixelDepth > MAX_REPORTED_PIXEL_DEPTH) {
109     pixelDepth = MAX_REPORTED_PIXEL_DEPTH;
110   }
112   float dpi = 96.0f;
113   CGDirectDisplayID displayID =
114       [[[aScreen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
115   CGFloat heightMM = ::CGDisplayScreenSize(displayID).height;
116   if (heightMM > 0) {
117     dpi = rect.height / (heightMM / MM_PER_INCH_FLOAT);
118   }
119   MOZ_LOG(sScreenLog, LogLevel::Debug,
120           ("New screen [%d %d %d %d (%d %d %d %d) %d %f %f %f]", rect.x, rect.y,
121            rect.width, rect.height, availRect.x, availRect.y, availRect.width,
122            availRect.height, pixelDepth, contentsScaleFactor.scale,
123            defaultCssScaleFactor.scale, dpi));
125   // Getting the refresh rate is a little hard on OS X. We could use
126   // CVDisplayLinkGetNominalOutputVideoRefreshPeriod, but that's a little
127   // involved. Ideally we could query it from vsync. For now, we leave it out.
128   RefPtr<Screen> screen = new Screen(rect, availRect, pixelDepth, pixelDepth, 0,
129                                      contentsScaleFactor, defaultCssScaleFactor,
130                                      dpi, Screen::IsPseudoDisplay::No);
131   return screen.forget();
133   NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
136 void ScreenHelperCocoa::RefreshScreens() {
137   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
139   MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
141   AutoTArray<RefPtr<Screen>, 4> screens;
143   for (NSScreen* screen in [NSScreen screens]) {
144     NSDictionary* desc = [screen deviceDescription];
145     if ([desc objectForKey:NSDeviceIsScreen] == nil) {
146       continue;
147     }
148     screens.AppendElement(MakeScreen(screen));
149   }
151   ScreenManager::Refresh(std::move(screens));
153   NS_OBJC_END_TRY_IGNORE_BLOCK;
156 NSScreen* ScreenHelperCocoa::CocoaScreenForScreen(nsIScreen* aScreen) {
157   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
159   for (NSScreen* screen in [NSScreen screens]) {
160     NSDictionary* desc = [screen deviceDescription];
161     if ([desc objectForKey:NSDeviceIsScreen] == nil) {
162       continue;
163     }
164     LayoutDeviceIntRect rect;
165     double scale;
166     aScreen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
167     aScreen->GetContentsScaleFactor(&scale);
168     NSRect frame = [screen frame];
169     LayoutDeviceIntRect frameRect =
170         nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, scale);
171     if (rect == frameRect) {
172       return screen;
173     }
174   }
175   return [NSScreen mainScreen];
177   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
180 }  // namespace widget
181 }  // namespace mozilla