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 "content/public/browser/user_metrics.h"
     21 #include "content/public/browser/web_ui.h"
     22 #include "grit/ash_strings.h"
     23 #include "grit/generated_resources.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 bool CompareDisplayMode(ash::DisplayMode d1, ash::DisplayMode d2) {
     58   if (d1.size.GetArea() == d2.size.GetArea())
     59     return d1.refresh_rate < d2.refresh_rate;
     60   return d1.size.GetArea() < d2.size.GetArea();
     61 }
     62 
     63 base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) {
     64   switch (profile) {
     65     case ui::COLOR_PROFILE_STANDARD:
     66       return l10n_util::GetStringUTF16(
     67           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD);
     68     case ui::COLOR_PROFILE_DYNAMIC:
     69       return l10n_util::GetStringUTF16(
     70           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC);
     71     case ui::COLOR_PROFILE_MOVIE:
     72       return l10n_util::GetStringUTF16(
     73           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE);
     74     case ui::COLOR_PROFILE_READING:
     75       return l10n_util::GetStringUTF16(
     76           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING);
     77     case ui::NUM_COLOR_PROFILES:
     78       break;
     79   }
     80 
     81   NOTREACHED();
     82   return base::string16();
     83 }
     84 
     85 }  // namespace
     86 
     87 DisplayOptionsHandler::DisplayOptionsHandler() {
     88   ash::Shell::GetInstance()->display_controller()->AddObserver(this);
     89 }
     90 
     91 DisplayOptionsHandler::~DisplayOptionsHandler() {
     92   ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
     93 }
     94 
     95 void DisplayOptionsHandler::GetLocalizedValues(
     96     base::DictionaryValue* localized_strings) {
     97   DCHECK(localized_strings);
     98   RegisterTitle(localized_strings, "displayOptionsPage",
     99                 IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE);
    100 
    101   localized_strings->SetString(
    102       "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
    103           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS));
    104   localized_strings->SetString(
    105       "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
    106           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION));
    107   localized_strings->SetString(
    108       "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
    109           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION));
    110   localized_strings->SetString(
    111       "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
    112           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN));
    113 
    114   localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16(
    115       IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING));
    116   localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16(
    117       IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING));
    118   localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
    119       IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME));
    120   localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16(
    121       IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY));
    122   localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16(
    123       IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST));
    124   localized_strings->SetString("orientation0", l10n_util::GetStringUTF16(
    125       IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION));
    126   localized_strings->SetString("orientation90", l10n_util::GetStringUTF16(
    127       IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90));
    128   localized_strings->SetString("orientation180", l10n_util::GetStringUTF16(
    129       IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180));
    130   localized_strings->SetString("orientation270", l10n_util::GetStringUTF16(
    131       IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270));
    132   localized_strings->SetString(
    133       "startCalibratingOverscan", l10n_util::GetStringUTF16(
    134           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN));
    135   localized_strings->SetString(
    136       "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
    137           IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE));
    138 }
    139 
    140 void DisplayOptionsHandler::InitializePage() {
    141   DCHECK(web_ui());
    142 }
    143 
    144 void DisplayOptionsHandler::RegisterMessages() {
    145   web_ui()->RegisterMessageCallback(
    146       "getDisplayInfo",
    147       base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
    148                  base::Unretained(this)));
    149   web_ui()->RegisterMessageCallback(
    150       "setMirroring",
    151       base::Bind(&DisplayOptionsHandler::HandleMirroring,
    152                  base::Unretained(this)));
    153   web_ui()->RegisterMessageCallback(
    154       "setPrimary",
    155       base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
    156                  base::Unretained(this)));
    157   web_ui()->RegisterMessageCallback(
    158       "setDisplayLayout",
    159       base::Bind(&DisplayOptionsHandler::HandleDisplayLayout,
    160                  base::Unretained(this)));
    161   web_ui()->RegisterMessageCallback(
    162       "setUIScale",
    163       base::Bind(&DisplayOptionsHandler::HandleSetUIScale,
    164                  base::Unretained(this)));
    165   web_ui()->RegisterMessageCallback(
    166       "setResolution",
    167       base::Bind(&DisplayOptionsHandler::HandleSetResolution,
    168                  base::Unretained(this)));
    169   web_ui()->RegisterMessageCallback(
    170       "setOrientation",
    171       base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
    172                  base::Unretained(this)));
    173   web_ui()->RegisterMessageCallback(
    174       "setColorProfile",
    175       base::Bind(&DisplayOptionsHandler::HandleSetColorProfile,
    176                  base::Unretained(this)));
    177 }
    178 
    179 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
    180 }
    181 
    182 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
    183   SendAllDisplayInfo();
    184 }
    185 
    186 void DisplayOptionsHandler::SendAllDisplayInfo() {
    187   DisplayManager* display_manager = GetDisplayManager();
    188 
    189   std::vector<gfx::Display> displays;
    190   for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
    191     displays.push_back(display_manager->GetDisplayAt(i));
    192   }
    193   SendDisplayInfo(displays);
    194 }
    195 
    196 void DisplayOptionsHandler::SendDisplayInfo(
    197     const std::vector<gfx::Display>& displays) {
    198   DisplayManager* display_manager = GetDisplayManager();
    199   base::FundamentalValue mirroring(display_manager->IsMirrored());
    200 
    201   int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
    202   base::ListValue js_displays;
    203   for (size_t i = 0; i < displays.size(); ++i) {
    204     const gfx::Display& display = displays[i];
    205     const ash::DisplayInfo& display_info =
    206         display_manager->GetDisplayInfo(display.id());
    207     const gfx::Rect& bounds = display.bounds();
    208     base::DictionaryValue* js_display = new base::DictionaryValue();
    209     js_display->SetString("id", base::Int64ToString(display.id()));
    210     js_display->SetInteger("x", bounds.x());
    211     js_display->SetInteger("y", bounds.y());
    212     js_display->SetInteger("width", bounds.width());
    213     js_display->SetInteger("height", bounds.height());
    214     js_display->SetString("name",
    215                           display_manager->GetDisplayNameForId(display.id()));
    216     js_display->SetBoolean("isPrimary", display.id() == primary_id);
    217     js_display->SetBoolean("isInternal", display.IsInternal());
    218     js_display->SetInteger("orientation",
    219                            static_cast<int>(display_info.rotation()));
    220     std::vector<ash::DisplayMode> display_modes;
    221     std::vector<float> ui_scales;
    222     if (display.IsInternal()) {
    223       ui_scales = DisplayManager::GetScalesForDisplay(display_info);
    224       gfx::SizeF base_size = display_info.bounds_in_native().size();
    225       base_size.Scale(1.0f / display_info.device_scale_factor());
    226       if (display_info.rotation() == gfx::Display::ROTATE_90 ||
    227           display_info.rotation() == gfx::Display::ROTATE_270) {
    228         float tmp = base_size.width();
    229         base_size.set_width(base_size.height());
    230         base_size.set_height(tmp);
    231       }
    232       for (size_t i = 0; i < ui_scales.size(); ++i) {
    233         gfx::SizeF new_size = base_size;
    234         new_size.Scale(ui_scales[i]);
    235         display_modes.push_back(ash::DisplayMode(
    236             gfx::ToFlooredSize(new_size), -1.0f, false, false));
    237       }
    238     } else {
    239       for (size_t i = 0; i < display_info.display_modes().size(); ++i)
    240         display_modes.push_back(display_info.display_modes()[i]);
    241     }
    242     std::sort(display_modes.begin(), display_modes.end(), CompareDisplayMode);
    243 
    244     base::ListValue* js_resolutions = new base::ListValue();
    245     gfx::Size current_size = display_info.bounds_in_native().size();
    246     for (size_t i = 0; i < display_modes.size(); ++i) {
    247       base::DictionaryValue* resolution_info = new base::DictionaryValue();
    248       gfx::Size resolution = display_modes[i].size;
    249       if (!ui_scales.empty()) {
    250         resolution_info->SetDouble("scale", ui_scales[i]);
    251         if (ui_scales[i] == 1.0f)
    252           resolution_info->SetBoolean("isBest", true);
    253         resolution_info->SetBoolean(
    254             "selected", display_info.configured_ui_scale() == ui_scales[i]);
    255       } else {
    256         // Picks the largest one as the "best", which is the last element
    257         // because |display_modes| is sorted by its area.
    258         if (i == display_modes.size() - 1)
    259           resolution_info->SetBoolean("isBest", true);
    260         resolution_info->SetBoolean("selected", (resolution == current_size));
    261       }
    262       resolution_info->SetInteger("width", resolution.width());
    263       resolution_info->SetInteger("height", resolution.height());
    264       if (display_modes[i].refresh_rate > 0.0f) {
    265         resolution_info->SetDouble("refreshRate",
    266                                    display_modes[i].refresh_rate);
    267       }
    268       js_resolutions->Append(resolution_info);
    269     }
    270     js_display->Set("resolutions", js_resolutions);
    271 
    272     js_display->SetInteger("colorProfile", display_info.color_profile());
    273     base::ListValue* available_color_profiles = new base::ListValue();
    274     for (size_t i = 0;
    275          i < display_info.available_color_profiles().size(); ++i) {
    276       base::DictionaryValue* color_profile_dict = new base::DictionaryValue();
    277       ui::ColorCalibrationProfile color_profile =
    278           display_info.available_color_profiles()[i];
    279       color_profile_dict->SetInteger("profileId", color_profile);
    280       color_profile_dict->SetString("name", GetColorProfileName(color_profile));
    281       available_color_profiles->Append(color_profile_dict);
    282     }
    283     js_display->Set("availableColorProfiles", available_color_profiles);
    284     js_displays.Append(js_display);
    285   }
    286 
    287   scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue());
    288   scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue());
    289   if (display_manager->GetNumDisplays() > 1) {
    290     const ash::DisplayLayout layout =
    291         display_manager->GetCurrentDisplayLayout();
    292     layout_value.reset(new base::FundamentalValue(layout.position));
    293     offset_value.reset(new base::FundamentalValue(layout.offset));
    294   }
    295 
    296   web_ui()->CallJavascriptFunction(
    297       "options.DisplayOptions.setDisplayInfo",
    298       mirroring, js_displays, *layout_value.get(), *offset_value.get());
    299 }
    300 
    301 void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) {
    302   ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring);
    303   // Not necessary to start fade-in animation. DisplayConfigurator will do that.
    304 }
    305 
    306 void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
    307     int position, int offset) {
    308   SetCurrentDisplayLayout(
    309       ash::DisplayLayout::FromInts(position, offset));
    310   ash::Shell::GetInstance()->display_configurator_animation()->
    311       StartFadeInAnimation();
    312 }
    313 
    314 void DisplayOptionsHandler::HandleDisplayInfo(
    315     const base::ListValue* unused_args) {
    316   SendAllDisplayInfo();
    317 }
    318 
    319 void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) {
    320   DCHECK(!args->empty());
    321   content::RecordAction(
    322       base::UserMetricsAction("Options_DisplayToggleMirroring"));
    323   bool is_mirroring = false;
    324   args->GetBoolean(0, &is_mirroring);
    325   ash::Shell::GetInstance()->display_configurator_animation()->
    326       StartFadeOutAnimation(base::Bind(
    327           &DisplayOptionsHandler::OnFadeOutForMirroringFinished,
    328           base::Unretained(this),
    329           is_mirroring));
    330 }
    331 
    332 void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) {
    333   DCHECK(!args->empty());
    334   int64 display_id = GetDisplayId(args);
    335   if (display_id == gfx::Display::kInvalidDisplayID)
    336     return;
    337 
    338   content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
    339   ash::Shell::GetInstance()->display_controller()->
    340       SetPrimaryDisplayId(display_id);
    341 }
    342 
    343 void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
    344   double layout = -1;
    345   double offset = -1;
    346   if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) {
    347     LOG(ERROR) << "Invalid parameter";
    348     SendAllDisplayInfo();
    349     return;
    350   }
    351   DCHECK_LE(ash::DisplayLayout::TOP, layout);
    352   DCHECK_GE(ash::DisplayLayout::LEFT, layout);
    353   content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange"));
    354   ash::Shell::GetInstance()->display_configurator_animation()->
    355       StartFadeOutAnimation(base::Bind(
    356           &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished,
    357           base::Unretained(this),
    358           static_cast<int>(layout),
    359           static_cast<int>(offset)));
    360 }
    361 
    362 void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) {
    363   DCHECK(!args->empty());
    364 
    365   int64 display_id = GetDisplayId(args);
    366   if (display_id == gfx::Display::kInvalidDisplayID)
    367     return;
    368 
    369   double ui_scale = 0.0f;
    370   if (!args->GetDouble(1, &ui_scale) || ui_scale == 0.0f) {
    371     LOG(ERROR) << "Can't find new ui_scale";
    372     return;
    373   }
    374 
    375   GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale);
    376 }
    377 
    378 void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) {
    379   DCHECK(!args->empty());
    380   int64 display_id = GetDisplayId(args);
    381   if (display_id == gfx::Display::kInvalidDisplayID)
    382     return;
    383 
    384   content::RecordAction(
    385       base::UserMetricsAction("Options_DisplaySetResolution"));
    386   double width = 0.0f;
    387   double height = 0.0f;
    388   if (!args->GetDouble(1, &width) || width == 0.0f) {
    389     LOG(ERROR) << "Can't find new width";
    390     return;
    391   }
    392   if (!args->GetDouble(2, &height) || height == 0.0f) {
    393     LOG(ERROR) << "Can't find new height";
    394     return;
    395   }
    396 
    397   const ash::DisplayInfo& display_info =
    398       GetDisplayManager()->GetDisplayInfo(display_id);
    399   gfx::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, height));
    400   gfx::Size old_resolution = display_info.bounds_in_native().size();
    401   bool has_new_resolution = false;
    402   bool has_old_resolution = false;
    403   for (size_t i = 0; i < display_info.display_modes().size(); ++i) {
    404     ash::DisplayMode display_mode = display_info.display_modes()[i];
    405     if (display_mode.size == new_resolution)
    406       has_new_resolution = true;
    407     if (display_mode.size == old_resolution)
    408       has_old_resolution = true;
    409   }
    410   if (!has_new_resolution) {
    411     LOG(ERROR) << "No new resolution " << new_resolution.ToString()
    412                << " is found in the display info " << display_info.ToString();
    413     return;
    414   }
    415   if (!has_old_resolution) {
    416     LOG(ERROR) << "No old resolution " << old_resolution.ToString()
    417                << " is found in the display info " << display_info.ToString();
    418     return;
    419   }
    420 
    421   ash::Shell::GetInstance()->resolution_notification_controller()->
    422       SetDisplayResolutionAndNotify(
    423           display_id, old_resolution, new_resolution,
    424           base::Bind(&StoreDisplayPrefs));
    425 }
    426 
    427 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
    428   DCHECK(!args->empty());
    429 
    430   int64 display_id = GetDisplayId(args);
    431   if (display_id == gfx::Display::kInvalidDisplayID)
    432     return;
    433 
    434   std::string rotation_value;
    435   gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0;
    436   if (!args->GetString(1, &rotation_value)) {
    437     LOG(ERROR) << "Can't find new orientation";
    438     return;
    439   }
    440   if (rotation_value == "90")
    441     new_rotation = gfx::Display::ROTATE_90;
    442   else if (rotation_value == "180")
    443     new_rotation = gfx::Display::ROTATE_180;
    444   else if (rotation_value == "270")
    445     new_rotation = gfx::Display::ROTATE_270;
    446   else if (rotation_value != "0")
    447     LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";
    448 
    449   content::RecordAction(
    450       base::UserMetricsAction("Options_DisplaySetOrientation"));
    451   GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
    452 }
    453 
    454 void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) {
    455   DCHECK(!args->empty());
    456   int64 display_id = GetDisplayId(args);
    457   if (display_id == gfx::Display::kInvalidDisplayID)
    458     return;
    459 
    460   std::string profile_value;
    461   if (!args->GetString(1, &profile_value)) {
    462     LOG(ERROR) << "Invalid profile_value";
    463     return;
    464   }
    465 
    466   int profile_id;
    467   if (!base::StringToInt(profile_value, &profile_id)) {
    468     LOG(ERROR) << "Invalid profile: " << profile_value;
    469     return;
    470   }
    471 
    472   if (profile_id < ui::COLOR_PROFILE_STANDARD ||
    473       profile_id > ui::COLOR_PROFILE_READING) {
    474     LOG(ERROR) << "Invalid profile_id: " << profile_id;
    475     return;
    476   }
    477 
    478   GetDisplayManager()->SetColorCalibrationProfile(
    479       display_id, static_cast<ui::ColorCalibrationProfile>(profile_id));
    480   SendAllDisplayInfo();
    481 }
    482 
    483 }  // namespace options
    484 }  // namespace chromeos
    485