fix_hi-dpi_transitions_on_catalina.patch 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
  2. From: Christopher Cameron <[email protected]>
  3. Date: Thu, 7 Nov 2019 18:28:51 +0000
  4. Subject: Fix hi-dpi transitions on Catalina
  5. A few issues here:
  6. First, actually wire up NativeWidgetNSWindowBridge as a DisplayObserver.
  7. In the past, it didn't matter that this code was missing, because we
  8. were getting notified via windowDidChangeBackingProperties. In Catalina,
  9. that call is happening at a different time, resulting us using an
  10. invalid cached version (which is the second issue).
  11. Second, change GetDisplayNearestWindow to call UpdateDisplaysIfNeeded.
  12. There was a bug here wherein we would return displays_[0], even if we
  13. knew (because of displays_require_update_) that that value was out of
  14. date.
  15. Thid, make GetCachedDisplayForScreen be robust to getting a surprise
  16. NSScreen* that we didn't know about. On Catalina, it happens that we
  17. can read -[NSScreen screens] and see that it has changed, before having
  18. received any notifications that such a change would happen (!).
  19. Fourth, listen for NSApplicationDidChangeScreenParametersNotification
  20. notifications. Just because it sounds like a healthy thing to be doing.
  21. Bug: 1021340
  22. Change-Id: Ibe5a6469d9e2c39cd81d0fb19ee2cfe3aedb1488
  23. Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1902508
  24. Reviewed-by: Elly Fong-Jones <[email protected]>
  25. Commit-Queue: ccameron <[email protected]>
  26. Cr-Commit-Position: refs/heads/master@{#713490}
  27. diff --git a/components/remote_cocoa/app_shim/bridged_native_widget_impl.h b/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
  28. index 9378a4cc6ced0ee4e9c8c3736ec051ca21e581b8..07625bb6729822cb771418307d51039f193eaf69 100644
  29. --- a/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
  30. +++ b/components/remote_cocoa/app_shim/bridged_native_widget_impl.h
  31. @@ -191,6 +191,8 @@ class REMOTE_COCOA_APP_SHIM_EXPORT BridgedNativeWidgetImpl
  32. void SaveKeyEventForRedispatch(NSEvent* event);
  33. // display::DisplayObserver:
  34. + void OnDisplayAdded(const display::Display& new_display) override;
  35. + void OnDisplayRemoved(const display::Display& old_display) override;
  36. void OnDisplayMetricsChanged(const display::Display& display,
  37. uint32_t metrics) override;
  38. diff --git a/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm b/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
  39. index 402b6099126e0ac8dd3b6be5c0ffb1a1946dc139..c6b03fe02d9228bc22b9343687ea845c77c47d8d 100644
  40. --- a/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
  41. +++ b/components/remote_cocoa/app_shim/bridged_native_widget_impl.mm
  42. @@ -310,9 +310,11 @@ BridgedNativeWidgetImpl::BridgedNativeWidgetImpl(
  43. bridge_mojo_binding_(this) {
  44. DCHECK(GetIdToWidgetImplMap().find(id_) == GetIdToWidgetImplMap().end());
  45. GetIdToWidgetImplMap().insert(std::make_pair(id_, this));
  46. + display::Screen::GetScreen()->AddObserver(this);
  47. }
  48. BridgedNativeWidgetImpl::~BridgedNativeWidgetImpl() {
  49. + display::Screen::GetScreen()->RemoveObserver(this);
  50. // The delegate should be cleared already. Note this enforces the precondition
  51. // that -[NSWindow close] is invoked on the hosted window before the
  52. // destructor is called.
  53. @@ -1102,7 +1104,17 @@ remote_cocoa::DragDropClient* BridgedNativeWidgetImpl::drag_drop_client() {
  54. }
  55. ////////////////////////////////////////////////////////////////////////////////
  56. -// BridgedNativeWidgetImpl, ui::CATransactionObserver
  57. +// BridgedNativeWidgetImpl, display::DisplayObserver:
  58. +
  59. +void BridgedNativeWidgetImpl::OnDisplayAdded(
  60. + const display::Display& display) {
  61. + UpdateWindowDisplay();
  62. +}
  63. +
  64. +void BridgedNativeWidgetImpl::OnDisplayRemoved(
  65. + const display::Display& display) {
  66. + UpdateWindowDisplay();
  67. +}
  68. void BridgedNativeWidgetImpl::OnDisplayMetricsChanged(
  69. const display::Display& display,
  70. diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
  71. index 463ff7105ac329cafed793fd87cfc8423e0a0ed7..6b5424c3dae77585bc95b2da48d20168706b3f33 100644
  72. --- a/ui/display/mac/screen_mac.mm
  73. +++ b/ui/display/mac/screen_mac.mm
  74. @@ -31,8 +31,8 @@ Boolean CGDisplayUsesForceToGray(void);
  75. namespace display {
  76. namespace {
  77. -// The delay to handle the display configuration changes.
  78. -// See comments in ScreenMac::HandleDisplayReconfiguration.
  79. +// The delay to handle the display configuration changes. This is in place to
  80. +// coalesce display update notifications and thereby avoid thrashing.
  81. const int64_t kConfigureDelayMs = 500;
  82. NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) {
  83. @@ -155,20 +155,27 @@ class ScreenMac : public Screen {
  84. CGDisplayRegisterReconfigurationCallback(
  85. ScreenMac::DisplayReconfigurationCallBack, this);
  86. + auto update_block = ^(NSNotification* notification) {
  87. + OnNSScreensMayHaveChanged();
  88. + };
  89. +
  90. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  91. screen_color_change_observer_.reset(
  92. [[center addObserverForName:NSScreenColorSpaceDidChangeNotification
  93. object:nil
  94. queue:nil
  95. - usingBlock:^(NSNotification* notification) {
  96. - configure_timer_.Reset();
  97. - displays_require_update_ = true;
  98. - }] retain]);
  99. + usingBlock:update_block] retain]);
  100. + screen_params_change_observer_.reset([[center
  101. + addObserverForName:NSApplicationDidChangeScreenParametersNotification
  102. + object:nil
  103. + queue:nil
  104. + usingBlock:update_block] retain]);
  105. }
  106. ~ScreenMac() override {
  107. NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  108. [center removeObserver:screen_color_change_observer_];
  109. + [center removeObserver:screen_params_change_observer_];
  110. CGDisplayRemoveReconfigurationCallback(
  111. ScreenMac::DisplayReconfigurationCallBack, this);
  112. @@ -193,16 +200,18 @@ class ScreenMac : public Screen {
  113. int GetNumDisplays() const override { return GetAllDisplays().size(); }
  114. const std::vector<Display>& GetAllDisplays() const override {
  115. + UpdateDisplaysIfNeeded();
  116. return displays_;
  117. }
  118. Display GetDisplayNearestWindow(
  119. gfx::NativeWindow native_window) const override {
  120. - NSWindow* window = native_window.GetNativeNSWindow();
  121. - EnsureDisplaysValid();
  122. + UpdateDisplaysIfNeeded();
  123. +
  124. if (displays_.size() == 1)
  125. return displays_[0];
  126. + NSWindow* window = native_window.GetNativeNSWindow();
  127. if (!window)
  128. return GetPrimaryDisplay();
  129. @@ -275,31 +284,30 @@ class ScreenMac : public Screen {
  130. static void DisplayReconfigurationCallBack(CGDirectDisplayID display,
  131. CGDisplayChangeSummaryFlags flags,
  132. void* userInfo) {
  133. - if (flags & kCGDisplayBeginConfigurationFlag)
  134. - return;
  135. -
  136. ScreenMac* screen_mac = static_cast<ScreenMac*>(userInfo);
  137. -
  138. - // Timer::Reset() ensures at least another interval passes before the
  139. - // associated task runs, effectively coalescing these events.
  140. - screen_mac->configure_timer_.Reset();
  141. - screen_mac->displays_require_update_ = true;
  142. + screen_mac->OnNSScreensMayHaveChanged();
  143. }
  144. private:
  145. Display GetCachedDisplayForScreen(NSScreen* screen) const {
  146. - EnsureDisplaysValid();
  147. + UpdateDisplaysIfNeeded();
  148. const CGDirectDisplayID display_id = [[[screen deviceDescription]
  149. objectForKey:@"NSScreenNumber"] unsignedIntValue];
  150. for (const Display& display : displays_) {
  151. if (display_id == display.id())
  152. return display;
  153. }
  154. - NOTREACHED(); // Asked for a hidden/sleeping/mirrored screen?
  155. + // In theory, this should not be reached, because |displays_require_update_|
  156. + // should have been set prior to -[NSScreen screens] changing. In practice,
  157. + // on Catalina, it has been observed that -[NSScreen screens] changes before
  158. + // any notifications are received.
  159. + // https://crbug.com/1021340.
  160. + OnNSScreensMayHaveChanged();
  161. + DLOG(ERROR) << "Value of -[NSScreen screens] changed before notification.";
  162. return BuildDisplayForScreen(screen);
  163. }
  164. - void EnsureDisplaysValid() const {
  165. + void UpdateDisplaysIfNeeded() const {
  166. if (displays_require_update_) {
  167. displays_ = BuildDisplaysFromQuartz();
  168. displays_require_update_ = false;
  169. @@ -307,7 +315,7 @@ class ScreenMac : public Screen {
  170. }
  171. void ConfigureTimerFired() {
  172. - EnsureDisplaysValid();
  173. + UpdateDisplaysIfNeeded();
  174. change_notifier_.NotifyDisplaysChanged(old_displays_, displays_);
  175. old_displays_ = displays_;
  176. }
  177. @@ -321,7 +329,7 @@ class ScreenMac : public Screen {
  178. // It would be ridiculous to have this many displays connected, but
  179. // CGDirectDisplayID is just an integer, so supporting up to this many
  180. // doesn't hurt.
  181. - CGDirectDisplayID online_displays[128];
  182. + CGDirectDisplayID online_displays[1024];
  183. CGDisplayCount online_display_count = 0;
  184. if (CGGetOnlineDisplayList(base::size(online_displays), online_displays,
  185. &online_display_count) != kCGErrorSuccess) {
  186. @@ -357,21 +365,32 @@ class ScreenMac : public Screen {
  187. : displays;
  188. }
  189. - // The displays currently attached to the device. Cached.
  190. + void OnNSScreensMayHaveChanged() const {
  191. + // Timer::Reset() ensures at least another interval passes before the
  192. + // associated task runs, effectively coalescing these events.
  193. + configure_timer_.Reset();
  194. + displays_require_update_ = true;
  195. + }
  196. +
  197. + // The displays currently attached to the device. Updated by
  198. + // UpdateDisplaysIfNeeded.
  199. mutable std::vector<Display> displays_;
  200. - // Set whenever the CGDisplayRegisterReconfigurationCallback is invoked and
  201. - // cleared when |displays_| is updated by BuildDisplaysFromQuartz().
  202. + // Whether or not |displays_| might need to be upated. Set in
  203. + // OnNSScreensMayHaveChanged, and un-set by UpdateDisplaysIfNeeded.
  204. mutable bool displays_require_update_ = false;
  205. - // The displays last communicated to DisplayChangeNotifier.
  206. - std::vector<Display> old_displays_;
  207. + // The timer to delay configuring outputs and notifying observers (to coalesce
  208. + // several updates into one update).
  209. + mutable base::RetainingOneShotTimer configure_timer_;
  210. - // The timer to delay configuring outputs and notifying observers.
  211. - base::RetainingOneShotTimer configure_timer_;
  212. + // The displays last communicated to the DisplayChangeNotifier.
  213. + std::vector<Display> old_displays_;
  214. - // The observer notified by NSScreenColorSpaceDidChangeNotification.
  215. + // The observers notified by NSScreenColorSpaceDidChangeNotification and
  216. + // NSApplicationDidChangeScreenParametersNotification.
  217. base::scoped_nsobject<id> screen_color_change_observer_;
  218. + base::scoped_nsobject<id> screen_params_change_observer_;
  219. DisplayChangeNotifier change_notifier_;