Home | History | Annotate | Download | only in chromeos
      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_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"
     29 
     30 using ash::DisplayManager;
     31 
     32 namespace chromeos {
     33 namespace options {
     34 namespace {
     35 
     36 DisplayManager* GetDisplayManager() {
     37   return ash::Shell::GetInstance()->display_manager();
     38 }
     39 
     40 int64 GetDisplayId(const base::ListValue* args) {
     41   // Assumes the display ID is specified as the first argument.
     42   std::string id_value;
     43   if (!args->GetString(0, &id_value)) {
     44     LOG(ERROR) << "Can't find ID";
     45     return gfx::Display::kInvalidDisplayID;
     46   }
     47 
     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;
     52   }
     53 
     54   return display_id;
     55 }
     56 
     57 base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) {
     58   switch (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:
     72       break;
     73   }
     74 
     75   NOTREACHED();
     76   return base::string16();
     77 }
     78 
     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);
     84 
     85   int result = 0;
     86   dict->GetInteger(field, &result);
     87   return result;
     88 }
     89 
     90 bool GetFloat(const base::DictionaryValue* dict,
     91               const std::string& field,
     92               float* result) {
     93   double double_result = 0;
     94   if (dict->GetDouble(field, &double_result)) {
     95     *result = static_cast<float>(double_result);
     96     return true;
     97   }
     98   return false;
     99 }
    100 
    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.";
    107     return false;
    108   }
    109   if (!GetFloat(dict, "refreshRate", &mode->refresh_rate)) {
    110     LOG(ERROR) << "missing refreshRate.";
    111     return false;
    112   }
    113   if (!GetFloat(dict, "scale", &mode->ui_scale)) {
    114     LOG(ERROR) << "missing ui-scale.";
    115     return false;
    116   }
    117   if (!GetFloat(dict, "deviceScaleFactor", &mode->device_scale_factor)) {
    118     LOG(ERROR) << "missing deviceScaleFactor.";
    119     return false;
    120   }
    121   return true;
    122 }
    123 
    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);
    136   result->SetBoolean(
    137       "isBest", is_internal ? (mode.ui_scale == 1.0f) : mode.native);
    138   result->SetBoolean("isNative", mode.native);
    139   result->SetBoolean(
    140       "selected", mode.IsEquivalent(
    141           GetDisplayManager()->GetActiveModeForDisplayId(display_id)));
    142   return result;
    143 }
    144 
    145 }  // namespace
    146 
    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);
    152 #endif
    153 }
    154 
    155 DisplayOptionsHandler::~DisplayOptionsHandler() {
    156 #if !defined(USE_ATHENA)
    157   // ash::Shell doesn't exist in Athena.
    158   ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
    159 #endif
    160 }
    161 
    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);
    167 
    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));
    180 
    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));
    207 }
    208 
    209 void DisplayOptionsHandler::InitializePage() {
    210   DCHECK(web_ui());
    211 #if !defined(USE_ATHENA)
    212   web_ui()->CallJavascriptFunction(
    213       "options.BrowserOptions.enableDisplayButton",
    214       base::FundamentalValue(true));
    215 #endif
    216 }
    217 
    218 void DisplayOptionsHandler::RegisterMessages() {
    219   web_ui()->RegisterMessageCallback(
    220       "getDisplayInfo",
    221       base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
    222                  base::Unretained(this)));
    223   web_ui()->RegisterMessageCallback(
    224       "setMirroring",
    225       base::Bind(&DisplayOptionsHandler::HandleMirroring,
    226                  base::Unretained(this)));
    227   web_ui()->RegisterMessageCallback(
    228       "setPrimary",
    229       base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
    230                  base::Unretained(this)));
    231   web_ui()->RegisterMessageCallback(
    232       "setDisplayLayout",
    233       base::Bind(&DisplayOptionsHandler::HandleDisplayLayout,
    234                  base::Unretained(this)));
    235   web_ui()->RegisterMessageCallback(
    236       "setDisplayMode",
    237       base::Bind(&DisplayOptionsHandler::HandleSetDisplayMode,
    238                  base::Unretained(this)));
    239   web_ui()->RegisterMessageCallback(
    240       "setOrientation",
    241       base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
    242                  base::Unretained(this)));
    243   web_ui()->RegisterMessageCallback(
    244       "setColorProfile",
    245       base::Bind(&DisplayOptionsHandler::HandleSetColorProfile,
    246                  base::Unretained(this)));
    247 }
    248 
    249 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
    250 }
    251 
    252 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
    253   SendAllDisplayInfo();
    254 }
    255 
    256 void DisplayOptionsHandler::SendAllDisplayInfo() {
    257   DisplayManager* display_manager = GetDisplayManager();
    258 
    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));
    262   }
    263   SendDisplayInfo(displays);
    264 }
    265 
    266 void DisplayOptionsHandler::SendDisplayInfo(
    267     const std::vector<gfx::Display>& displays) {
    268   DisplayManager* display_manager = GetDisplayManager();
    269   base::FundamentalValue mirroring(display_manager->IsMirrored());
    270 
    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()));
    290 
    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]));
    297     }
    298     js_display->Set("resolutions", js_resolutions);
    299 
    300     js_display->SetInteger("colorProfile", display_info.color_profile());
    301     base::ListValue* available_color_profiles = new base::ListValue();
    302     for (size_t i = 0;
    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);
    310     }
    311     js_display->Set("availableColorProfiles", available_color_profiles);
    312     js_displays.Append(js_display);
    313   }
    314 
    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));
    322   }
    323 
    324   web_ui()->CallJavascriptFunction(
    325       "options.DisplayOptions.setDisplayInfo",
    326       mirroring, js_displays, *layout_value.get(), *offset_value.get());
    327 }
    328 
    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.
    332 }
    333 
    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();
    340 }
    341 
    342 void DisplayOptionsHandler::HandleDisplayInfo(
    343     const base::ListValue* unused_args) {
    344   SendAllDisplayInfo();
    345 }
    346 
    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),
    357           is_mirroring));
    358 }
    359 
    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)
    364     return;
    365 
    366   content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
    367   ash::Shell::GetInstance()->display_controller()->
    368       SetPrimaryDisplayId(display_id);
    369 }
    370 
    371 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
    372   double layout = -1;
    373   double offset = -1;
    374   if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) {
    375     LOG(ERROR) << "Invalid parameter";
    376     SendAllDisplayInfo();
    377     return;
    378   }
    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)));
    388 }
    389 
    390 void DisplayOptionsHandler::HandleSetDisplayMode(const base::ListValue* args) {
    391   DCHECK(!args->empty());
    392 
    393   int64 display_id = GetDisplayId(args);
    394   if (display_id == gfx::Display::kInvalidDisplayID)
    395     return;
    396 
    397   const base::DictionaryValue* mode_data = NULL;
    398   if (!args->GetDictionary(1, &mode_data)) {
    399     LOG(ERROR) << "Failed to get mode data";
    400     return;
    401   }
    402 
    403   ash::DisplayMode mode;
    404   if (!ConvertValueToDisplayMode(mode_data, &mode))
    405     return;
    406 
    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()->
    414         PrepareNotification(
    415             display_id, current_mode, mode, base::Bind(&StoreDisplayPrefs));
    416   }
    417 }
    418 
    419 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
    420   DCHECK(!args->empty());
    421 
    422   int64 display_id = GetDisplayId(args);
    423   if (display_id == gfx::Display::kInvalidDisplayID)
    424     return;
    425 
    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";
    430     return;
    431   }
    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";
    440 
    441   content::RecordAction(
    442       base::UserMetricsAction("Options_DisplaySetOrientation"));
    443   GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
    444 }
    445 
    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)
    450     return;
    451 
    452   std::string profile_value;
    453   if (!args->GetString(1, &profile_value)) {
    454     LOG(ERROR) << "Invalid profile_value";
    455     return;
    456   }
    457 
    458   int profile_id;
    459   if (!base::StringToInt(profile_value, &profile_id)) {
    460     LOG(ERROR) << "Invalid profile: " << profile_value;
    461     return;
    462   }
    463 
    464   if (profile_id < ui::COLOR_PROFILE_STANDARD ||
    465       profile_id > ui::COLOR_PROFILE_READING) {
    466     LOG(ERROR) << "Invalid profile_id: " << profile_id;
    467     return;
    468   }
    469 
    470   GetDisplayManager()->SetColorCalibrationProfile(
    471       display_id, static_cast<ui::ColorCalibrationProfile>(profile_id));
    472   SendAllDisplayInfo();
    473 }
    474 
    475 }  // namespace options
    476 }  // namespace chromeos
    477