resolution_notification_controller.cc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/display/resolution_notification_controller.h"
  5. #include <utility>
  6. #include "ash/display/display_change_dialog.h"
  7. #include "ash/display/display_util.h"
  8. #include "ash/public/cpp/notification_utils.h"
  9. #include "ash/resources/vector_icons/vector_icons.h"
  10. #include "ash/session/session_controller_impl.h"
  11. #include "ash/shell.h"
  12. #include "ash/strings/grit/ash_strings.h"
  13. #include "ash/system/screen_layout_observer.h"
  14. #include "base/bind.h"
  15. #include "base/strings/utf_string_conversions.h"
  16. #include "ui/base/l10n/l10n_util.h"
  17. #include "ui/display/display_features.h"
  18. #include "ui/display/manager/display_manager.h"
  19. #include "ui/display/manager/managed_display_info.h"
  20. #include "ui/display/util/display_util.h"
  21. namespace ash {
  22. struct ResolutionNotificationController::ResolutionChangeInfo {
  23. ResolutionChangeInfo(int64_t display_id,
  24. const display::ManagedDisplayMode& old_resolution,
  25. const display::ManagedDisplayMode& new_resolution,
  26. base::OnceClosure accept_callback);
  27. ResolutionChangeInfo(const ResolutionChangeInfo&) = delete;
  28. ResolutionChangeInfo& operator=(const ResolutionChangeInfo&) = delete;
  29. ~ResolutionChangeInfo();
  30. // The id of the display where the resolution change happens.
  31. const int64_t display_id;
  32. // The resolution before the change.
  33. display::ManagedDisplayMode old_resolution;
  34. // The requested resolution. Note that this may be different from
  35. // |current_resolution| which is the actual resolution set.
  36. display::ManagedDisplayMode new_resolution;
  37. // The actual resolution after the change.
  38. display::ManagedDisplayMode current_resolution;
  39. // The callback when accept is chosen.
  40. base::OnceClosure accept_callback;
  41. };
  42. ResolutionNotificationController::ResolutionChangeInfo::ResolutionChangeInfo(
  43. int64_t display_id,
  44. const display::ManagedDisplayMode& old_resolution,
  45. const display::ManagedDisplayMode& new_resolution,
  46. base::OnceClosure accept_callback)
  47. : display_id(display_id),
  48. old_resolution(old_resolution),
  49. new_resolution(new_resolution),
  50. accept_callback(std::move(accept_callback)) {}
  51. ResolutionNotificationController::ResolutionChangeInfo::
  52. ~ResolutionChangeInfo() = default;
  53. ResolutionNotificationController::ResolutionNotificationController() {
  54. Shell::Get()->window_tree_host_manager()->AddObserver(this);
  55. }
  56. ResolutionNotificationController::~ResolutionNotificationController() {
  57. Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
  58. }
  59. bool ResolutionNotificationController::PrepareNotificationAndSetDisplayMode(
  60. int64_t display_id,
  61. const display::ManagedDisplayMode& old_resolution,
  62. const display::ManagedDisplayMode& new_resolution,
  63. crosapi::mojom::DisplayConfigSource source,
  64. base::OnceClosure accept_callback) {
  65. Shell::Get()->screen_layout_observer()->SetDisplayChangedFromSettingsUI(
  66. display_id);
  67. display::DisplayManager* const display_manager =
  68. Shell::Get()->display_manager();
  69. if (source == crosapi::mojom::DisplayConfigSource::kPolicy ||
  70. display::IsInternalDisplayId(display_id)) {
  71. // We don't show notifications to confirm/revert the resolution change in
  72. // the case of an internal display or policy-forced changes.
  73. return display_manager->SetDisplayMode(display_id, new_resolution);
  74. }
  75. // If multiple resolution changes are invoked for the same display,
  76. // the original resolution for the first resolution change has to be used
  77. // instead of the specified |old_resolution|.
  78. display::ManagedDisplayMode original_resolution;
  79. if (change_info_ && change_info_->display_id == display_id) {
  80. DCHECK_EQ(change_info_->new_resolution.size(), old_resolution.size());
  81. original_resolution = change_info_->old_resolution;
  82. }
  83. if (change_info_ && change_info_->display_id != display_id) {
  84. // Preparing the notification for a new resolution change of another display
  85. // before the previous one was accepted. We decided that it's safer to
  86. // revert the previous resolution change since the user didn't explicitly
  87. // accept it, and we have no way of knowing for sure that it worked.
  88. RevertResolutionChange(false /* display_was_removed */);
  89. }
  90. change_info_ = std::make_unique<ResolutionChangeInfo>(
  91. display_id, old_resolution, new_resolution, std::move(accept_callback));
  92. if (!original_resolution.size().IsEmpty())
  93. change_info_->old_resolution = original_resolution;
  94. if (!display_manager->SetDisplayMode(display_id, new_resolution)) {
  95. // Discard the prepared notification data since we failed to set the new
  96. // resolution.
  97. change_info_.reset();
  98. return false;
  99. }
  100. return true;
  101. }
  102. bool ResolutionNotificationController::ShouldShowDisplayChangeDialog() const {
  103. return change_info_ && Shell::Get()->session_controller()->login_status() !=
  104. LoginStatus::KIOSK_APP;
  105. }
  106. void ResolutionNotificationController::CreateOrReplaceModalDialog() {
  107. if (confirmation_dialog_)
  108. confirmation_dialog_->GetWidget()->CloseNow();
  109. if (!ShouldShowDisplayChangeDialog())
  110. return;
  111. const std::u16string display_name =
  112. base::UTF8ToUTF16(Shell::Get()->display_manager()->GetDisplayNameForId(
  113. change_info_->display_id));
  114. const std::u16string actual_display_size =
  115. base::UTF8ToUTF16(change_info_->current_resolution.size().ToString());
  116. const std::u16string requested_display_size =
  117. base::UTF8ToUTF16(change_info_->new_resolution.size().ToString());
  118. std::u16string dialog_title =
  119. l10n_util::GetStringUTF16(IDS_ASH_RESOLUTION_CHANGE_DIALOG_TITLE);
  120. // Construct the timeout message, leaving a placeholder for the countdown
  121. // timer so that the string does not need to be completely rebuilt every
  122. // timer tick.
  123. constexpr char16_t kTimeoutPlaceHolder[] = u"$1";
  124. std::u16string timeout_message_with_placeholder;
  125. if (display::features::IsListAllDisplayModesEnabled()) {
  126. dialog_title = l10n_util::GetStringUTF16(
  127. IDS_ASH_RESOLUTION_REFRESH_CHANGE_DIALOG_TITLE);
  128. const std::u16string actual_refresh_rate = ConvertRefreshRateToString16(
  129. change_info_->current_resolution.refresh_rate());
  130. const std::u16string requested_refresh_rate = ConvertRefreshRateToString16(
  131. change_info_->new_resolution.refresh_rate());
  132. const bool no_fallback = actual_display_size == requested_display_size &&
  133. actual_refresh_rate == requested_refresh_rate;
  134. timeout_message_with_placeholder =
  135. no_fallback ? l10n_util::GetStringFUTF16(
  136. IDS_ASH_RESOLUTION_REFRESH_CHANGE_DIALOG_CHANGED,
  137. display_name, actual_display_size,
  138. actual_refresh_rate, kTimeoutPlaceHolder)
  139. : l10n_util::GetStringFUTF16(
  140. IDS_ASH_RESOLUTION_REFRESH_CHANGE_DIALOG_FALLBACK,
  141. {display_name, requested_display_size,
  142. requested_refresh_rate, actual_display_size,
  143. actual_refresh_rate, kTimeoutPlaceHolder},
  144. /*offsets=*/nullptr);
  145. } else {
  146. timeout_message_with_placeholder =
  147. actual_display_size == requested_display_size
  148. ? l10n_util::GetStringFUTF16(
  149. IDS_ASH_RESOLUTION_CHANGE_DIALOG_CHANGED, display_name,
  150. actual_display_size, kTimeoutPlaceHolder)
  151. : l10n_util::GetStringFUTF16(
  152. IDS_ASH_RESOLUTION_CHANGE_DIALOG_FALLBACK, display_name,
  153. requested_display_size, actual_display_size,
  154. kTimeoutPlaceHolder);
  155. }
  156. DisplayChangeDialog* dialog = new DisplayChangeDialog(
  157. std::move(dialog_title), std::move(timeout_message_with_placeholder),
  158. base::BindOnce(&ResolutionNotificationController::AcceptResolutionChange,
  159. weak_factory_.GetWeakPtr()),
  160. base::BindOnce(&ResolutionNotificationController::RevertResolutionChange,
  161. weak_factory_.GetWeakPtr()));
  162. confirmation_dialog_ = dialog->GetWeakPtr();
  163. }
  164. void ResolutionNotificationController::AcceptResolutionChange() {
  165. if (!change_info_)
  166. return;
  167. base::OnceClosure callback = std::move(change_info_->accept_callback);
  168. change_info_.reset();
  169. std::move(callback).Run();
  170. }
  171. void ResolutionNotificationController::RevertResolutionChange(
  172. bool display_was_removed) {
  173. if (!change_info_)
  174. return;
  175. const int64_t display_id = change_info_->display_id;
  176. display::ManagedDisplayMode old_resolution = change_info_->old_resolution;
  177. change_info_.reset();
  178. Shell::Get()->screen_layout_observer()->SetDisplayChangedFromSettingsUI(
  179. display_id);
  180. if (display_was_removed) {
  181. // If display was removed then we are inside the stack of
  182. // DisplayManager::UpdateDisplaysWith(), and we need to update the selected
  183. // mode of this removed display without reentering again into
  184. // UpdateDisplaysWith() because this can cause a crash. crbug.com/709722.
  185. Shell::Get()->display_manager()->SetSelectedModeForDisplayId(
  186. display_id, old_resolution);
  187. } else {
  188. Shell::Get()->display_manager()->SetDisplayMode(display_id, old_resolution);
  189. }
  190. }
  191. void ResolutionNotificationController::OnDisplayRemoved(
  192. const display::Display& old_display) {
  193. if (change_info_ && change_info_->display_id == old_display.id()) {
  194. if (confirmation_dialog_)
  195. confirmation_dialog_->GetWidget()->CloseNow();
  196. RevertResolutionChange(true /* display_was_removed */);
  197. }
  198. }
  199. void ResolutionNotificationController::OnDisplayConfigurationChanged() {
  200. if (!change_info_)
  201. return;
  202. display::ManagedDisplayMode mode;
  203. if (Shell::Get()->display_manager()->GetActiveModeForDisplayId(
  204. change_info_->display_id, &mode)) {
  205. change_info_->current_resolution = mode;
  206. }
  207. CreateOrReplaceModalDialog();
  208. }
  209. } // namespace ash