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.
5 #include "chrome/browser/ui/webui/options/chromeos/display_options_handler.h"
9 #include "ash/display/display_configurator_animation.h"
10 #include "ash/display/display_controller.h"
11 #include "ash/display/display_manager.h"
12 #include "ash/display/resolution_notification_controller.h"
13 #include "ash/shell.h"
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/values.h"
19 #include "chrome/browser/chromeos/display/display_preferences.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "content/public/browser/user_metrics.h"
22 #include "content/public/browser/web_ui.h"
23 #include "grit/ash_strings.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/gfx/display.h"
26 #include "ui/gfx/rect.h"
27 #include "ui/gfx/screen.h"
28 #include "ui/gfx/size_conversions.h"
30 using ash::DisplayManager;
36 DisplayManager* GetDisplayManager() {
37 return ash::Shell::GetInstance()->display_manager();
40 int64 GetDisplayId(const base::ListValue* args) {
41 // Assumes the display ID is specified as the first argument.
43 if (!args->GetString(0, &id_value)) {
44 LOG(ERROR) << "Can't find ID";
45 return gfx::Display::kInvalidDisplayID;
48 int64 display_id = gfx::Display::kInvalidDisplayID;
49 if (!base::StringToInt64(id_value, &display_id)) {
50 LOG(ERROR) << "Invalid display id: " << id_value;
51 return gfx::Display::kInvalidDisplayID;
57 base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) {
59 case ui::COLOR_PROFILE_STANDARD:
60 return l10n_util::GetStringUTF16(
61 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD);
62 case ui::COLOR_PROFILE_DYNAMIC:
63 return l10n_util::GetStringUTF16(
64 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC);
65 case ui::COLOR_PROFILE_MOVIE:
66 return l10n_util::GetStringUTF16(
67 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE);
68 case ui::COLOR_PROFILE_READING:
69 return l10n_util::GetStringUTF16(
70 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING);
71 case ui::NUM_COLOR_PROFILES:
76 return base::string16();
79 int GetIntOrDouble(const base::DictionaryValue* dict,
80 const std::string& field) {
81 double double_result = 0;
82 if (dict->GetDouble(field, &double_result))
83 return static_cast<int>(double_result);
86 dict->GetInteger(field, &result);
90 bool GetFloat(const base::DictionaryValue* dict,
91 const std::string& field,
93 double double_result = 0;
94 if (dict->GetDouble(field, &double_result)) {
95 *result = static_cast<float>(double_result);
101 bool ConvertValueToDisplayMode(const base::DictionaryValue* dict,
102 ash::DisplayMode* mode) {
103 mode->size.set_width(GetIntOrDouble(dict, "originalWidth"));
104 mode->size.set_height(GetIntOrDouble(dict, "originalHeight"));
105 if (mode->size.IsEmpty()) {
106 LOG(ERROR) << "missing width or height.";
109 if (!GetFloat(dict, "refreshRate", &mode->refresh_rate)) {
110 LOG(ERROR) << "missing refreshRate.";
113 if (!GetFloat(dict, "scale", &mode->ui_scale)) {
114 LOG(ERROR) << "missing ui-scale.";
117 if (!GetFloat(dict, "deviceScaleFactor", &mode->device_scale_factor)) {
118 LOG(ERROR) << "missing deviceScaleFactor.";
124 base::DictionaryValue* ConvertDisplayModeToValue(int64 display_id,
125 const ash::DisplayMode& mode) {
126 bool is_internal = GetDisplayManager()->IsInternalDisplayId(display_id);
127 base::DictionaryValue* result = new base::DictionaryValue();
128 gfx::Size size_dip = mode.GetSizeInDIP();
129 result->SetInteger("width", size_dip.width());
130 result->SetInteger("height", size_dip.height());
131 result->SetInteger("originalWidth", mode.size.width());
132 result->SetInteger("originalHeight", mode.size.height());
133 result->SetDouble("deviceScaleFactor", mode.device_scale_factor);
134 result->SetDouble("scale", mode.ui_scale);
135 result->SetDouble("refreshRate", mode.refresh_rate);
137 "isBest", is_internal ? (mode.ui_scale == 1.0f) : mode.native);
138 result->SetBoolean("isNative", mode.native);
140 "selected", mode.IsEquivalent(
141 GetDisplayManager()->GetActiveModeForDisplayId(display_id)));
147 DisplayOptionsHandler::DisplayOptionsHandler() {
148 #if !defined(USE_ATHENA)
149 // ash::Shell doesn't exist in Athena.
150 // See: http://crbug.com/416961
151 ash::Shell::GetInstance()->display_controller()->AddObserver(this);
155 DisplayOptionsHandler::~DisplayOptionsHandler() {
156 #if !defined(USE_ATHENA)
157 // ash::Shell doesn't exist in Athena.
158 ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
162 void DisplayOptionsHandler::GetLocalizedValues(
163 base::DictionaryValue* localized_strings) {
164 DCHECK(localized_strings);
165 RegisterTitle(localized_strings, "displayOptionsPage",
166 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE);
168 localized_strings->SetString(
169 "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
170 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS));
171 localized_strings->SetString(
172 "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
173 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION));
174 localized_strings->SetString(
175 "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
176 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION));
177 localized_strings->SetString(
178 "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
179 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN));
181 localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16(
182 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING));
183 localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16(
184 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING));
185 localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
186 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME));
187 localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16(
188 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY));
189 localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16(
190 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST));
191 localized_strings->SetString("annotateNative", l10n_util::GetStringUTF16(
192 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_NATIVE));
193 localized_strings->SetString("orientation0", l10n_util::GetStringUTF16(
194 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION));
195 localized_strings->SetString("orientation90", l10n_util::GetStringUTF16(
196 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90));
197 localized_strings->SetString("orientation180", l10n_util::GetStringUTF16(
198 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180));
199 localized_strings->SetString("orientation270", l10n_util::GetStringUTF16(
200 IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270));
201 localized_strings->SetString(
202 "startCalibratingOverscan", l10n_util::GetStringUTF16(
203 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN));
204 localized_strings->SetString(
205 "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
206 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE));
209 void DisplayOptionsHandler::InitializePage() {
211 #if !defined(USE_ATHENA)
212 web_ui()->CallJavascriptFunction(
213 "options.BrowserOptions.enableDisplayButton",
214 base::FundamentalValue(true));
218 void DisplayOptionsHandler::RegisterMessages() {
219 web_ui()->RegisterMessageCallback(
221 base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
222 base::Unretained(this)));
223 web_ui()->RegisterMessageCallback(
225 base::Bind(&DisplayOptionsHandler::HandleMirroring,
226 base::Unretained(this)));
227 web_ui()->RegisterMessageCallback(
229 base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
230 base::Unretained(this)));
231 web_ui()->RegisterMessageCallback(
233 base::Bind(&DisplayOptionsHandler::HandleDisplayLayout,
234 base::Unretained(this)));
235 web_ui()->RegisterMessageCallback(
237 base::Bind(&DisplayOptionsHandler::HandleSetDisplayMode,
238 base::Unretained(this)));
239 web_ui()->RegisterMessageCallback(
241 base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
242 base::Unretained(this)));
243 web_ui()->RegisterMessageCallback(
245 base::Bind(&DisplayOptionsHandler::HandleSetColorProfile,
246 base::Unretained(this)));
249 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
252 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
253 SendAllDisplayInfo();
256 void DisplayOptionsHandler::SendAllDisplayInfo() {
257 DisplayManager* display_manager = GetDisplayManager();
259 std::vector<gfx::Display> displays;
260 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
261 displays.push_back(display_manager->GetDisplayAt(i));
263 SendDisplayInfo(displays);
266 void DisplayOptionsHandler::SendDisplayInfo(
267 const std::vector<gfx::Display>& displays) {
268 DisplayManager* display_manager = GetDisplayManager();
269 base::FundamentalValue mirroring(display_manager->IsMirrored());
271 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
272 base::ListValue js_displays;
273 for (size_t i = 0; i < displays.size(); ++i) {
274 const gfx::Display& display = displays[i];
275 const ash::DisplayInfo& display_info =
276 display_manager->GetDisplayInfo(display.id());
277 const gfx::Rect& bounds = display.bounds();
278 base::DictionaryValue* js_display = new base::DictionaryValue();
279 js_display->SetString("id", base::Int64ToString(display.id()));
280 js_display->SetInteger("x", bounds.x());
281 js_display->SetInteger("y", bounds.y());
282 js_display->SetInteger("width", bounds.width());
283 js_display->SetInteger("height", bounds.height());
284 js_display->SetString("name",
285 display_manager->GetDisplayNameForId(display.id()));
286 js_display->SetBoolean("isPrimary", display.id() == primary_id);
287 js_display->SetBoolean("isInternal", display.IsInternal());
288 js_display->SetInteger("orientation",
289 static_cast<int>(display_info.rotation()));
291 base::ListValue* js_resolutions = new base::ListValue();
292 const std::vector<ash::DisplayMode>& display_modes =
293 display_info.display_modes();
294 for (size_t i = 0; i < display_modes.size(); ++i) {
295 js_resolutions->Append(
296 ConvertDisplayModeToValue(display.id(), display_modes[i]));
298 js_display->Set("resolutions", js_resolutions);
300 js_display->SetInteger("colorProfile", display_info.color_profile());
301 base::ListValue* available_color_profiles = new base::ListValue();
303 i < display_info.available_color_profiles().size(); ++i) {
304 base::DictionaryValue* color_profile_dict = new base::DictionaryValue();
305 ui::ColorCalibrationProfile color_profile =
306 display_info.available_color_profiles()[i];
307 color_profile_dict->SetInteger("profileId", color_profile);
308 color_profile_dict->SetString("name", GetColorProfileName(color_profile));
309 available_color_profiles->Append(color_profile_dict);
311 js_display->Set("availableColorProfiles", available_color_profiles);
312 js_displays.Append(js_display);
315 scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue());
316 scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue());
317 if (display_manager->GetNumDisplays() > 1) {
318 const ash::DisplayLayout layout =
319 display_manager->GetCurrentDisplayLayout();
320 layout_value.reset(new base::FundamentalValue(layout.position));
321 offset_value.reset(new base::FundamentalValue(layout.offset));
324 web_ui()->CallJavascriptFunction(
325 "options.DisplayOptions.setDisplayInfo",
326 mirroring, js_displays, *layout_value.get(), *offset_value.get());
329 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) {
330 ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring);
331 // Not necessary to start fade-in animation. DisplayConfigurator will do that.
334 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
335 int position, int offset) {
336 SetCurrentDisplayLayout(
337 ash::DisplayLayout::FromInts(position, offset));
338 ash::Shell::GetInstance()->display_configurator_animation()->
339 StartFadeInAnimation();
342 void DisplayOptionsHandler::HandleDisplayInfo(
343 const base::ListValue* unused_args) {
344 SendAllDisplayInfo();
347 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) {
348 DCHECK(!args->empty());
349 content::RecordAction(
350 base::UserMetricsAction("Options_DisplayToggleMirroring"));
351 bool is_mirroring = false;
352 args->GetBoolean(0, &is_mirroring);
353 ash::Shell::GetInstance()->display_configurator_animation()->
354 StartFadeOutAnimation(base::Bind(
355 &DisplayOptionsHandler::OnFadeOutForMirroringFinished,
356 base::Unretained(this),
360 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) {
361 DCHECK(!args->empty());
362 int64 display_id = GetDisplayId(args);
363 if (display_id == gfx::Display::kInvalidDisplayID)
366 content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
367 ash::Shell::GetInstance()->display_controller()->
368 SetPrimaryDisplayId(display_id);
371 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
374 if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) {
375 LOG(ERROR) << "Invalid parameter";
376 SendAllDisplayInfo();
379 DCHECK_LE(ash::DisplayLayout::TOP, layout);
380 DCHECK_GE(ash::DisplayLayout::LEFT, layout);
381 content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange"));
382 ash::Shell::GetInstance()->display_configurator_animation()->
383 StartFadeOutAnimation(base::Bind(
384 &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished,
385 base::Unretained(this),
386 static_cast<int>(layout),
387 static_cast<int>(offset)));
390 void DisplayOptionsHandler::HandleSetDisplayMode(const base::ListValue* args) {
391 DCHECK(!args->empty());
393 int64 display_id = GetDisplayId(args);
394 if (display_id == gfx::Display::kInvalidDisplayID)
397 const base::DictionaryValue* mode_data = NULL;
398 if (!args->GetDictionary(1, &mode_data)) {
399 LOG(ERROR) << "Failed to get mode data";
403 ash::DisplayMode mode;
404 if (!ConvertValueToDisplayMode(mode_data, &mode))
407 content::RecordAction(
408 base::UserMetricsAction("Options_DisplaySetResolution"));
409 ash::DisplayManager* display_manager = GetDisplayManager();
410 ash::DisplayMode current_mode =
411 display_manager->GetActiveModeForDisplayId(display_id);
412 if (display_manager->SetDisplayMode(display_id, mode)) {
413 ash::Shell::GetInstance()->resolution_notification_controller()->
415 display_id, current_mode, mode, base::Bind(&StoreDisplayPrefs));
419 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
420 DCHECK(!args->empty());
422 int64 display_id = GetDisplayId(args);
423 if (display_id == gfx::Display::kInvalidDisplayID)
426 std::string rotation_value;
427 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0;
428 if (!args->GetString(1, &rotation_value)) {
429 LOG(ERROR) << "Can't find new orientation";
432 if (rotation_value == "90")
433 new_rotation = gfx::Display::ROTATE_90;
434 else if (rotation_value == "180")
435 new_rotation = gfx::Display::ROTATE_180;
436 else if (rotation_value == "270")
437 new_rotation = gfx::Display::ROTATE_270;
438 else if (rotation_value != "0")
439 LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";
441 content::RecordAction(
442 base::UserMetricsAction("Options_DisplaySetOrientation"));
443 GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
446 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) {
447 DCHECK(!args->empty());
448 int64 display_id = GetDisplayId(args);
449 if (display_id == gfx::Display::kInvalidDisplayID)
452 std::string profile_value;
453 if (!args->GetString(1, &profile_value)) {
454 LOG(ERROR) << "Invalid profile_value";
459 if (!base::StringToInt(profile_value, &profile_id)) {
460 LOG(ERROR) << "Invalid profile: " << profile_value;
464 if (profile_id < ui::COLOR_PROFILE_STANDARD ||
465 profile_id > ui::COLOR_PROFILE_READING) {
466 LOG(ERROR) << "Invalid profile_id: " << profile_id;
470 GetDisplayManager()->SetColorCalibrationProfile(
471 display_id, static_cast<ui::ColorCalibrationProfile>(profile_id));
472 SendAllDisplayInfo();
475 } // namespace options
476 } // namespace chromeos