1 // Copyright (c) 2012 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
5 #include "chrome/browser/ui/webui/options/chromeos/display_options_handler.h"
6
7 #include <string>
8
9 #include "ash/display/display_controller.h"
10 #include "ash/display/display_manager.h"
11 #include "ash/display/output_configurator_animation.h"
12 #include "ash/display/resolution_notification_controller.h"
13 #include "ash/screen_ash.h"
14 #include "ash/shell.h"
15 #include "base/bind.h"
16 #include "base/logging.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/values.h"
20 #include "chrome/browser/chromeos/display/display_preferences.h"
21 #include "chromeos/display/output_configurator.h"
22 #include "content/public/browser/web_ui.h"
23 #include "grit/ash_strings.h"
24 #include "grit/generated_resources.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/gfx/display.h"
27 #include "ui/gfx/rect.h"
28 #include "ui/gfx/screen.h"
29 #include "ui/gfx/size_conversions.h"
30
31 using ash::internal::DisplayManager;
32
33 namespace chromeos {
34 namespace options {
35 namespace {
36
GetDisplayManager()37 DisplayManager* GetDisplayManager() {
38 return ash::Shell::GetInstance()->display_manager();
39 }
40
GetDisplayId(const base::ListValue * args)41 int64 GetDisplayId(const base::ListValue* args) {
42 // Assumes the display ID is specified as the first argument.
43 std::string id_value;
44 if (!args->GetString(0, &id_value)) {
45 LOG(ERROR) << "Can't find ID";
46 return gfx::Display::kInvalidDisplayID;
47 }
48
49 int64 display_id = gfx::Display::kInvalidDisplayID;
50 if (!base::StringToInt64(id_value, &display_id)) {
51 LOG(ERROR) << "Invalid display id: " << id_value;
52 return gfx::Display::kInvalidDisplayID;
53 }
54
55 return display_id;
56 }
57
CompareResolution(ash::internal::Resolution r1,ash::internal::Resolution r2)58 bool CompareResolution(ash::internal::Resolution r1,
59 ash::internal::Resolution r2) {
60 return r1.size.GetArea() < r2.size.GetArea();
61 }
62
63 } // namespace
64
DisplayOptionsHandler()65 DisplayOptionsHandler::DisplayOptionsHandler() {
66 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
67 }
68
~DisplayOptionsHandler()69 DisplayOptionsHandler::~DisplayOptionsHandler() {
70 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
71 }
72
GetLocalizedValues(DictionaryValue * localized_strings)73 void DisplayOptionsHandler::GetLocalizedValues(
74 DictionaryValue* localized_strings) {
75 DCHECK(localized_strings);
76 RegisterTitle(localized_strings, "displayOptionsPage",
77 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE);
78
79 localized_strings->SetString(
80 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
81 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS));
82 localized_strings->SetString(
83 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
84 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION));
85 localized_strings->SetString(
86 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
87 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION));
88 localized_strings->SetString(
89 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
90 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN));
91
92 localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16(
93 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING));
94 localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16(
95 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING));
96 localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
97 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME));
98 localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16(
99 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY));
100 localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16(
101 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST));
102 localized_strings->SetString("orientation0", l10n_util::GetStringUTF16(
103 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION));
104 localized_strings->SetString("orientation90", l10n_util::GetStringUTF16(
105 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90));
106 localized_strings->SetString("orientation180", l10n_util::GetStringUTF16(
107 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180));
108 localized_strings->SetString("orientation270", l10n_util::GetStringUTF16(
109 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270));
110 localized_strings->SetString(
111 "startCalibratingOverscan", l10n_util::GetStringUTF16(
112 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN));
113 }
114
InitializePage()115 void DisplayOptionsHandler::InitializePage() {
116 DCHECK(web_ui());
117 }
118
RegisterMessages()119 void DisplayOptionsHandler::RegisterMessages() {
120 web_ui()->RegisterMessageCallback(
121 "getDisplayInfo",
122 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
123 base::Unretained(this)));
124 web_ui()->RegisterMessageCallback(
125 "setMirroring",
126 base::Bind(&DisplayOptionsHandler::HandleMirroring,
127 base::Unretained(this)));
128 web_ui()->RegisterMessageCallback(
129 "setPrimary",
130 base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
131 base::Unretained(this)));
132 web_ui()->RegisterMessageCallback(
133 "setDisplayLayout",
134 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout,
135 base::Unretained(this)));
136 web_ui()->RegisterMessageCallback(
137 "setUIScale",
138 base::Bind(&DisplayOptionsHandler::HandleSetUIScale,
139 base::Unretained(this)));
140 web_ui()->RegisterMessageCallback(
141 "setResolution",
142 base::Bind(&DisplayOptionsHandler::HandleSetResolution,
143 base::Unretained(this)));
144 web_ui()->RegisterMessageCallback(
145 "setOrientation",
146 base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
147 base::Unretained(this)));
148 }
149
OnDisplayConfigurationChanging()150 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
151 }
152
OnDisplayConfigurationChanged()153 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
154 SendAllDisplayInfo();
155 }
156
SendAllDisplayInfo()157 void DisplayOptionsHandler::SendAllDisplayInfo() {
158 DisplayManager* display_manager = GetDisplayManager();
159
160 std::vector<gfx::Display> displays;
161 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
162 displays.push_back(display_manager->GetDisplayAt(i));
163 }
164 SendDisplayInfo(displays);
165 }
166
SendDisplayInfo(const std::vector<gfx::Display> & displays)167 void DisplayOptionsHandler::SendDisplayInfo(
168 const std::vector<gfx::Display>& displays) {
169 DisplayManager* display_manager = GetDisplayManager();
170 base::FundamentalValue mirroring(display_manager->IsMirrored());
171
172 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
173 base::ListValue js_displays;
174 for (size_t i = 0; i < displays.size(); ++i) {
175 const gfx::Display& display = displays[i];
176 const ash::internal::DisplayInfo& display_info =
177 display_manager->GetDisplayInfo(display.id());
178 const gfx::Rect& bounds = display.bounds();
179 base::DictionaryValue* js_display = new base::DictionaryValue();
180 js_display->SetString("id", base::Int64ToString(display.id()));
181 js_display->SetInteger("x", bounds.x());
182 js_display->SetInteger("y", bounds.y());
183 js_display->SetInteger("width", bounds.width());
184 js_display->SetInteger("height", bounds.height());
185 js_display->SetString("name",
186 display_manager->GetDisplayNameForId(display.id()));
187 js_display->SetBoolean("isPrimary", display.id() == primary_id);
188 js_display->SetBoolean("isInternal", display.IsInternal());
189 js_display->SetInteger("orientation",
190 static_cast<int>(display_info.rotation()));
191 std::vector<ash::internal::Resolution> resolutions;
192 std::vector<float> ui_scales;
193 if (display.IsInternal()) {
194 ui_scales = DisplayManager::GetScalesForDisplay(display_info);
195 gfx::SizeF base_size = display_info.bounds_in_native().size();
196 base_size.Scale(1.0f / display.device_scale_factor());
197 if (display_info.rotation() == gfx::Display::ROTATE_90 ||
198 display_info.rotation() == gfx::Display::ROTATE_270) {
199 float tmp = base_size.width();
200 base_size.set_width(base_size.height());
201 base_size.set_height(tmp);
202 }
203 for (size_t i = 0; i < ui_scales.size(); ++i) {
204 gfx::SizeF new_size = base_size;
205 new_size.Scale(ui_scales[i]);
206 resolutions.push_back(ash::internal::Resolution(
207 gfx::ToFlooredSize(new_size), false /* interlaced */));
208 }
209 } else {
210 for (size_t i = 0; i < display_info.resolutions().size(); ++i)
211 resolutions.push_back(display_info.resolutions()[i]);
212 }
213 std::sort(resolutions.begin(), resolutions.end(), CompareResolution);
214
215 base::ListValue* js_resolutions = new base::ListValue();
216 gfx::Size current_size = display_info.bounds_in_native().size();
217 gfx::Insets current_overscan = display_info.GetOverscanInsetsInPixel();
218 for (size_t i = 0; i < resolutions.size(); ++i) {
219 base::DictionaryValue* resolution_info = new base::DictionaryValue();
220 gfx::Size resolution = resolutions[i].size;
221 if (!ui_scales.empty()) {
222 resolution_info->SetDouble("scale", ui_scales[i]);
223 if (ui_scales[i] == 1.0f)
224 resolution_info->SetBoolean("isBest", true);
225 resolution_info->SetBoolean(
226 "selected", display_info.configured_ui_scale() == ui_scales[i]);
227 } else {
228 // Picks the largest one as the "best", which is the last element
229 // because |resolutions| is sorted by its area.
230 if (i == resolutions.size() - 1)
231 resolution_info->SetBoolean("isBest", true);
232 resolution_info->SetBoolean("selected", (resolution == current_size));
233 resolution.Enlarge(
234 -current_overscan.width(), -current_overscan.height());
235 }
236 resolution_info->SetInteger("width", resolution.width());
237 resolution_info->SetInteger("height", resolution.height());
238 js_resolutions->Append(resolution_info);
239 }
240 js_display->Set("resolutions", js_resolutions);
241 js_displays.Append(js_display);
242 }
243
244 scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue());
245 scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue());
246 if (display_manager->GetNumDisplays() > 1) {
247 const ash::DisplayLayout layout =
248 display_manager->GetCurrentDisplayLayout();
249 layout_value.reset(new base::FundamentalValue(layout.position));
250 offset_value.reset(new base::FundamentalValue(layout.offset));
251 }
252
253 web_ui()->CallJavascriptFunction(
254 "options.DisplayOptions.setDisplayInfo",
255 mirroring, js_displays, *layout_value.get(), *offset_value.get());
256 }
257
OnFadeOutForMirroringFinished(bool is_mirroring)258 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) {
259 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring);
260 // Not necessary to start fade-in animation. OutputConfigurator will do that.
261 }
262
OnFadeOutForDisplayLayoutFinished(int position,int offset)263 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
264 int position, int offset) {
265 SetCurrentDisplayLayout(
266 ash::DisplayLayout::FromInts(position, offset));
267 ash::Shell::GetInstance()->output_configurator_animation()->
268 StartFadeInAnimation();
269 }
270
HandleDisplayInfo(const base::ListValue * unused_args)271 void DisplayOptionsHandler::HandleDisplayInfo(
272 const base::ListValue* unused_args) {
273 SendAllDisplayInfo();
274 }
275
HandleMirroring(const base::ListValue * args)276 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) {
277 DCHECK(!args->empty());
278 bool is_mirroring = false;
279 args->GetBoolean(0, &is_mirroring);
280 ash::Shell::GetInstance()->output_configurator_animation()->
281 StartFadeOutAnimation(base::Bind(
282 &DisplayOptionsHandler::OnFadeOutForMirroringFinished,
283 base::Unretained(this),
284 is_mirroring));
285 }
286
HandleSetPrimary(const base::ListValue * args)287 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) {
288 DCHECK(!args->empty());
289 int64 display_id = GetDisplayId(args);
290 if (display_id == gfx::Display::kInvalidDisplayID)
291 return;
292
293 ash::Shell::GetInstance()->display_controller()->
294 SetPrimaryDisplayId(display_id);
295 }
296
HandleDisplayLayout(const base::ListValue * args)297 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
298 double layout = -1;
299 double offset = -1;
300 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) {
301 LOG(ERROR) << "Invalid parameter";
302 SendAllDisplayInfo();
303 return;
304 }
305 DCHECK_LE(ash::DisplayLayout::TOP, layout);
306 DCHECK_GE(ash::DisplayLayout::LEFT, layout);
307 ash::Shell::GetInstance()->output_configurator_animation()->
308 StartFadeOutAnimation(base::Bind(
309 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished,
310 base::Unretained(this),
311 static_cast<int>(layout),
312 static_cast<int>(offset)));
313 }
314
HandleSetUIScale(const base::ListValue * args)315 void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) {
316 DCHECK(!args->empty());
317
318 int64 display_id = GetDisplayId(args);
319 if (display_id == gfx::Display::kInvalidDisplayID)
320 return;
321
322 double ui_scale = 0.0f;
323 if (!args->GetDouble(1, &ui_scale) || ui_scale == 0.0f) {
324 LOG(ERROR) << "Can't find new ui_scale";
325 return;
326 }
327
328 GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale);
329 }
330
HandleSetResolution(const base::ListValue * args)331 void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) {
332 DCHECK(!args->empty());
333 int64 display_id = GetDisplayId(args);
334 if (display_id == gfx::Display::kInvalidDisplayID)
335 return;
336
337 double width = 0.0f;
338 double height = 0.0f;
339 if (!args->GetDouble(1, &width) || width == 0.0f) {
340 LOG(ERROR) << "Can't find new width";
341 return;
342 }
343 if (!args->GetDouble(2, &height) || height == 0.0f) {
344 LOG(ERROR) << "Can't find new height";
345 return;
346 }
347
348 const ash::internal::DisplayInfo& display_info =
349 GetDisplayManager()->GetDisplayInfo(display_id);
350 gfx::Insets current_overscan = display_info.GetOverscanInsetsInPixel();
351 gfx::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, height));
352 new_resolution.Enlarge(current_overscan.width(), current_overscan.height());
353 gfx::Size old_resolution = display_info.bounds_in_native().size();
354 bool has_new_resolution = false;
355 bool has_old_resolution = false;
356 for (size_t i = 0; i < display_info.resolutions().size(); ++i) {
357 ash::internal::Resolution resolution = display_info.resolutions()[i];
358 if (resolution.size == new_resolution)
359 has_new_resolution = true;
360 if (resolution.size == old_resolution)
361 has_old_resolution = true;
362 }
363 if (!has_new_resolution) {
364 LOG(ERROR) << "No new resolution " << new_resolution.ToString()
365 << " is found in the display info " << display_info.ToString();
366 return;
367 }
368 if (!has_old_resolution) {
369 LOG(ERROR) << "No old resolution " << old_resolution.ToString()
370 << " is found in the display info " << display_info.ToString();
371 return;
372 }
373
374 ash::Shell::GetInstance()->resolution_notification_controller()->
375 SetDisplayResolutionAndNotify(
376 display_id, old_resolution, new_resolution,
377 base::Bind(&StoreDisplayPrefs));
378 }
379
HandleSetOrientation(const base::ListValue * args)380 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
381 DCHECK(!args->empty());
382
383 int64 display_id = GetDisplayId(args);
384 if (display_id == gfx::Display::kInvalidDisplayID)
385 return;
386
387 std::string rotation_value;
388 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0;
389 if (!args->GetString(1, &rotation_value)) {
390 LOG(ERROR) << "Can't find new orientation";
391 return;
392 }
393 if (rotation_value == "90")
394 new_rotation = gfx::Display::ROTATE_90;
395 else if (rotation_value == "180")
396 new_rotation = gfx::Display::ROTATE_180;
397 else if (rotation_value == "270")
398 new_rotation = gfx::Display::ROTATE_270;
399 else if (rotation_value != "0")
400 LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";
401
402 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
403 }
404
405 } // namespace options
406 } // namespace chromeos
407