summaryrefslogtreecommitdiffstats
path: root/widget/cocoa/ScreenHelperCocoa.mm
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--widget/cocoa/ScreenHelperCocoa.mm178
1 files changed, 178 insertions, 0 deletions
diff --git a/widget/cocoa/ScreenHelperCocoa.mm b/widget/cocoa/ScreenHelperCocoa.mm
new file mode 100644
index 0000000000..27c18bbfdf
--- /dev/null
+++ b/widget/cocoa/ScreenHelperCocoa.mm
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScreenHelperCocoa.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "mozilla/Logging.h"
+#include "nsCocoaUtils.h"
+#include "nsObjCExceptions.h"
+
+using namespace mozilla;
+
+static LazyLogModule sScreenLog("WidgetScreen");
+
+@interface ScreenHelperDelegate : NSObject {
+ @private
+ mozilla::widget::ScreenHelperCocoa* mHelper;
+}
+
+- (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper;
+- (void)didChangeScreenParameters:(NSNotification*)aNotification;
+@end
+
+@implementation ScreenHelperDelegate
+- (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper {
+ if ((self = [self init])) {
+ mHelper = aScreenHelper;
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(didChangeScreenParameters:)
+ name:NSApplicationDidChangeScreenParametersNotification
+ object:nil];
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super dealloc];
+}
+
+- (void)didChangeScreenParameters:(NSNotification*)aNotification {
+ MOZ_LOG(sScreenLog, LogLevel::Debug,
+ ("Received NSApplicationDidChangeScreenParametersNotification"));
+
+ mHelper->RefreshScreens();
+}
+@end
+
+namespace mozilla {
+namespace widget {
+
+ScreenHelperCocoa::ScreenHelperCocoa() {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperCocoa created"));
+
+ mDelegate = [[ScreenHelperDelegate alloc] initWithScreenHelper:this];
+
+ RefreshScreens();
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+ScreenHelperCocoa::~ScreenHelperCocoa() {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ [mDelegate release];
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+static already_AddRefed<Screen> MakeScreen(NSScreen* aScreen) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ DesktopToLayoutDeviceScale contentsScaleFactor(nsCocoaUtils::GetBackingScaleFactor(aScreen));
+ CSSToLayoutDeviceScale defaultCssScaleFactor(contentsScaleFactor.scale);
+ NSRect frame = [aScreen frame];
+ LayoutDeviceIntRect rect =
+ nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, contentsScaleFactor.scale);
+ frame = [aScreen visibleFrame];
+ LayoutDeviceIntRect availRect =
+ nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, contentsScaleFactor.scale);
+
+ // aScreen may be capable of displaying multiple pixel depths, for example by
+ // transitioning to an HDR-capable depth when required by a window displayed on
+ // the screen. We want to note the maximum capabilities of the screen, so we use
+ // the largest depth it offers.
+ uint32_t pixelDepth = 0;
+ const NSWindowDepth* depths = [aScreen supportedWindowDepths];
+ for (size_t d = 0; NSWindowDepth depth = depths[d]; d++) {
+ uint32_t bpp = NSBitsPerPixelFromDepth(depth);
+ if (bpp > pixelDepth) {
+ pixelDepth = bpp;
+ }
+ }
+
+ // But it confuses content if we return too-high a value here. Cap depth with
+ // a value that matches what Chrome returns for high bpp screens.
+ static const uint32_t MAX_REPORTED_PIXEL_DEPTH = 30;
+ if (pixelDepth > MAX_REPORTED_PIXEL_DEPTH) {
+ pixelDepth = MAX_REPORTED_PIXEL_DEPTH;
+ }
+
+ float dpi = 96.0f;
+ CGDirectDisplayID displayID =
+ [[[aScreen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
+ CGFloat heightMM = ::CGDisplayScreenSize(displayID).height;
+ if (heightMM > 0) {
+ dpi = rect.height / (heightMM / MM_PER_INCH_FLOAT);
+ }
+ MOZ_LOG(sScreenLog, LogLevel::Debug,
+ ("New screen [%d %d %d %d (%d %d %d %d) %d %f %f %f]", rect.x, rect.y, rect.width,
+ rect.height, availRect.x, availRect.y, availRect.width, availRect.height, pixelDepth,
+ contentsScaleFactor.scale, defaultCssScaleFactor.scale, dpi));
+
+ // Getting the refresh rate is a little hard on OS X. We could use
+ // CVDisplayLinkGetNominalOutputVideoRefreshPeriod, but that's a little
+ // involved. Ideally we could query it from vsync. For now, we leave it out.
+ RefPtr<Screen> screen =
+ new Screen(rect, availRect, pixelDepth, pixelDepth, 0, contentsScaleFactor,
+ defaultCssScaleFactor, dpi, Screen::IsPseudoDisplay::No);
+ return screen.forget();
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
+}
+
+void ScreenHelperCocoa::RefreshScreens() {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
+
+ AutoTArray<RefPtr<Screen>, 4> screens;
+
+ for (NSScreen* screen in [NSScreen screens]) {
+ NSDictionary* desc = [screen deviceDescription];
+ if ([desc objectForKey:NSDeviceIsScreen] == nil) {
+ continue;
+ }
+ screens.AppendElement(MakeScreen(screen));
+ }
+
+ ScreenManager::Refresh(std::move(screens));
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+NSScreen* ScreenHelperCocoa::CocoaScreenForScreen(nsIScreen* aScreen) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ for (NSScreen* screen in [NSScreen screens]) {
+ NSDictionary* desc = [screen deviceDescription];
+ if ([desc objectForKey:NSDeviceIsScreen] == nil) {
+ continue;
+ }
+ LayoutDeviceIntRect rect;
+ double scale;
+ aScreen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
+ aScreen->GetContentsScaleFactor(&scale);
+ NSRect frame = [screen frame];
+ LayoutDeviceIntRect frameRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, scale);
+ if (rect == frameRect) {
+ return screen;
+ }
+ }
+ return [NSScreen mainScreen];
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(nil);
+}
+
+} // namespace widget
+} // namespace mozilla