123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
- From: Christopher Cameron <[email protected]>
- Date: Thu, 7 Nov 2019 18:28:51 +0000
- Subject: Fix hi-dpi transitions on Catalina
- A few issues here:
- First, actually wire up NativeWidgetNSWindowBridge as a DisplayObserver.
- In the past, it didn't matter that this code was missing, because we
- were getting notified via windowDidChangeBackingProperties. In Catalina,
- that call is happening at a different time, resulting us using an
- invalid cached version (which is the second issue).
- Second, change GetDisplayNearestWindow to call UpdateDisplaysIfNeeded.
- There was a bug here wherein we would return displays_[0], even if we
- knew (because of displays_require_update_) that that value was out of
- date.
- Thid, make GetCachedDisplayForScreen be robust to getting a surprise
- NSScreen* that we didn't know about. On Catalina, it happens that we
- can read -[NSScreen screens] and see that it has changed, before having
- received any notifications that such a change would happen (!).
- Fourth, listen for NSApplicationDidChangeScreenParametersNotification
- notifications. Just because it sounds like a healthy thing to be doing.
- Bug: 1021340
- Change-Id: Ibe5a6469d9e2c39cd81d0fb19ee2cfe3aedb1488
- Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1902508
- Reviewed-by: Elly Fong-Jones <[email protected]>
- Commit-Queue: ccameron <[email protected]>
- Cr-Commit-Position: refs/heads/master@{#713490}
- diff --git a/components/remote_cocoa/app_shim/bridged_native_widget_impl.h b/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
- index 9378a4cc6ced0ee4e9c8c3736ec051ca21e581b8..07625bb6729822cb771418307d51039f193eaf69 100644
- --- a/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
- +++ b/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
- @@ -191,6 +191,8 @@ class REMOTE_COCOA_APP_SHIM_EXPORT BridgedNativeWidgetImpl
- void SaveKeyEventForRedispatch(NSEvent* event);
-
- // display::DisplayObserver:
- + void OnDisplayAdded(const display::Display& new_display) override;
- + void OnDisplayRemoved(const display::Display& old_display) override;
- void OnDisplayMetricsChanged(const display::Display& display,
- uint32_t metrics) override;
-
- diff --git a/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm b/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
- index 402b6099126e0ac8dd3b6be5c0ffb1a1946dc139..c6b03fe02d9228bc22b9343687ea845c77c47d8d 100644
- --- a/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
- +++ b/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
- @@ -310,9 +310,11 @@ BridgedNativeWidgetImpl::BridgedNativeWidgetImpl(
- bridge_mojo_binding_(this) {
- DCHECK(GetIdToWidgetImplMap().find(id_) == GetIdToWidgetImplMap().end());
- GetIdToWidgetImplMap().insert(std::make_pair(id_, this));
- + display::Screen::GetScreen()->AddObserver(this);
- }
-
- BridgedNativeWidgetImpl::~BridgedNativeWidgetImpl() {
- + display::Screen::GetScreen()->RemoveObserver(this);
- // The delegate should be cleared already. Note this enforces the precondition
- // that -[NSWindow close] is invoked on the hosted window before the
- // destructor is called.
- @@ -1102,7 +1104,17 @@ remote_cocoa::DragDropClient* BridgedNativeWidgetImpl::drag_drop_client() {
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- -// BridgedNativeWidgetImpl, ui::CATransactionObserver
- +// BridgedNativeWidgetImpl, display::DisplayObserver:
- +
- +void BridgedNativeWidgetImpl::OnDisplayAdded(
- + const display::Display& display) {
- + UpdateWindowDisplay();
- +}
- +
- +void BridgedNativeWidgetImpl::OnDisplayRemoved(
- + const display::Display& display) {
- + UpdateWindowDisplay();
- +}
-
- void BridgedNativeWidgetImpl::OnDisplayMetricsChanged(
- const display::Display& display,
- diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
- index 463ff7105ac329cafed793fd87cfc8423e0a0ed7..6b5424c3dae77585bc95b2da48d20168706b3f33 100644
- --- a/ui/display/mac/screen_mac.mm
- +++ b/ui/display/mac/screen_mac.mm
- @@ -31,8 +31,8 @@ Boolean CGDisplayUsesForceToGray(void);
- namespace display {
- namespace {
-
- -// The delay to handle the display configuration changes.
- -// See comments in ScreenMac::HandleDisplayReconfiguration.
- +// The delay to handle the display configuration changes. This is in place to
- +// coalesce display update notifications and thereby avoid thrashing.
- const int64_t kConfigureDelayMs = 500;
-
- NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) {
- @@ -155,20 +155,27 @@ class ScreenMac : public Screen {
- CGDisplayRegisterReconfigurationCallback(
- ScreenMac::DisplayReconfigurationCallBack, this);
-
- + auto update_block = ^(NSNotification* notification) {
- + OnNSScreensMayHaveChanged();
- + };
- +
- NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
- screen_color_change_observer_.reset(
- [[center addObserverForName:NSScreenColorSpaceDidChangeNotification
- object:nil
- queue:nil
- - usingBlock:^(NSNotification* notification) {
- - configure_timer_.Reset();
- - displays_require_update_ = true;
- - }] retain]);
- + usingBlock:update_block] retain]);
- + screen_params_change_observer_.reset([[center
- + addObserverForName:NSApplicationDidChangeScreenParametersNotification
- + object:nil
- + queue:nil
- + usingBlock:update_block] retain]);
- }
-
- ~ScreenMac() override {
- NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
- [center removeObserver:screen_color_change_observer_];
- + [center removeObserver:screen_params_change_observer_];
-
- CGDisplayRemoveReconfigurationCallback(
- ScreenMac::DisplayReconfigurationCallBack, this);
- @@ -193,16 +200,18 @@ class ScreenMac : public Screen {
- int GetNumDisplays() const override { return GetAllDisplays().size(); }
-
- const std::vector<Display>& GetAllDisplays() const override {
- + UpdateDisplaysIfNeeded();
- return displays_;
- }
-
- Display GetDisplayNearestWindow(
- gfx::NativeWindow native_window) const override {
- - NSWindow* window = native_window.GetNativeNSWindow();
- - EnsureDisplaysValid();
- + UpdateDisplaysIfNeeded();
- +
- if (displays_.size() == 1)
- return displays_[0];
-
- + NSWindow* window = native_window.GetNativeNSWindow();
- if (!window)
- return GetPrimaryDisplay();
-
- @@ -275,31 +284,30 @@ class ScreenMac : public Screen {
- static void DisplayReconfigurationCallBack(CGDirectDisplayID display,
- CGDisplayChangeSummaryFlags flags,
- void* userInfo) {
- - if (flags & kCGDisplayBeginConfigurationFlag)
- - return;
- -
- ScreenMac* screen_mac = static_cast<ScreenMac*>(userInfo);
- -
- - // Timer::Reset() ensures at least another interval passes before the
- - // associated task runs, effectively coalescing these events.
- - screen_mac->configure_timer_.Reset();
- - screen_mac->displays_require_update_ = true;
- + screen_mac->OnNSScreensMayHaveChanged();
- }
-
- private:
- Display GetCachedDisplayForScreen(NSScreen* screen) const {
- - EnsureDisplaysValid();
- + UpdateDisplaysIfNeeded();
- const CGDirectDisplayID display_id = [[[screen deviceDescription]
- objectForKey:@"NSScreenNumber"] unsignedIntValue];
- for (const Display& display : displays_) {
- if (display_id == display.id())
- return display;
- }
- - NOTREACHED(); // Asked for a hidden/sleeping/mirrored screen?
- + // In theory, this should not be reached, because |displays_require_update_|
- + // should have been set prior to -[NSScreen screens] changing. In practice,
- + // on Catalina, it has been observed that -[NSScreen screens] changes before
- + // any notifications are received.
- + // https://crbug.com/1021340.
- + OnNSScreensMayHaveChanged();
- + DLOG(ERROR) << "Value of -[NSScreen screens] changed before notification.";
- return BuildDisplayForScreen(screen);
- }
-
- - void EnsureDisplaysValid() const {
- + void UpdateDisplaysIfNeeded() const {
- if (displays_require_update_) {
- displays_ = BuildDisplaysFromQuartz();
- displays_require_update_ = false;
- @@ -307,7 +315,7 @@ class ScreenMac : public Screen {
- }
-
- void ConfigureTimerFired() {
- - EnsureDisplaysValid();
- + UpdateDisplaysIfNeeded();
- change_notifier_.NotifyDisplaysChanged(old_displays_, displays_);
- old_displays_ = displays_;
- }
- @@ -321,7 +329,7 @@ class ScreenMac : public Screen {
- // It would be ridiculous to have this many displays connected, but
- // CGDirectDisplayID is just an integer, so supporting up to this many
- // doesn't hurt.
- - CGDirectDisplayID online_displays[128];
- + CGDirectDisplayID online_displays[1024];
- CGDisplayCount online_display_count = 0;
- if (CGGetOnlineDisplayList(base::size(online_displays), online_displays,
- &online_display_count) != kCGErrorSuccess) {
- @@ -357,21 +365,32 @@ class ScreenMac : public Screen {
- : displays;
- }
-
- - // The displays currently attached to the device. Cached.
- + void OnNSScreensMayHaveChanged() const {
- + // Timer::Reset() ensures at least another interval passes before the
- + // associated task runs, effectively coalescing these events.
- + configure_timer_.Reset();
- + displays_require_update_ = true;
- + }
- +
- + // The displays currently attached to the device. Updated by
- + // UpdateDisplaysIfNeeded.
- mutable std::vector<Display> displays_;
-
- - // Set whenever the CGDisplayRegisterReconfigurationCallback is invoked and
- - // cleared when |displays_| is updated by BuildDisplaysFromQuartz().
- + // Whether or not |displays_| might need to be upated. Set in
- + // OnNSScreensMayHaveChanged, and un-set by UpdateDisplaysIfNeeded.
- mutable bool displays_require_update_ = false;
-
- - // The displays last communicated to DisplayChangeNotifier.
- - std::vector<Display> old_displays_;
- + // The timer to delay configuring outputs and notifying observers (to coalesce
- + // several updates into one update).
- + mutable base::RetainingOneShotTimer configure_timer_;
-
- - // The timer to delay configuring outputs and notifying observers.
- - base::RetainingOneShotTimer configure_timer_;
- + // The displays last communicated to the DisplayChangeNotifier.
- + std::vector<Display> old_displays_;
-
- - // The observer notified by NSScreenColorSpaceDidChangeNotification.
- + // The observers notified by NSScreenColorSpaceDidChangeNotification and
- + // NSApplicationDidChangeScreenParametersNotification.
- base::scoped_nsobject<id> screen_color_change_observer_;
- + base::scoped_nsobject<id> screen_params_change_observer_;
-
- DisplayChangeNotifier change_notifier_;
-
|