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_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 
     37 DisplayManager* GetDisplayManager() {
     38   return ash::Shell::GetInstance()->display_manager();
     39 }
     40 
     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 
     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 
     65 DisplayOptionsHandler::DisplayOptionsHandler() {
     66   ash::Shell::GetInstance()->display_controller()->AddObserver(this);
     67 }
     68 
     69 DisplayOptionsHandler::~DisplayOptionsHandler() {
     70   ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
     71 }
     72 
     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 
    115 void DisplayOptionsHandler::InitializePage() {
    116   DCHECK(web_ui());
    117 }
    118 
    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 
    150 void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
    151 }
    152 
    153 void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
    154   SendAllDisplayInfo();
    155 }
    156 
    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 
    167 void DisplayOptionsHandler::SendDisplayInfo(
    168     const std::vector<gfx::Display>& displays) {
    169   DisplayManager* display_manager = GetDisplayManager();
    170   ash::DisplayController* display_controller =
    171       ash::Shell::GetInstance()->display_controller();
    172   base::FundamentalValue mirroring(display_manager->IsMirrored());
    173 
    174   int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
    175   base::ListValue js_displays;
    176   for (size_t i = 0; i < displays.size(); ++i) {
    177     const gfx::Display& display = displays[i];
    178     const ash::internal::DisplayInfo& display_info =
    179         display_manager->GetDisplayInfo(display.id());
    180     const gfx::Rect& bounds = display.bounds();
    181     base::DictionaryValue* js_display = new base::DictionaryValue();
    182     js_display->SetString("id", base::Int64ToString(display.id()));
    183     js_display->SetInteger("x", bounds.x());
    184     js_display->SetInteger("y", bounds.y());
    185     js_display->SetInteger("width", bounds.width());
    186     js_display->SetInteger("height", bounds.height());
    187     js_display->SetString("name",
    188                           display_manager->GetDisplayNameForId(display.id()));
    189     js_display->SetBoolean("isPrimary", display.id() == primary_id);
    190     js_display->SetBoolean("isInternal", display.IsInternal());
    191     js_display->SetInteger("orientation",
    192                            static_cast<int>(display_info.rotation()));
    193     std::vector<ash::internal::Resolution> resolutions;
    194     std::vector<float> ui_scales;
    195     if (display.IsInternal()) {
    196       ui_scales = DisplayManager::GetScalesForDisplay(display_info);
    197       gfx::SizeF base_size = display_info.bounds_in_pixel().size();
    198       base_size.Scale(1.0f / display.device_scale_factor());
    199       if (display_info.rotation() == gfx::Display::ROTATE_90 ||
    200           display_info.rotation() == gfx::Display::ROTATE_270) {
    201         float tmp = base_size.width();
    202         base_size.set_width(base_size.height());
    203         base_size.set_height(tmp);
    204       }
    205       for (size_t i = 0; i < ui_scales.size(); ++i) {
    206         gfx::SizeF new_size = base_size;
    207         new_size.Scale(ui_scales[i]);
    208         resolutions.push_back(ash::internal::Resolution(
    209             gfx::ToFlooredSize(new_size), false /* interlaced */));
    210       }
    211     } else {
    212       for (size_t i = 0; i < display_info.resolutions().size(); ++i)
    213         resolutions.push_back(display_info.resolutions()[i]);
    214     }
    215     std::sort(resolutions.begin(), resolutions.end(), CompareResolution);
    216 
    217     base::ListValue* js_resolutions = new base::ListValue();
    218     gfx::Size current_size(bounds.width() * display.device_scale_factor(),
    219                            bounds.height() * display.device_scale_factor());
    220     for (size_t i = 0; i < resolutions.size(); ++i) {
    221       base::DictionaryValue* resolution_info = new base::DictionaryValue();
    222       if (!ui_scales.empty()) {
    223         resolution_info->SetDouble("scale", ui_scales[i]);
    224         if (ui_scales[i] == 1.0f)
    225           resolution_info->SetBoolean("isBest", true);
    226         resolution_info->SetBoolean(
    227             "selected", display_info.ui_scale() == ui_scales[i]);
    228       } else {
    229         // Picks the largest one as the "best", which is the last element
    230         // because |resolutions| is sorted by its area.
    231         if (i == resolutions.size() - 1)
    232           resolution_info->SetBoolean("isBest", true);
    233         resolution_info->SetBoolean(
    234             "selected", (resolutions[i].size == current_size));
    235       }
    236       resolution_info->SetInteger("width", resolutions[i].size.width());
    237       resolution_info->SetInteger("height",resolutions[i].size.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_controller->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 
    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 
    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 
    271 void DisplayOptionsHandler::HandleDisplayInfo(
    272     const base::ListValue* unused_args) {
    273   SendAllDisplayInfo();
    274 }
    275 
    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 
    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 
    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 
    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 
    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::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, height));
    351   gfx::Size old_resolution = display_info.size_in_pixel();
    352   bool has_new_resolution = false;
    353   bool has_old_resolution = false;
    354   for (size_t i = 0; i < display_info.resolutions().size(); ++i) {
    355     ash::internal::Resolution resolution = display_info.resolutions()[i];
    356     if (resolution.size == new_resolution)
    357       has_new_resolution = true;
    358     if (resolution.size == old_resolution)
    359       has_old_resolution = true;
    360   }
    361   if (!has_new_resolution) {
    362     LOG(ERROR) << "No new resolution " << new_resolution.ToString()
    363                << " is found in the display info " << display_info.ToString();
    364     return;
    365   }
    366   if (!has_old_resolution) {
    367     LOG(ERROR) << "No old resolution " << old_resolution.ToString()
    368                << " is found in the display info " << display_info.ToString();
    369     return;
    370   }
    371 
    372   ash::Shell::GetInstance()->resolution_notification_controller()->
    373       SetDisplayResolutionAndNotify(
    374           display_id, old_resolution, new_resolution,
    375           base::Bind(&StoreDisplayPrefs));
    376 }
    377 
    378 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
    379   DCHECK(!args->empty());
    380 
    381   int64 display_id = GetDisplayId(args);
    382   if (display_id == gfx::Display::kInvalidDisplayID)
    383     return;
    384 
    385   std::string rotation_value;
    386   gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0;
    387   if (!args->GetString(1, &rotation_value)) {
    388     LOG(ERROR) << "Can't find new orientation";
    389     return;
    390   }
    391   if (rotation_value == "90")
    392     new_rotation = gfx::Display::ROTATE_90;
    393   else if (rotation_value == "180")
    394     new_rotation = gfx::Display::ROTATE_180;
    395   else if (rotation_value == "270")
    396     new_rotation = gfx::Display::ROTATE_270;
    397   else if (rotation_value != "0")
    398     LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";
    399 
    400   GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
    401 }
    402 
    403 }  // namespace options
    404 }  // namespace chromeos
    405