Commit 7badf7f3 authored by mukai@chromium.org's avatar mukai@chromium.org

Introduce user customization of external HighDPI mode for 4K monitor.

This CL adds the options of customizing external display's device
scale factor from the options page. There are two TODOs though:
- the configured device scale factor should be stored
- not possible to specify device scale factor and resolution at the
  same time; from 1600x900, it can't enter to 1920x1080(2x). Instead
  it enters to 3840x2160, then the user needs to select 2x again.

Actually the former would solve the latter; this CL sets the new
resolution and the DSF at the same time but resolution change is
asynchronous, so changed DSF will be overwritten by the resolution
change.

I will address these issues in further CL(s).

BUG=396704
R=oshima@chromium.org, stevenjb@chromium.org
TBR=asvitkine@chromium.org
TEST=manually

Review URL: https://codereview.chromium.org/417113012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287677 0039d316-1c4b-4281-b951-d872f2087c98
parent 9c1da58e
......@@ -46,25 +46,60 @@ const DeviceScaleFactorDPIThreshold kThresholdTable[] = {
// 1 inch in mm.
const float kInchInMm = 25.4f;
// Display mode list is sorted by (in descending priority):
// * the area in pixels.
// * refresh rate.
// The minimum pixel width whose monitor can be called as '4K'.
const int kMinimumWidthFor4K = 3840;
// The list of device scale factors (in addition to 1.0f) which is
// available in extrenal large monitors.
const float kAdditionalDeviceScaleFactorsFor4k[] = {1.25f, 2.0f};
// Display mode list is sorted by:
// * the area in pixels in ascending order
// * refresh rate in descending order
struct DisplayModeSorter {
bool operator()(const DisplayMode& a, const DisplayMode& b) {
if (a.size.GetArea() == b.size.GetArea())
gfx::Size size_a_dip = a.GetSizeInDIP();
gfx::Size size_b_dip = b.GetSizeInDIP();
if (size_a_dip.GetArea() == size_b_dip.GetArea())
return (a.refresh_rate > b.refresh_rate);
return (a.size.GetArea() > b.size.GetArea());
return (size_a_dip.GetArea() < size_b_dip.GetArea());
}
};
} // namespace
// static
std::vector<DisplayMode> DisplayChangeObserver::GetDisplayModeList(
std::vector<DisplayMode> DisplayChangeObserver::GetInternalDisplayModeList(
const DisplayInfo& display_info,
const DisplayConfigurator::DisplayState& output) {
std::vector<DisplayMode> display_mode_list;
const ui::DisplayMode* ui_native_mode = output.display->native_mode();
DisplayMode native_mode(ui_native_mode->size(),
ui_native_mode->refresh_rate(),
ui_native_mode->is_interlaced(),
true);
native_mode.device_scale_factor = display_info.device_scale_factor();
std::vector<float> ui_scales =
DisplayManager::GetScalesForDisplay(display_info);
for (size_t i = 0; i < ui_scales.size(); ++i) {
DisplayMode mode = native_mode;
mode.ui_scale = ui_scales[i];
mode.native = (ui_scales[i] == 1.0f);
display_mode_list.push_back(mode);
}
std::sort(
display_mode_list.begin(), display_mode_list.end(), DisplayModeSorter());
return display_mode_list;
}
// static
std::vector<DisplayMode> DisplayChangeObserver::GetExternalDisplayModeList(
const DisplayConfigurator::DisplayState& output) {
typedef std::map<std::pair<int, int>, DisplayMode> DisplayModeMap;
DisplayModeMap display_mode_map;
DisplayMode native_mode;
for (std::vector<const ui::DisplayMode*>::const_iterator it =
output.display->modes().begin();
it != output.display->modes().end();
......@@ -76,6 +111,8 @@ std::vector<DisplayMode> DisplayChangeObserver::GetDisplayModeList(
mode_info.refresh_rate(),
mode_info.is_interlaced(),
output.display->native_mode() == *it);
if (display_mode.native)
native_mode = display_mode;
// Add the display mode if it isn't already present and override interlaced
// display modes with non-interlaced ones.
......@@ -92,6 +129,17 @@ std::vector<DisplayMode> DisplayChangeObserver::GetDisplayModeList(
++iter) {
display_mode_list.push_back(iter->second);
}
if (native_mode.size.width() >= kMinimumWidthFor4K) {
for (size_t i = 0; i < arraysize(kAdditionalDeviceScaleFactorsFor4k);
++i) {
DisplayMode mode = native_mode;
mode.device_scale_factor = kAdditionalDeviceScaleFactorsFor4k[i];
mode.native = false;
display_mode_list.push_back(mode);
}
}
std::sort(
display_mode_list.begin(), display_mode_list.end(), DisplayModeSorter());
return display_mode_list;
......@@ -150,8 +198,6 @@ void DisplayChangeObserver::OnDisplayModeChanged(
}
gfx::Rect display_bounds(state.display->origin(), mode_info->size());
std::vector<DisplayMode> display_modes = GetDisplayModeList(state);
std::string name =
state.display->type() == ui::DISPLAY_CONNECTION_TYPE_INTERNAL ?
l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME) :
......@@ -168,13 +214,19 @@ void DisplayChangeObserver::OnDisplayModeChanged(
new_info.set_device_scale_factor(device_scale_factor);
new_info.SetBounds(display_bounds);
new_info.set_native(true);
new_info.set_display_modes(display_modes);
new_info.set_touch_support(state.touch_device_id == 0 ?
gfx::Display::TOUCH_SUPPORT_UNAVAILABLE :
gfx::Display::TOUCH_SUPPORT_AVAILABLE);
new_info.set_touch_device_id(state.touch_device_id);
new_info.set_is_aspect_preserving_scaling(
state.display->is_aspect_preserving_scaling());
std::vector<DisplayMode> display_modes =
(state.display->type() == ui::DISPLAY_CONNECTION_TYPE_INTERNAL) ?
GetInternalDisplayModeList(new_info, state) :
GetExternalDisplayModeList(state);
new_info.set_display_modes(display_modes);
new_info.set_available_color_profiles(
Shell::GetInstance()
->display_configurator()
......
......@@ -12,6 +12,7 @@
namespace ash {
class DisplayInfo;
struct DisplayMode;
// An object that observes changes in display configuration and
......@@ -20,8 +21,13 @@ class DisplayChangeObserver : public ui::DisplayConfigurator::StateController,
public ui::DisplayConfigurator::Observer,
public ShellObserver {
public:
// Returns the mode list for internal display.
ASH_EXPORT static std::vector<DisplayMode> GetInternalDisplayModeList(
const DisplayInfo& display_info,
const ui::DisplayConfigurator::DisplayState& output);
// Returns the resolution list.
ASH_EXPORT static std::vector<DisplayMode> GetDisplayModeList(
ASH_EXPORT static std::vector<DisplayMode> GetExternalDisplayModeList(
const ui::DisplayConfigurator::DisplayState& output);
DisplayChangeObserver();
......
......@@ -17,7 +17,7 @@ typedef testing::Test DisplayChangeObserverTest;
namespace ash {
TEST_F(DisplayChangeObserverTest, GetDisplayModeList) {
TEST_F(DisplayChangeObserverTest, GetExternalDisplayModeList) {
ScopedVector<const ui::DisplayMode> modes;
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1200), false, 60));
......@@ -48,37 +48,168 @@ TEST_F(DisplayChangeObserverTest, GetDisplayModeList) {
output.display = &display_snapshot;
std::vector<DisplayMode> display_modes =
DisplayChangeObserver::GetDisplayModeList(output);
DisplayChangeObserver::GetExternalDisplayModeList(output);
ASSERT_EQ(6u, display_modes.size());
EXPECT_EQ("1920x1200", display_modes[0].size.ToString());
EXPECT_FALSE(display_modes[0].interlaced);
EXPECT_EQ("640x480", display_modes[0].size.ToString());
EXPECT_TRUE(display_modes[0].interlaced);
EXPECT_EQ(display_modes[0].refresh_rate, 60);
EXPECT_EQ("1920x1080", display_modes[1].size.ToString());
EXPECT_EQ("1024x600", display_modes[1].size.ToString());
EXPECT_FALSE(display_modes[1].interlaced);
EXPECT_EQ(display_modes[1].refresh_rate, 80);
EXPECT_EQ(display_modes[1].refresh_rate, 70);
EXPECT_EQ("1280x720", display_modes[2].size.ToString());
EXPECT_FALSE(display_modes[2].interlaced);
EXPECT_EQ(display_modes[2].refresh_rate, 60);
EXPECT_EQ("1024x768", display_modes[2].size.ToString());
EXPECT_TRUE(display_modes[2].interlaced);
EXPECT_EQ(display_modes[2].refresh_rate, 70);
EXPECT_EQ("1024x768", display_modes[3].size.ToString());
EXPECT_TRUE(display_modes[3].interlaced);
EXPECT_EQ(display_modes[3].refresh_rate, 70);
EXPECT_EQ("1280x720", display_modes[3].size.ToString());
EXPECT_FALSE(display_modes[3].interlaced);
EXPECT_EQ(display_modes[3].refresh_rate, 60);
EXPECT_EQ("1024x600", display_modes[4].size.ToString());
EXPECT_EQ("1920x1080", display_modes[4].size.ToString());
EXPECT_FALSE(display_modes[4].interlaced);
EXPECT_EQ(display_modes[4].refresh_rate, 70);
EXPECT_EQ(display_modes[4].refresh_rate, 80);
EXPECT_EQ("640x480", display_modes[5].size.ToString());
EXPECT_TRUE(display_modes[5].interlaced);
EXPECT_EQ("1920x1200", display_modes[5].size.ToString());
EXPECT_FALSE(display_modes[5].interlaced);
EXPECT_EQ(display_modes[5].refresh_rate, 60);
// Outputs without any modes shouldn't cause a crash.
modes.clear();
display_snapshot.set_modes(modes.get());
display_modes = DisplayChangeObserver::GetDisplayModeList(output);
display_modes = DisplayChangeObserver::GetExternalDisplayModeList(output);
EXPECT_EQ(0u, display_modes.size());
}
TEST_F(DisplayChangeObserverTest, GetInternalDisplayModeList) {
ScopedVector<const ui::DisplayMode> modes;
// Data picked from peppy.
modes.push_back(new ui::DisplayMode(gfx::Size(1366, 768), false, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 768), false, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(800, 600), false, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(600, 600), false, 56.2));
modes.push_back(new ui::DisplayMode(gfx::Size(640, 480), false, 59.9));
ui::TestDisplaySnapshot display_snapshot;
display_snapshot.set_modes(modes.get());
display_snapshot.set_native_mode(modes[0]);
DisplayConfigurator::DisplayState output;
output.display = &display_snapshot;
DisplayInfo info;
info.SetBounds(gfx::Rect(0, 0, 1366, 768));
std::vector<DisplayMode> display_modes =
DisplayChangeObserver::GetInternalDisplayModeList(info, output);
ASSERT_EQ(5u, display_modes.size());
EXPECT_EQ("1366x768", display_modes[0].size.ToString());
EXPECT_FALSE(display_modes[0].native);
EXPECT_NEAR(display_modes[0].ui_scale, 0.5, 0.01);
EXPECT_EQ(display_modes[0].refresh_rate, 60);
EXPECT_EQ("1366x768", display_modes[1].size.ToString());
EXPECT_FALSE(display_modes[1].native);
EXPECT_NEAR(display_modes[1].ui_scale, 0.6, 0.01);
EXPECT_EQ(display_modes[1].refresh_rate, 60);
EXPECT_EQ("1366x768", display_modes[2].size.ToString());
EXPECT_FALSE(display_modes[2].native);
EXPECT_NEAR(display_modes[2].ui_scale, 0.75, 0.01);
EXPECT_EQ(display_modes[2].refresh_rate, 60);
EXPECT_EQ("1366x768", display_modes[3].size.ToString());
EXPECT_TRUE(display_modes[3].native);
EXPECT_NEAR(display_modes[3].ui_scale, 1.0, 0.01);
EXPECT_EQ(display_modes[3].refresh_rate, 60);
EXPECT_EQ("1366x768", display_modes[4].size.ToString());
EXPECT_FALSE(display_modes[4].native);
EXPECT_NEAR(display_modes[4].ui_scale, 1.125, 0.01);
EXPECT_EQ(display_modes[4].refresh_rate, 60);
}
TEST_F(DisplayChangeObserverTest, GetExternalDisplayModeList4K) {
ScopedVector<const ui::DisplayMode> modes;
modes.push_back(new ui::DisplayMode(gfx::Size(3840, 2160), false, 30));
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1200), false, 60));
// All non-interlaced (as would be seen with different refresh rates).
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1080), false, 80));
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1080), false, 70));
modes.push_back(new ui::DisplayMode(gfx::Size(1920, 1080), false, 60));
// Interlaced vs non-interlaced.
modes.push_back(new ui::DisplayMode(gfx::Size(1280, 720), true, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(1280, 720), false, 60));
// Interlaced only.
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 768), true, 70));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 768), true, 60));
// Mixed.
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 600), true, 60));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 600), false, 70));
modes.push_back(new ui::DisplayMode(gfx::Size(1024, 600), false, 60));
// Just one interlaced mode.
modes.push_back(new ui::DisplayMode(gfx::Size(640, 480), true, 60));
ui::TestDisplaySnapshot display_snapshot;
display_snapshot.set_modes(modes.get());
display_snapshot.set_native_mode(modes[0]);
DisplayConfigurator::DisplayState output;
output.display = &display_snapshot;
std::vector<DisplayMode> display_modes =
DisplayChangeObserver::GetExternalDisplayModeList(output);
ASSERT_EQ(9u, display_modes.size());
EXPECT_EQ("640x480", display_modes[0].size.ToString());
EXPECT_TRUE(display_modes[0].interlaced);
EXPECT_EQ(display_modes[0].refresh_rate, 60);
EXPECT_EQ("1024x600", display_modes[1].size.ToString());
EXPECT_FALSE(display_modes[1].interlaced);
EXPECT_EQ(display_modes[1].refresh_rate, 70);
EXPECT_EQ("1024x768", display_modes[2].size.ToString());
EXPECT_TRUE(display_modes[2].interlaced);
EXPECT_EQ(display_modes[2].refresh_rate, 70);
EXPECT_EQ("1280x720", display_modes[3].size.ToString());
EXPECT_FALSE(display_modes[3].interlaced);
EXPECT_EQ(display_modes[3].refresh_rate, 60);
EXPECT_EQ("1920x1080", display_modes[4].size.ToString());
EXPECT_FALSE(display_modes[4].interlaced);
EXPECT_EQ(display_modes[4].refresh_rate, 80);
EXPECT_EQ("3840x2160", display_modes[5].size.ToString());
EXPECT_FALSE(display_modes[5].interlaced);
EXPECT_FALSE(display_modes[5].native);
EXPECT_EQ(display_modes[5].refresh_rate, 30);
EXPECT_EQ(display_modes[5].device_scale_factor, 2.0);
EXPECT_EQ("1920x1200", display_modes[6].size.ToString());
EXPECT_FALSE(display_modes[6].interlaced);
EXPECT_EQ(display_modes[6].refresh_rate, 60);
EXPECT_EQ("3840x2160", display_modes[7].size.ToString());
EXPECT_FALSE(display_modes[7].interlaced);
EXPECT_FALSE(display_modes[7].native);
EXPECT_EQ(display_modes[7].refresh_rate, 30);
EXPECT_EQ(display_modes[7].device_scale_factor, 1.25);
EXPECT_EQ("3840x2160", display_modes[8].size.ToString());
EXPECT_FALSE(display_modes[8].interlaced);
EXPECT_TRUE(display_modes[8].native);
EXPECT_EQ(display_modes[8].refresh_rate, 30);
// Outputs without any modes shouldn't cause a crash.
modes.clear();
display_snapshot.set_modes(modes.get());
display_modes = DisplayChangeObserver::GetExternalDisplayModeList(output);
EXPECT_EQ(0u, display_modes.size());
}
......
......@@ -29,7 +29,11 @@ bool allow_upgrade_to_high_dpi = false;
}
DisplayMode::DisplayMode()
: refresh_rate(0.0f), interlaced(false), native(false) {}
: refresh_rate(0.0f),
interlaced(false),
native(false),
ui_scale(1.0f),
device_scale_factor(1.0f) {}
DisplayMode::DisplayMode(const gfx::Size& size,
float refresh_rate,
......@@ -38,7 +42,23 @@ DisplayMode::DisplayMode(const gfx::Size& size,
: size(size),
refresh_rate(refresh_rate),
interlaced(interlaced),
native(native) {}
native(native),
ui_scale(1.0f),
device_scale_factor(1.0f) {}
gfx::Size DisplayMode::GetSizeInDIP() const {
gfx::SizeF size_dip(size);
size_dip.Scale(ui_scale);
size_dip.Scale(1.0f / device_scale_factor);
return gfx::ToFlooredSize(size_dip);
}
bool DisplayMode::IsEquivalent(const DisplayMode& other) const {
const float kEpsilon = 0.0001f;
return size == other.size &&
std::abs(ui_scale - other.ui_scale) < kEpsilon &&
std::abs(device_scale_factor - other.device_scale_factor) < kEpsilon;
}
// satic
DisplayInfo DisplayInfo::CreateFromSpec(const std::string& spec) {
......
......@@ -24,10 +24,18 @@ struct ASH_EXPORT DisplayMode {
bool interlaced,
bool native);
// Returns the size in DIP which isvisible to the user.
gfx::Size GetSizeInDIP() const;
// Returns true if |other| has same size and scale factors.
bool IsEquivalent(const DisplayMode& other) const;
gfx::Size size; // Physical pixel size of the display.
float refresh_rate; // Refresh rate of the display, in Hz.
bool interlaced; // True if mode is interlaced.
bool native; // True if mode is native mode of the display.
float ui_scale; // The UI scale factor of the mode.
float device_scale_factor; // The device scale factor of the mode.
};
// DisplayInfo contains metadata for each display. This is used to
......
......@@ -4,6 +4,7 @@
#include "ash/display/display_manager.h"
#include <algorithm>
#include <cmath>
#include <set>
#include <string>
......@@ -82,9 +83,12 @@ struct DisplayInfoSortFunctor {
};
struct DisplayModeMatcher {
DisplayModeMatcher(const gfx::Size& size) : size(size) {}
bool operator()(const DisplayMode& mode) { return mode.size == size; }
gfx::Size size;
DisplayModeMatcher(const DisplayMode& target_mode)
: target_mode(target_mode) {}
bool operator()(const DisplayMode& mode) {
return target_mode.IsEquivalent(mode);
}
DisplayMode target_mode;
};
struct ScaleComparator {
......@@ -429,6 +433,7 @@ void DisplayManager::SetDisplayUIScale(int64 display_id,
return;
}
// TODO(mukai): merge this implementation into SetDisplayMode().
DisplayInfoList display_info_list;
for (DisplayList::const_iterator iter = displays_.begin();
iter != displays_.end(); ++iter) {
......@@ -458,8 +463,10 @@ void DisplayManager::SetDisplayResolution(int64 display_id,
const DisplayInfo& display_info = GetDisplayInfo(display_id);
const std::vector<DisplayMode>& modes = display_info.display_modes();
DCHECK_NE(0u, modes.size());
DisplayMode target_mode;
target_mode.size = resolution;
std::vector<DisplayMode>::const_iterator iter =
std::find_if(modes.begin(), modes.end(), DisplayModeMatcher(resolution));
std::find_if(modes.begin(), modes.end(), DisplayModeMatcher(target_mode));
if (iter == modes.end()) {
LOG(WARNING) << "Unsupported resolution was requested:"
<< resolution.ToString();
......@@ -472,6 +479,51 @@ void DisplayManager::SetDisplayResolution(int64 display_id,
#endif
}
bool DisplayManager::SetDisplayMode(int64 display_id,
const DisplayMode& display_mode) {
if (IsInternalDisplayId(display_id)) {
SetDisplayUIScale(display_id, display_mode.ui_scale);
return false;
}
DisplayInfoList display_info_list;
bool display_property_changed = false;
bool resolution_changed = false;
for (DisplayList::const_iterator iter = displays_.begin();
iter != displays_.end(); ++iter) {
DisplayInfo info = GetDisplayInfo(iter->id());
if (info.id() == display_id) {
const std::vector<DisplayMode>& modes = info.display_modes();
std::vector<DisplayMode>::const_iterator iter =
std::find_if(modes.begin(),
modes.end(),
DisplayModeMatcher(display_mode));
if (iter == modes.end()) {
LOG(WARNING) << "Unsupported resolution was requested:"
<< display_mode.size.ToString();
return false;
}
display_modes_[display_id] = *iter;
if (info.bounds_in_native().size() != display_mode.size)
resolution_changed = true;
if (info.device_scale_factor() != display_mode.device_scale_factor) {
info.set_device_scale_factor(display_mode.device_scale_factor);
display_property_changed = true;
}
}
display_info_list.push_back(info);
}
if (display_property_changed) {
AddMirrorDisplayInfoIfAny(&display_info_list);
UpdateDisplays(display_info_list);
}
#if defined(OS_CHROMEOS)
if (resolution_changed && base::SysInfo::IsRunningOnChromeOS())
Shell::GetInstance()->display_configurator()->OnConfigurationChanged();
#endif
return resolution_changed;
}
void DisplayManager::RegisterDisplayProperty(
int64 display_id,
gfx::Display::Rotation rotation,
......@@ -485,6 +537,8 @@ void DisplayManager::RegisterDisplayProperty(
display_info_[display_id].set_rotation(rotation);
display_info_[display_id].SetColorProfile(color_profile);
// Just in case the preference file was corrupted.
// TODO(mukai): register |display_modes_| here as well, so the lookup for the
// default mode in GetActiveModeForDisplayId() gets much simpler.
if (0.5f <= ui_scale && ui_scale <= 2.0f)
display_info_[display_id].set_configured_ui_scale(ui_scale);
if (overscan_insets)
......@@ -497,6 +551,33 @@ void DisplayManager::RegisterDisplayProperty(
}
}
DisplayMode DisplayManager::GetActiveModeForDisplayId(int64 display_id) const {
DisplayMode selected_mode;
if (GetSelectedModeForDisplayId(display_id, &selected_mode))
return selected_mode;
// If 'selected' mode is empty, it should return the default mode. This means
// the native mode for the external display. Unfortunately this is not true
// for the internal display because restoring UI-scale doesn't register the
// restored mode to |display_mode_|, so it needs to look up the mode whose
// UI-scale value matches. See the TODO in RegisterDisplayProperty().
const DisplayInfo& info = GetDisplayInfo(display_id);
const std::vector<DisplayMode>& display_modes = info.display_modes();
if (IsInternalDisplayId(display_id)) {
for (size_t i = 0; i < display_modes.size(); ++i) {
if (info.configured_ui_scale() == display_modes[i].ui_scale)
return display_modes[i];
}
} else {
for (size_t i = 0; i < display_modes.size(); ++i) {
if (display_modes[i].native)
return display_modes[i];
}
}
return selected_mode;
}
bool DisplayManager::GetSelectedModeForDisplayId(int64 id,
DisplayMode* mode_out) const {
std::map<int64, DisplayMode>::const_iterator iter = display_modes_.find(id);
......@@ -597,7 +678,10 @@ void DisplayManager::OnNativeDisplaysChanged(
new_display_info_list.push_back(*iter);
}
const gfx::Size& resolution = iter->bounds_in_native().size();
DisplayMode new_mode;
new_mode.size = iter->bounds_in_native().size();
new_mode.device_scale_factor = iter->device_scale_factor();
new_mode.ui_scale = iter->configured_ui_scale();
const std::vector<DisplayMode>& display_modes = iter->display_modes();
// This is empty the displays are initialized from InitFromCommandLine.
if (!display_modes.size())
......@@ -605,7 +689,7 @@ void DisplayManager::OnNativeDisplaysChanged(
std::vector<DisplayMode>::const_iterator display_modes_iter =
std::find_if(display_modes.begin(),
display_modes.end(),
DisplayModeMatcher(resolution));
DisplayModeMatcher(new_mode));
// Update the actual resolution selected as the resolution request may fail.
if (display_modes_iter == display_modes.end())
display_modes_.erase(iter->id());
......
......@@ -162,11 +162,19 @@ class ASH_EXPORT DisplayManager
void SetDisplayRotation(int64 display_id, gfx::Display::Rotation rotation);
// Sets the display's ui scale.
// TODO(mukai): remove this and merge into SetDisplayMode.
void SetDisplayUIScale(int64 display_id, float ui_scale);
// Sets the display's resolution.
// TODO(mukai): remove this and merge into SetDisplayMode.
void SetDisplayResolution(int64 display_id, const gfx::Size& resolution);
// Sets the external display's configuration, including resolution change,
// ui-scale change, and device scale factor change. Returns true if it changes
// the display resolution so that the caller needs to show a notification in
// case the new resolution actually doesn't work.
bool SetDisplayMode(int64 display_id, const DisplayMode& display_mode);
// Register per display properties. |overscan_insets| is NULL if
// the display has no custom overscan insets.
void RegisterDisplayProperty(int64 display_id,
......@@ -176,7 +184,11 @@ class ASH_EXPORT DisplayManager
const gfx::Size& resolution_in_pixels,
ui::ColorCalibrationProfile color_profile);
// Returns the display's selected mode.
// Returns the display mode of |display_id| which is currently used.
DisplayMode GetActiveModeForDisplayId(int64 display_id) const;
// Returns the display's selected mode. This returns false and doesn't
// set |mode_out| if the display mode is in default.
bool GetSelectedModeForDisplayId(int64 display_id,
DisplayMode* mode_out) const;
......
......@@ -736,13 +736,19 @@ TEST_F(DisplayManagerTest, DontRememberBestResolution) {
display_manager()->OnNativeDisplaysChanged(display_info_list);
DisplayMode mode;
DisplayMode expected_mode;
expected_mode.size = gfx::Size(1000, 500);
EXPECT_FALSE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
// Unsupported resolution.
display_manager()->SetDisplayResolution(display_id, gfx::Size(800, 4000));
EXPECT_FALSE(
display_manager()->GetSelectedModeForDisplayId(display_id, &mode));
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
// Supported resolution.
display_manager()->SetDisplayResolution(display_id, gfx::Size(800, 300));
......@@ -751,6 +757,9 @@ TEST_F(DisplayManagerTest, DontRememberBestResolution) {
EXPECT_EQ("800x300", mode.size.ToString());
EXPECT_EQ(59.0f, mode.refresh_rate);
EXPECT_FALSE(mode.native);
expected_mode.size = gfx::Size(800, 300);
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
// Best resolution.
display_manager()->SetDisplayResolution(display_id, gfx::Size(1000, 500));
......@@ -759,6 +768,9 @@ TEST_F(DisplayManagerTest, DontRememberBestResolution) {
EXPECT_EQ("1000x500", mode.size.ToString());
EXPECT_EQ(58.0f, mode.refresh_rate);
EXPECT_TRUE(mode.native);
expected_mode.size = gfx::Size(1000, 500);
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
}
TEST_F(DisplayManagerTest, ResolutionFallback) {
......@@ -965,6 +977,88 @@ TEST_F(DisplayManagerTest, UIScale) {
EXPECT_EQ("1280x850", display.bounds().size().ToString());
}
TEST_F(DisplayManagerTest, UIScaleWithDisplayMode) {
int display_id = 1000;
// Setup the display modes with UI-scale.
DisplayInfo native_display_info =
CreateDisplayInfo(display_id, gfx::Rect(0, 0, 1280, 800));
std::vector<DisplayMode> display_modes;
const DisplayMode base_mode(gfx::Size(1280, 800), 60.0f, false, false);
std::vector<float> scales =
DisplayManager::GetScalesForDisplay(native_display_info);
for (size_t i = 0; i < scales.size(); i++) {
DisplayMode mode = base_mode;
mode.ui_scale = scales[i];
mode.native = (scales[i] == 1.0f);
display_modes.push_back(mode);
}
native_display_info.set_display_modes(display_modes);
std::vector<DisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
DisplayMode expected_mode = base_mode;
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 1.125f);
EXPECT_EQ(1.0, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.8f);
EXPECT_EQ(1.0f, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.75f);
EXPECT_EQ(1.0f, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.625f);
EXPECT_EQ(1.0f, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
gfx::Display::SetInternalDisplayId(display_id);
display_manager()->SetDisplayUIScale(display_id, 1.5f);
EXPECT_EQ(1.0f, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 1.25f);
EXPECT_EQ(1.0f, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 1.125f);
EXPECT_EQ(1.125f, GetDisplayInfoAt(0).configured_ui_scale());
expected_mode.ui_scale = 1.125f;
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.8f);
EXPECT_EQ(0.8f, GetDisplayInfoAt(0).configured_ui_scale());
expected_mode.ui_scale = 0.8f;
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.75f);
EXPECT_EQ(0.8f, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.625f);
EXPECT_EQ(0.625f, GetDisplayInfoAt(0).configured_ui_scale());
expected_mode.ui_scale = 0.625f;
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.6f);
EXPECT_EQ(0.625f, GetDisplayInfoAt(0).configured_ui_scale());
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
display_manager()->SetDisplayUIScale(display_id, 0.5f);
EXPECT_EQ(0.5f, GetDisplayInfoAt(0).configured_ui_scale());
expected_mode.ui_scale = 0.5f;
EXPECT_TRUE(expected_mode.IsEquivalent(
display_manager()->GetActiveModeForDisplayId(display_id)));
}
TEST_F(DisplayManagerTest, UIScaleUpgradeToHighDPI) {
int64 display_id = Shell::GetScreen()->GetPrimaryDisplay().id();
gfx::Display::SetInternalDisplayId(display_id);
......
......@@ -5,6 +5,7 @@
#include "ash/display/resolution_notification_controller.h"
#include "ash/display/display_controller.h"
#include "ash/display/display_info.h"
#include "ash/display/display_manager.h"
#include "ash/shell.h"
#include "ash/system/system_notifier.h"
......@@ -102,8 +103,8 @@ const char ResolutionNotificationController::kNotificationId[] =
struct ResolutionNotificationController::ResolutionChangeInfo {
ResolutionChangeInfo(int64 display_id,
const gfx::Size& old_resolution,
const gfx::Size& new_resolution,
const DisplayMode& old_resolution,
const DisplayMode& new_resolution,
const base::Closure& accept_callback);
~ResolutionChangeInfo();
......@@ -111,14 +112,14 @@ struct ResolutionNotificationController::ResolutionChangeInfo {
int64 display_id;
// The resolution before the change.
gfx::Size old_resolution;
DisplayMode old_resolution;
// The requested resolution. Note that this may be different from
// |current_resolution| which is the actual resolution set.
gfx::Size new_resolution;
DisplayMode new_resolution;
// The actual resolution after the change.
gfx::Size current_resolution;
DisplayMode current_resolution;
// The callback when accept is chosen.
base::Closure accept_callback;
......@@ -137,8 +138,8 @@ struct ResolutionNotificationController::ResolutionChangeInfo {
ResolutionNotificationController::ResolutionChangeInfo::ResolutionChangeInfo(
int64 display_id,
const gfx::Size& old_resolution,
const gfx::Size& new_resolution,
const DisplayMode& old_resolution,
const DisplayMode& new_resolution,
const base::Closure& accept_callback)
: display_id(display_id),
old_resolution(old_resolution),
......@@ -166,29 +167,24 @@ ResolutionNotificationController::~ResolutionNotificationController() {
Shell::GetScreen()->RemoveObserver(this);
}
void ResolutionNotificationController::SetDisplayResolutionAndNotify(
void ResolutionNotificationController::PrepareNotification(
int64 display_id,
const gfx::Size& old_resolution,
const gfx::Size& new_resolution,
const DisplayMode& old_resolution,
const DisplayMode& new_resolution,
const base::Closure& accept_callback) {
// If multiple resolution changes are invoked for the same display,
// the original resolution for the first resolution change has to be used
// instead of the specified |old_resolution|.
gfx::Size original_resolution;
DisplayMode original_resolution;
if (change_info_ && change_info_->display_id == display_id) {
DCHECK(change_info_->new_resolution == old_resolution);
DCHECK(change_info_->new_resolution.size == old_resolution.size);
original_resolution = change_info_->old_resolution;
}
change_info_.reset(new ResolutionChangeInfo(
display_id, old_resolution, new_resolution, accept_callback));
if (!original_resolution.IsEmpty())
if (!original_resolution.size.IsEmpty())
change_info_->old_resolution = original_resolution;
// SetDisplayResolution() causes OnConfigurationChanged() and the notification
// will be shown at that point.
Shell::GetInstance()->display_manager()->SetDisplayResolution(
display_id, new_resolution);
}
bool ResolutionNotificationController::DoesNotificationTimeout() {
......@@ -224,16 +220,17 @@ void ResolutionNotificationController::CreateOrUpdateNotification(
Shell::GetInstance()->display_manager()->GetDisplayNameForId(
change_info_->display_id));
const base::string16 message =
(change_info_->new_resolution == change_info_->current_resolution) ?
(change_info_->new_resolution.size ==
change_info_->current_resolution.size) ?
l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
display_name,
base::UTF8ToUTF16(change_info_->new_resolution.ToString())) :
base::UTF8ToUTF16(change_info_->new_resolution.size.ToString())) :
l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED_TO_UNSUPPORTED,
display_name,
base::UTF8ToUTF16(change_info_->new_resolution.ToString()),
base::UTF8ToUTF16(change_info_->current_resolution.ToString()));
base::UTF8ToUTF16(change_info_->new_resolution.size.ToString()),
base::UTF8ToUTF16(change_info_->current_resolution.size.ToString()));
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
scoped_ptr<Notification> notification(new Notification(
......@@ -279,9 +276,9 @@ void ResolutionNotificationController::RevertResolutionChange() {
message_center::MessageCenter::Get()->RemoveNotification(
kNotificationId, false /* by_user */);
int64 display_id = change_info_->display_id;
gfx::Size old_resolution = change_info_->old_resolution;
DisplayMode old_resolution = change_info_->old_resolution;
change_info_.reset();
Shell::GetInstance()->display_manager()->SetDisplayResolution(
Shell::GetInstance()->display_manager()->SetDisplayMode(
display_id, old_resolution);
}
......@@ -303,9 +300,8 @@ void ResolutionNotificationController::OnDisplayConfigurationChanged() {
if (!change_info_)
return;
const DisplayInfo& info = Shell::GetInstance()->display_manager()->
GetDisplayInfo(change_info_->display_id);
change_info_->current_resolution = info.bounds_in_native().size();
change_info_->current_resolution = Shell::GetInstance()->display_manager()->
GetActiveModeForDisplayId(change_info_->display_id);
CreateOrUpdateNotification(true);
if (g_use_timer && change_info_->timeout_count > 0) {
change_info_->timer.Start(FROM_HERE,
......
......@@ -24,6 +24,8 @@ class Widget;
namespace ash {
struct DisplayMode;
// A class which manages the notification of display resolution change and
// also manages the timeout in case the new resolution is unusable.
class ASH_EXPORT ResolutionNotificationController
......@@ -33,17 +35,22 @@ class ASH_EXPORT ResolutionNotificationController
ResolutionNotificationController();
virtual ~ResolutionNotificationController();
// Updates the display resolution for |display_id| to |new_resolution| and
// creates a notification for this change which offers a button to revert the
// Prepare a resolution change notification for |display_id| from
// |old_resolution| to |new_resolution|, which offers a button to revert the
// change in case something goes wrong. The notification times out if there's
// only one display connected and the user is trying to modify its resolution.
// In that case, the timeout has to be set since the user cannot make any
// changes if something goes wrong.
void SetDisplayResolutionAndNotify(
int64 display_id,
const gfx::Size& old_resolution,
const gfx::Size& new_resolution,
const base::Closure& accept_callback);
//
// This method does not create a notification itself. The notification will be
// created the next OnDisplayConfigurationChanged(), which will be called
// asynchronously after the resolution change is requested. So typically this
// method will be combined with resolution change methods like
// DisplayManager::SetDisplayMode().
void PrepareNotification(int64 display_id,
const DisplayMode& old_resolution,
const DisplayMode& new_resolution,
const base::Closure& accept_callback);
// Returns true if the notification is visible or scheduled to be visible and
// the notification times out.
......
......@@ -64,13 +64,23 @@ class ResolutionNotificationControllerTest : public ash::test::AshTestBase {
const gfx::Size& new_resolution,
const gfx::Size& actual_new_resolution) {
DisplayManager* display_manager = Shell::GetInstance()->display_manager();
const DisplayInfo& info = display_manager->GetDisplayInfo(display.id());
controller()->SetDisplayResolutionAndNotify(
display.id(),
info.size_in_pixel(),
new_resolution,
base::Bind(&ResolutionNotificationControllerTest::OnAccepted,
base::Unretained(this)));
DisplayMode old_mode(info.size_in_pixel(),
60 /* refresh_rate */,
false /* interlaced */,
false /* native */);
DisplayMode new_mode = old_mode;
new_mode.size = new_resolution;
if (display_manager->SetDisplayMode(display.id(), new_mode)) {
controller()->PrepareNotification(
display.id(),
old_mode,
new_mode,
base::Bind(&ResolutionNotificationControllerTest::OnAccepted,
base::Unretained(this)));
}
// OnConfigurationChanged event won't be emitted in the test environment,
// so invoke UpdateDisplay() to emit that event explicitly.
......
......@@ -424,9 +424,15 @@ TEST_F(DisplayPreferencesTest, PreventStore) {
int64 id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
// Set display's resolution in single display. It creates the notification and
// display preferences should not stored meanwhile.
ash::Shell::GetInstance()->resolution_notification_controller()->
SetDisplayResolutionAndNotify(
id, gfx::Size(400, 300), gfx::Size(500, 400), base::Closure());
ash::Shell* shell = ash::Shell::GetInstance();
ash::DisplayMode old_mode;
ash::DisplayMode new_mode;
old_mode.size = gfx::Size(400, 300);
new_mode.size = gfx::Size(500, 400);
if (shell->display_manager()->SetDisplayMode(id, new_mode)) {
shell->resolution_notification_controller()->PrepareNotification(
id, old_mode, new_mode, base::Closure());
}
UpdateDisplay("500x400#500x400|400x300|300x200");
const base::DictionaryValue* properties =
......
......@@ -149,10 +149,10 @@ cr.define('options', function() {
initializePage: function() {
Page.prototype.initializePage.call(this);
$('display-options-toggle-mirroring').onclick = function() {
$('display-options-toggle-mirroring').onclick = (function() {
this.mirroring_ = !this.mirroring_;
chrome.send('setMirroring', [this.mirroring_]);
}.bind(this);
}).bind(this);
var container = $('display-options-displays-view-host');
container.onmousemove = this.onMouseMove_.bind(this);
......@@ -160,28 +160,23 @@ cr.define('options', function() {
container.ontouchmove = this.onTouchMove_.bind(this);
container.ontouchend = this.endDragging_.bind(this);
$('display-options-set-primary').onclick = function() {
$('display-options-set-primary').onclick = (function() {
chrome.send('setPrimary', [this.displays_[this.focusedIndex_].id]);
}.bind(this);
$('display-options-resolution-selection').onchange = function(ev) {
}).bind(this);
$('display-options-resolution-selection').onchange = (function(ev) {
var display = this.displays_[this.focusedIndex_];
var resolution = display.resolutions[ev.target.value];
if (resolution.scale) {
chrome.send('setUIScale', [display.id, resolution.scale]);
} else {
chrome.send('setResolution',
[display.id, resolution.width, resolution.height]);
}
}.bind(this);
$('display-options-orientation-selection').onchange = function(ev) {
chrome.send('setDisplayMode', [display.id, resolution]);
}).bind(this);
$('display-options-orientation-selection').onchange = (function(ev) {
chrome.send('setOrientation', [this.displays_[this.focusedIndex_].id,
ev.target.value]);
}.bind(this);
$('display-options-color-profile-selection').onchange = function(ev) {
}).bind(this);
$('display-options-color-profile-selection').onchange = (function(ev) {
chrome.send('setColorProfile', [this.displays_[this.focusedIndex_].id,
ev.target.value]);
}.bind(this);
$('selected-display-start-calibrating-overscan').onclick = function() {
}).bind(this);
$('selected-display-start-calibrating-overscan').onclick = (function() {
// Passes the target display ID. Do not specify it through URL hash,
// we do not care back/forward.
var displayOverscan = options.DisplayOverscan.getInstance();
......@@ -189,7 +184,7 @@ cr.define('options', function() {
PageManager.showPageByName('displayOverscan');
chrome.send('coreOptionsUserMetricsAction',
['Options_DisplaySetOverscan']);
}.bind(this);
}).bind(this);
chrome.send('getDisplayInfo');
},
......@@ -613,6 +608,7 @@ cr.define('options', function() {
resolution.appendChild(option);
resolution.disabled = true;
} else {
var previousOption;
for (var i = 0; i < display.resolutions.length; i++) {
var option = document.createElement('option');
option.value = i;
......@@ -622,8 +618,14 @@ cr.define('options', function() {
option.textContent += ' ' +
loadTimeData.getString('annotateBest');
}
if (display.resolutions[i].deviceScaleFactor && previousOption &&
previousOption.textContent == option.textContent) {
option.textContent +=
' (' + display.resolutions[i].deviceScaleFactor + 'x)';
}
option.selected = display.resolutions[i].selected;
resolution.appendChild(option);
previousOption = option;
}
resolution.disabled = (display.resolutions.length <= 1);
}
......
......@@ -54,12 +54,6 @@ int64 GetDisplayId(const base::ListValue* args) {
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:
......@@ -82,6 +76,69 @@ base::string16 GetColorProfileName(ui::ColorCalibrationProfile profile) {
return base::string16();
}
int GetIntOrDouble(const base::DictionaryValue* dict,
const std::string& field) {
double double_result = 0;
if (dict->GetDouble(field, &double_result))
return static_cast<int>(double_result);
int result = 0;
dict->GetInteger(field, &result);
return result;
}
bool GetFloat(const base::DictionaryValue* dict,
const std::string& field,
float* result) {
double double_result = 0;
if (dict->GetDouble(field, &double_result)) {
*result = static_cast<float>(double_result);
return true;
}
return false;
}
bool ConvertValueToDisplayMode(const base::DictionaryValue* dict,
ash::DisplayMode* mode) {
mode->size.set_width(GetIntOrDouble(dict, "originalWidth"));
mode->size.set_height(GetIntOrDouble(dict, "originalHeight"));
if (mode->size.IsEmpty()) {
LOG(ERROR) << "missing width or height.";
return false;
}
if (!GetFloat(dict, "refreshRate", &mode->refresh_rate)) {
LOG(ERROR) << "missing refreshRate.";
return false;
}
if (!GetFloat(dict, "scale", &mode->ui_scale)) {
LOG(ERROR) << "missing ui-scale.";
return false;
}
if (!GetFloat(dict, "deviceScaleFactor", &mode->device_scale_factor)) {
LOG(ERROR) << "missing deviceScaleFactor.";
return false;
}
return true;
}
base::DictionaryValue* ConvertDisplayModeToValue(int64 display_id,
const ash::DisplayMode& mode) {
base::DictionaryValue* result = new base::DictionaryValue();
gfx::Size size_dip = mode.GetSizeInDIP();
result->SetInteger("width", size_dip.width());
result->SetInteger("height", size_dip.height());
result->SetInteger("originalWidth", mode.size.width());
result->SetInteger("originalHeight", mode.size.height());
result->SetDouble("deviceScaleFactor", mode.device_scale_factor);
result->SetDouble("scale", mode.ui_scale);
result->SetDouble("refreshRate", mode.refresh_rate);
result->SetBoolean("isBest", mode.native);
result->SetBoolean(
"selected", mode.IsEquivalent(
GetDisplayManager()->GetActiveModeForDisplayId(display_id)));
return result;
}
} // namespace
DisplayOptionsHandler::DisplayOptionsHandler() {
......@@ -159,12 +216,8 @@ void DisplayOptionsHandler::RegisterMessages() {
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,
"setDisplayMode",
base::Bind(&DisplayOptionsHandler::HandleSetDisplayMode,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"setOrientation",
......@@ -217,55 +270,13 @@ void DisplayOptionsHandler::SendDisplayInfo(
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();
const std::vector<ash::DisplayMode>& display_modes =
display_info.display_modes();
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_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_resolutions->Append(
ConvertDisplayModeToValue(display.id(), display_modes[i]));
}
js_display->Set("resolutions", js_resolutions);
......@@ -359,69 +370,33 @@ void DisplayOptionsHandler::HandleDisplayLayout(const base::ListValue* args) {
static_cast<int>(offset)));
}
void DisplayOptionsHandler::HandleSetUIScale(const base::ListValue* args) {
void DisplayOptionsHandler::HandleSetDisplayMode(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";
const base::DictionaryValue* mode_data = NULL;
if (!args->GetDictionary(1, &mode_data)) {
LOG(ERROR) << "Failed to get mode data";
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)
ash::DisplayMode mode;
if (!ConvertValueToDisplayMode(mode_data, &mode))
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;
ash::DisplayManager* display_manager = GetDisplayManager();
ash::DisplayMode current_mode =
display_manager->GetActiveModeForDisplayId(display_id);
if (display_manager->SetDisplayMode(display_id, mode)) {
ash::Shell::GetInstance()->resolution_notification_controller()->
PrepareNotification(
display_id, current_mode, mode, base::Bind(&StoreDisplayPrefs));
}
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::Size new_resolution = gfx::ToFlooredSize(gfx::SizeF(width, 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) {
......
......@@ -58,8 +58,7 @@ class DisplayOptionsHandler : public ::options::OptionsPageUIHandler,
void HandleMirroring(const base::ListValue* args);
void HandleSetPrimary(const base::ListValue* args);
void HandleDisplayLayout(const base::ListValue* args);
void HandleSetUIScale(const base::ListValue* args);
void HandleSetResolution(const base::ListValue* args);
void HandleSetDisplayMode(const base::ListValue* args);
void HandleSetOrientation(const base::ListValue* args);
void HandleSetColorProfile(const base::ListValue* args);
......
......@@ -8361,7 +8361,10 @@ should be able to be added at any place in this file.
<action name="Options_DisplaySetResolution">
<owner>stevenjb@chromium.org</owner>
<description>Settings: Device: Display settings: Resolution</description>
<description>
Settings: Device: Display settings: Display mode (i.e. resolution, UI-scale,
and device scale factor).
</description>
</action>
<action name="Options_DisplayToggleMirroring">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment