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 "ScreenHelperCocoa.h" 8 9#import <Cocoa/Cocoa.h> 10 11#include "mozilla/Logging.h" 12#include "nsCocoaUtils.h" 13#include "nsObjCExceptions.h" 14 15using namespace mozilla; 16 17static LazyLogModule sScreenLog("WidgetScreen"); 18 19@interface ScreenHelperDelegate : NSObject { 20 @private 21 mozilla::widget::ScreenHelperCocoa* mHelper; 22} 23 24- (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper; 25- (void)didChangeScreenParameters:(NSNotification*)aNotification; 26@end 27 28@implementation ScreenHelperDelegate 29- (id)initWithScreenHelper:(mozilla::widget::ScreenHelperCocoa*)aScreenHelper { 30 if ((self = [self init])) { 31 mHelper = aScreenHelper; 32 33 [[NSNotificationCenter defaultCenter] 34 addObserver:self 35 selector:@selector(didChangeScreenParameters:) 36 name:NSApplicationDidChangeScreenParametersNotification 37 object:nil]; 38 } 39 40 return self; 41} 42 43- (void)dealloc { 44 [[NSNotificationCenter defaultCenter] removeObserver:self]; 45 [super dealloc]; 46} 47 48- (void)didChangeScreenParameters:(NSNotification*)aNotification { 49 MOZ_LOG(sScreenLog, LogLevel::Debug, 50 ("Received NSApplicationDidChangeScreenParametersNotification")); 51 52 mHelper->RefreshScreens(); 53} 54@end 55 56namespace mozilla { 57namespace widget { 58 59ScreenHelperCocoa::ScreenHelperCocoa() { 60 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; 61 62 MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperCocoa created")); 63 64 mDelegate = [[ScreenHelperDelegate alloc] initWithScreenHelper:this]; 65 66 RefreshScreens(); 67 68 NS_OBJC_END_TRY_IGNORE_BLOCK; 69} 70 71ScreenHelperCocoa::~ScreenHelperCocoa() { 72 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; 73 74 [mDelegate release]; 75 76 NS_OBJC_END_TRY_IGNORE_BLOCK; 77} 78 79static already_AddRefed<Screen> MakeScreen(NSScreen* aScreen) { 80 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 81 82 DesktopToLayoutDeviceScale contentsScaleFactor(nsCocoaUtils::GetBackingScaleFactor(aScreen)); 83 CSSToLayoutDeviceScale defaultCssScaleFactor(contentsScaleFactor.scale); 84 NSRect frame = [aScreen frame]; 85 LayoutDeviceIntRect rect = 86 nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, contentsScaleFactor.scale); 87 frame = [aScreen visibleFrame]; 88 LayoutDeviceIntRect availRect = 89 nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, contentsScaleFactor.scale); 90 NSWindowDepth depth = [aScreen depth]; 91 uint32_t pixelDepth = NSBitsPerPixelFromDepth(depth); 92 float dpi = 96.0f; 93 CGDirectDisplayID displayID = 94 [[[aScreen deviceDescription] objectForKey:@"NSScreenNumber"] intValue]; 95 CGFloat heightMM = ::CGDisplayScreenSize(displayID).height; 96 if (heightMM > 0) { 97 dpi = rect.height / (heightMM / MM_PER_INCH_FLOAT); 98 } 99 MOZ_LOG(sScreenLog, LogLevel::Debug, 100 ("New screen [%d %d %d %d (%d %d %d %d) %d %f %f %f]", rect.x, rect.y, rect.width, 101 rect.height, availRect.x, availRect.y, availRect.width, availRect.height, pixelDepth, 102 contentsScaleFactor.scale, defaultCssScaleFactor.scale, dpi)); 103 104 RefPtr<Screen> screen = new Screen(rect, availRect, pixelDepth, pixelDepth, contentsScaleFactor, 105 defaultCssScaleFactor, dpi); 106 return screen.forget(); 107 108 NS_OBJC_END_TRY_BLOCK_RETURN(nullptr); 109} 110 111void ScreenHelperCocoa::RefreshScreens() { 112 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; 113 114 MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens")); 115 116 AutoTArray<RefPtr<Screen>, 4> screens; 117 118 for (NSScreen* screen in [NSScreen screens]) { 119 NSDictionary* desc = [screen deviceDescription]; 120 if ([desc objectForKey:NSDeviceIsScreen] == nil) { 121 continue; 122 } 123 screens.AppendElement(MakeScreen(screen)); 124 } 125 126 ScreenManager& screenManager = ScreenManager::GetSingleton(); 127 screenManager.Refresh(std::move(screens)); 128 129 NS_OBJC_END_TRY_IGNORE_BLOCK; 130} 131 132NSScreen* ScreenHelperCocoa::CocoaScreenForScreen(nsIScreen* aScreen) { 133 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 134 135 for (NSScreen* screen in [NSScreen screens]) { 136 NSDictionary* desc = [screen deviceDescription]; 137 if ([desc objectForKey:NSDeviceIsScreen] == nil) { 138 continue; 139 } 140 LayoutDeviceIntRect rect; 141 double scale; 142 aScreen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height); 143 aScreen->GetContentsScaleFactor(&scale); 144 NSRect frame = [screen frame]; 145 LayoutDeviceIntRect frameRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, scale); 146 if (rect == frameRect) { 147 return screen; 148 } 149 } 150 return [NSScreen mainScreen]; 151 152 NS_OBJC_END_TRY_BLOCK_RETURN(nil); 153} 154 155} // namespace widget 156} // namespace mozilla 157