root/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetDisplayManager
  2. GetDisplayId
  3. CompareDisplayMode
  4. GetColorProfileName
  5. GetLocalizedValues
  6. InitializePage
  7. RegisterMessages
  8. OnDisplayConfigurationChanging
  9. OnDisplayConfigurationChanged
  10. SendAllDisplayInfo
  11. SendDisplayInfo
  12. OnFadeOutForMirroringFinished
  13. OnFadeOutForDisplayLayoutFinished
  14. HandleDisplayInfo
  15. HandleMirroring
  16. HandleSetPrimary
  17. HandleDisplayLayout
  18. HandleSetUIScale
  19. HandleSetResolution
  20. HandleSetOrientation
  21. HandleSetColorProfile

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/options/chromeos/display_options_handler.h"

#include <string>

#include "ash/display/display_controller.h"
#include "ash/display/display_manager.h"
#include "ash/display/output_configurator_animation.h"
#include "ash/display/resolution_notification_controller.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/chromeos/display/display_preferences.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_ui.h"
#include "grit/ash_strings.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/display.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"

using ash::DisplayManager;

namespace chromeos {
namespace options {
namespace {

DisplayManager* GetDisplayManager() {
  return ash::Shell::GetInstance()->display_manager();
}

int64 GetDisplayId(const base::ListValue* args) {
  // Assumes the display ID is specified as the first argument.
  std::string id_value;
  if (!args->GetString(0, &id_value)) {
    LOG(ERROR) << "Can't find ID";
    return gfx::Display::kInvalidDisplayID;
  }

  int64 display_id = gfx::Display::kInvalidDisplayID;
  if (!base::StringToInt64(id_value, &display_id)) {
    LOG(ERROR) << "Invalid display id: " << id_value;
    return gfx::Display::kInvalidDisplayID;
  }

  return display_id;
}

bool CompareDisplayMode(ash::DisplayMode d1, ash::DisplayMode d2) {
  if (d1.size.GetArea() == d2.size.GetArea())
    return d1.refresh_rate < d2.refresh_rate;
  return d1.size.GetArea() < d2.size.GetArea();
}

base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) {
  switch (profile) {
    case ui::COLOR_PROFILE_STANDARD:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD);
    case ui::COLOR_PROFILE_DYNAMIC:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC);
    case ui::COLOR_PROFILE_MOVIE:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE);
    case ui::COLOR_PROFILE_READING:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING);
    case ui::NUM_COLOR_PROFILES:
      break;
  }

  NOTREACHED();
  return base::string16();
}

}  // namespace

DisplayOptionsHandler::DisplayOptionsHandler() {
  ash::Shell::GetInstance()->display_controller()->AddObserver(this);
}

DisplayOptionsHandler::~DisplayOptionsHandler() {
  ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
}

void DisplayOptionsHandler::GetLocalizedValues(
    base::DictionaryValue* localized_strings) {
  DCHECK(localized_strings);
  RegisterTitle(localized_strings, "displayOptionsPage",
                IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE);

  localized_strings->SetString(
      "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS));
  localized_strings->SetString(
      "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION));
  localized_strings->SetString(
      "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION));
  localized_strings->SetString(
      "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN));

  localized_strings->SetString("startMirroring", l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_MIRRORING));
  localized_strings->SetString("stopMirroring", l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STOP_MIRRORING));
  localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME));
  localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY));
  localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST));
  localized_strings->SetString("orientation0", l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION));
  localized_strings->SetString("orientation90", l10n_util::GetStringUTF16(
      IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90));
  localized_strings->SetString("orientation180", l10n_util::GetStringUTF16(
      IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180));
  localized_strings->SetString("orientation270", l10n_util::GetStringUTF16(
      IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270));
  localized_strings->SetString(
      "startCalibratingOverscan", l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN));
  localized_strings->SetString(
      "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE));
}

void DisplayOptionsHandler::InitializePage() {
  DCHECK(web_ui());
}

void DisplayOptionsHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "getDisplayInfo",
      base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setMirroring",
      base::Bind(&DisplayOptionsHandler::HandleMirroring,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setPrimary",
      base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setDisplayLayout",
      base::Bind(&DisplayOptionsHandler::HandleDisplayLayout,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setUIScale",
      base::Bind(&DisplayOptionsHandler::HandleSetUIScale,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setResolution",
      base::Bind(&DisplayOptionsHandler::HandleSetResolution,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setOrientation",
      base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setColorProfile",
      base::Bind(&DisplayOptionsHandler::HandleSetColorProfile,
                 base::Unretained(this)));
}

void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
}

void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
  SendAllDisplayInfo();
}

void DisplayOptionsHandler::SendAllDisplayInfo() {
  DisplayManager* display_manager = GetDisplayManager();

  std::vector<gfx::Display> displays;
  for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
    displays.push_back(display_manager->GetDisplayAt(i));
  }
  SendDisplayInfo(displays);
}

void DisplayOptionsHandler::SendDisplayInfo(
    const std::vector<gfx::Display>& displays) {
  DisplayManager* display_manager = GetDisplayManager();
  base::FundamentalValue mirroring(display_manager->IsMirrored());

  int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
  base::ListValue js_displays;
  for (size_t i = 0; i < displays.size(); ++i) {
    const gfx::Display& display = displays[i];
    const ash::DisplayInfo& display_info =
        display_manager->GetDisplayInfo(display.id());
    const gfx::Rect& bounds = display.bounds();
    base::DictionaryValue* js_display = new base::DictionaryValue();
    js_display->SetString("id", base::Int64ToString(display.id()));
    js_display->SetInteger("x", bounds.x());
    js_display->SetInteger("y", bounds.y());
    js_display->SetInteger("width", bounds.width());
    js_display->SetInteger("height", bounds.height());
    js_display->SetString("name",
                          display_manager->GetDisplayNameForId(display.id()));
    js_display->SetBoolean("isPrimary", display.id() == primary_id);
    js_display->SetBoolean("isInternal", display.IsInternal());
    js_display->SetInteger("orientation",
                           static_cast<int>(display_info.rotation()));
    std::vector<ash::DisplayMode> display_modes;
    std::vector<float> ui_scales;
    if (display.IsInternal()) {
      ui_scales = DisplayManager::GetScalesForDisplay(display_info);
      gfx::SizeF base_size = display_info.bounds_in_native().size();
      base_size.Scale(1.0f / display_info.device_scale_factor());
      if (display_info.rotation() == gfx::Display::ROTATE_90 ||
          display_info.rotation() == gfx::Display::ROTATE_270) {
        float tmp = base_size.width();
        base_size.set_width(base_size.height());
        base_size.set_height(tmp);
      }
      for (size_t i = 0; i < ui_scales.size(); ++i) {
        gfx::SizeF new_size = base_size;
        new_size.Scale(ui_scales[i]);
        display_modes.push_back(ash::DisplayMode(
            gfx::ToFlooredSize(new_size), -1.0f, false, false));
      }
    } else {
      for (size_t i = 0; i < display_info.display_modes().size(); ++i)
        display_modes.push_back(display_info.display_modes()[i]);
    }
    std::sort(display_modes.begin(), display_modes.end(), CompareDisplayMode);

    base::ListValue* js_resolutions = new base::ListValue();
    gfx::Size current_size = display_info.bounds_in_native().size();
    gfx::Insets current_overscan = display_info.GetOverscanInsetsInPixel();
    for (size_t i = 0; i < display_modes.size(); ++i) {
      base::DictionaryValue* resolution_info = new base::DictionaryValue();
      gfx::Size resolution = display_modes[i].size;
      if (!ui_scales.empty()) {
        resolution_info->SetDouble("scale", ui_scales[i]);
        if (ui_scales[i] == 1.0f)
          resolution_info->SetBoolean("isBest", true);
        resolution_info->SetBoolean(
            "selected", display_info.configured_ui_scale() == ui_scales[i]);
      } else {
        // Picks the largest one as the "best", which is the last element
        // because |display_modes| is sorted by its area.
        if (i == display_modes.size() - 1)
          resolution_info->SetBoolean("isBest", true);
        resolution_info->SetBoolean("selected", (resolution == current_size));
        resolution.Enlarge(
            -current_overscan.width(), -current_overscan.height());
      }
      resolution_info->SetInteger("width", resolution.width());
      resolution_info->SetInteger("height", resolution.height());
      if (display_modes[i].refresh_rate > 0.0f) {
        resolution_info->SetDouble("refreshRate",
                                   display_modes[i].refresh_rate);
      }
      js_resolutions->Append(resolution_info);
    }
    js_display->Set("resolutions", js_resolutions);

    js_display->SetInteger("colorProfile", display_info.color_profile());
    base::ListValue* available_color_profiles = new base::ListValue();
    for (size_t i = 0;
         i < display_info.available_color_profiles().size(); ++i) {
      base::DictionaryValue* color_profile_dict = new base::DictionaryValue();
      ui::ColorCalibrationProfile color_profile =
          display_info.available_color_profiles()[i];
      color_profile_dict->SetInteger("profileId", color_profile);
      color_profile_dict->SetString("name", GetColorProfileName(color_profile));
      available_color_profiles->Append(color_profile_dict);
    }
    js_display->Set("availableColorProfiles", available_color_profiles);
    js_displays.Append(js_display);
  }

  scoped_ptr<base::Value> layout_value(base::Value::CreateNullValue());
  scoped_ptr<base::Value> offset_value(base::Value::CreateNullValue());
  if (display_manager->GetNumDisplays() > 1) {
    const ash::DisplayLayout layout =
        display_manager->GetCurrentDisplayLayout();
    layout_value.reset(new base::FundamentalValue(layout.position));
    offset_value.reset(new base::FundamentalValue(layout.offset));
  }

  web_ui()->CallJavascriptFunction(
      "options.DisplayOptions.setDisplayInfo",
      mirroring, js_displays, *layout_value.get(), *offset_value.get());
}

void DisplayOptionsHandler::OnFadeOutForMirroringFinished(bool is_mirroring) {
  ash::Shell::GetInstance()->display_manager()->SetMirrorMode(is_mirroring);
  // Not necessary to start fade-in animation.  OutputConfigurator will do that.
}

void DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished(
    int position, int offset) {
  SetCurrentDisplayLayout(
      ash::DisplayLayout::FromInts(position, offset));
  ash::Shell::GetInstance()->output_configurator_animation()->
      StartFadeInAnimation();
}

void DisplayOptionsHandler::HandleDisplayInfo(
    const base::ListValue* unused_args) {
  SendAllDisplayInfo();
}

void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) {
  DCHECK(!args->empty());
  content::RecordAction(
      base::UserMetricsAction("Options_DisplayToggleMirroring"));
  bool is_mirroring = false;
  args->GetBoolean(0, &is_mirroring);
  ash::Shell::GetInstance()->output_configurator_animation()->
      StartFadeOutAnimation(base::Bind(
          &DisplayOptionsHandler::OnFadeOutForMirroringFinished,
          base::Unretained(this),
          is_mirroring));
}

void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) {
  DCHECK(!args->empty());
  int64 display_id = GetDisplayId(args);
  if (display_id == gfx::Display::kInvalidDisplayID)
    return;

  content::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
  ash::Shell::GetInstance()->display_controller()->
      SetPrimaryDisplayId(display_id);
}

void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
  double layout = -1;
  double offset = -1;
  if (!args->GetDouble(0, &layout) || !args->GetDouble(1, &offset)) {
    LOG(ERROR) << "Invalid parameter";
    SendAllDisplayInfo();
    return;
  }
  DCHECK_LE(ash::DisplayLayout::TOP, layout);
  DCHECK_GE(ash::DisplayLayout::LEFT, layout);
  content::RecordAction(base::UserMetricsAction("Options_DisplayRearrange"));
  ash::Shell::GetInstance()->output_configurator_animation()->
      StartFadeOutAnimation(base::Bind(
          &DisplayOptionsHandler::OnFadeOutForDisplayLayoutFinished,
          base::Unretained(this),
          static_cast<int>(layout),
          static_cast<int>(offset)));
}

void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) {
  DCHECK(!args->empty());

  int64 display_id = GetDisplayId(args);
  if (display_id == gfx::Display::kInvalidDisplayID)
    return;

  double ui_scale = 0.0f;
  if (!args->GetDouble(1, &ui_scale) || ui_scale == 0.0f) {
    LOG(ERROR) << "Can't find new ui_scale";
    return;
  }

  GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale);
}

void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) {
  DCHECK(!args->empty());
  int64 display_id = GetDisplayId(args);
  if (display_id == gfx::Display::kInvalidDisplayID)
    return;

  content::RecordAction(
      base::UserMetricsAction("Options_DisplaySetResolution"));
  double width = 0.0f;
  double height = 0.0f;
  if (!args->GetDouble(1, &width) || width == 0.0f) {
    LOG(ERROR) << "Can't find new width";
    return;
  }
  if (!args->GetDouble(2, &height) || height == 0.0f) {
    LOG(ERROR) << "Can't find new height";
    return;
  }

  const ash::DisplayInfo& display_info =
      GetDisplayManager()->GetDisplayInfo(display_id);
  gfx::Insets current_overscan = display_info.GetOverscanInsetsInPixel();
  gfx::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, height));
  new_resolution.Enlarge(current_overscan.width(), current_overscan.height());
  gfx::Size old_resolution = display_info.bounds_in_native().size();
  bool has_new_resolution = false;
  bool has_old_resolution = false;
  for (size_t i = 0; i < display_info.display_modes().size(); ++i) {
    ash::DisplayMode display_mode = display_info.display_modes()[i];
    if (display_mode.size == new_resolution)
      has_new_resolution = true;
    if (display_mode.size == old_resolution)
      has_old_resolution = true;
  }
  if (!has_new_resolution) {
    LOG(ERROR) << "No new resolution " << new_resolution.ToString()
               << " is found in the display info " << display_info.ToString();
    return;
  }
  if (!has_old_resolution) {
    LOG(ERROR) << "No old resolution " << old_resolution.ToString()
               << " is found in the display info " << display_info.ToString();
    return;
  }

  ash::Shell::GetInstance()->resolution_notification_controller()->
      SetDisplayResolutionAndNotify(
          display_id, old_resolution, new_resolution,
          base::Bind(&StoreDisplayPrefs));
}

void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
  DCHECK(!args->empty());

  int64 display_id = GetDisplayId(args);
  if (display_id == gfx::Display::kInvalidDisplayID)
    return;

  std::string rotation_value;
  gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_0;
  if (!args->GetString(1, &rotation_value)) {
    LOG(ERROR) << "Can't find new orientation";
    return;
  }
  if (rotation_value == "90")
    new_rotation = gfx::Display::ROTATE_90;
  else if (rotation_value == "180")
    new_rotation = gfx::Display::ROTATE_180;
  else if (rotation_value == "270")
    new_rotation = gfx::Display::ROTATE_270;
  else if (rotation_value != "0")
    LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";

  content::RecordAction(
      base::UserMetricsAction("Options_DisplaySetOrientation"));
  GetDisplayManager()->SetDisplayRotation(display_id, new_rotation);
}

void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) {
  DCHECK(!args->empty());
  int64 display_id = GetDisplayId(args);
  if (display_id == gfx::Display::kInvalidDisplayID)
    return;

  std::string profile_value;
  if (!args->GetString(1, &profile_value)) {
    LOG(ERROR) << "Invalid profile_value";
    return;
  }

  int profile_id;
  if (!base::StringToInt(profile_value, &profile_id)) {
    LOG(ERROR) << "Invalid profile: " << profile_value;
    return;
  }

  if (profile_id < ui::COLOR_PROFILE_STANDARD ||
      profile_id > ui::COLOR_PROFILE_READING) {
    LOG(ERROR) << "Invalid profile_id: " << profile_id;
    return;
  }

  GetDisplayManager()->SetColorCalibrationProfile(
      display_id, static_cast<ui::ColorCalibrationProfile>(profile_id));
  SendAllDisplayInfo();
}

}  // namespace options
}  // namespace chromeos

/* [<][>][^][v][top][bottom][index][help] */