blob: af1c42d5bcb0d786163cbdba6d62a26e1f74447e [file] [log] [blame]
// Copyright 2016 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 "ui/display/manager/display_manager_utilities.h"
#include <algorithm>
#include <vector>
#include "base/sys_info.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/size_f.h"
namespace display {
namespace {
// List of value UI Scale values. Scales for 2x are equivalent to 640,
// 800, 1024, 1280, 1440, 1600 and 1920 pixel width respectively on
// 2560 pixel width 2x density display. Please see crbug.com/233375
// for the full list of resolutions.
constexpr float kUIScalesFor2x[] = {0.5f, 0.625f, 0.8f, 1.0f,
1.125f, 1.25f, 1.5f, 2.0f};
constexpr float kUIScalesFor1_25x[] = {0.5f, 0.625f, 0.8f, 1.0f, 1.25f};
constexpr float kUIScalesFor1280[] = {0.5f, 0.625f, 0.8f, 1.0f, 1.125f};
constexpr float kUIScalesFor1366[] = {0.5f, 0.6f, 0.75f, 1.0f, 1.125f};
// The default UI scales for the above display densities.
constexpr float kDefaultUIScaleFor2x = 1.0f;
constexpr float kDefaultUIScaleFor1_25x = 0.8f;
constexpr float kDefaultUIScaleFor1280 = 1.0f;
constexpr float kDefaultUIScaleFor1366 = 1.0f;
// Encapsulates the list of UI scales and the default one.
struct DisplayUIScales {
std::vector<float> scales;
float default_scale;
};
DisplayUIScales GetScalesForDisplay(
const scoped_refptr<ManagedDisplayMode>& native_mode) {
#define ASSIGN_ARRAY(v, a) v.assign(a, a + arraysize(a))
DisplayUIScales ret;
if (native_mode->device_scale_factor() == 2.0f) {
ASSIGN_ARRAY(ret.scales, kUIScalesFor2x);
ret.default_scale = kDefaultUIScaleFor2x;
return ret;
} else if (native_mode->device_scale_factor() == 1.25f) {
ASSIGN_ARRAY(ret.scales, kUIScalesFor1_25x);
ret.default_scale = kDefaultUIScaleFor1_25x;
return ret;
}
switch (native_mode->size().width()) {
case 1280:
ASSIGN_ARRAY(ret.scales, kUIScalesFor1280);
ret.default_scale = kDefaultUIScaleFor1280;
break;
case 1366:
ASSIGN_ARRAY(ret.scales, kUIScalesFor1366);
ret.default_scale = kDefaultUIScaleFor1366;
break;
default:
ASSIGN_ARRAY(ret.scales, kUIScalesFor1280);
ret.default_scale = kDefaultUIScaleFor1280;
#if defined(OS_CHROMEOS)
if (base::SysInfo::IsRunningOnChromeOS())
NOTREACHED() << "Unknown resolution:" << native_mode->size().ToString();
#endif
}
return ret;
}
struct ScaleComparator {
explicit ScaleComparator(float s) : scale(s) {}
bool operator()(const scoped_refptr<ManagedDisplayMode>& mode) const {
const float kEpsilon = 0.0001f;
return std::abs(scale - mode->ui_scale()) < kEpsilon;
}
float scale;
};
scoped_refptr<ManagedDisplayMode> FindNextMode(
const ManagedDisplayInfo::ManagedDisplayModeList& modes,
size_t index,
bool up) {
DCHECK_LT(index, modes.size());
size_t new_index = index;
if (up && (index + 1 < modes.size()))
++new_index;
else if (!up && index != 0)
--new_index;
return modes[new_index];
}
} // namespace
ManagedDisplayInfo::ManagedDisplayModeList CreateInternalManagedDisplayModeList(
const scoped_refptr<ManagedDisplayMode>& native_mode) {
ManagedDisplayInfo::ManagedDisplayModeList display_mode_list;
float native_ui_scale = (native_mode->device_scale_factor() == 1.25f)
? 1.0f
: native_mode->device_scale_factor();
const DisplayUIScales display_ui_scales = GetScalesForDisplay(native_mode);
for (float ui_scale : display_ui_scales.scales) {
scoped_refptr<ManagedDisplayMode> mode(new ManagedDisplayMode(
native_mode->size(), native_mode->refresh_rate(),
native_mode->is_interlaced(), ui_scale == native_ui_scale, ui_scale,
native_mode->device_scale_factor()));
mode->set_is_default(ui_scale == display_ui_scales.default_scale);
display_mode_list.push_back(mode);
}
return display_mode_list;
}
ManagedDisplayInfo::ManagedDisplayModeList CreateUnifiedManagedDisplayModeList(
const scoped_refptr<ManagedDisplayMode>& native_mode,
const std::set<std::pair<float, float>>& dsf_scale_list) {
ManagedDisplayInfo::ManagedDisplayModeList display_mode_list;
for (auto& pair : dsf_scale_list) {
gfx::SizeF scaled_size(native_mode->size());
scaled_size.Scale(pair.second);
scoped_refptr<ManagedDisplayMode> mode(new ManagedDisplayMode(
gfx::ToFlooredSize(scaled_size), native_mode->refresh_rate(),
native_mode->is_interlaced(), false /* native */,
native_mode->ui_scale(), pair.first /* device_scale_factor */));
display_mode_list.push_back(mode);
}
// Sort the mode by the size in DIP.
std::sort(display_mode_list.begin(), display_mode_list.end(),
[](const scoped_refptr<ManagedDisplayMode>& a,
const scoped_refptr<ManagedDisplayMode>& b) {
return a->GetSizeInDIP(false).GetArea() <
b->GetSizeInDIP(false).GetArea();
});
return display_mode_list;
}
scoped_refptr<ManagedDisplayMode> GetDisplayModeForResolution(
const ManagedDisplayInfo& info,
const gfx::Size& resolution) {
if (Display::IsInternalDisplayId(info.id()))
return scoped_refptr<ManagedDisplayMode>();
const ManagedDisplayInfo::ManagedDisplayModeList& modes =
info.display_modes();
DCHECK_NE(0u, modes.size());
scoped_refptr<ManagedDisplayMode> target_mode;
ManagedDisplayInfo::ManagedDisplayModeList::const_iterator iter =
std::find_if(modes.begin(), modes.end(),
[resolution](const scoped_refptr<ManagedDisplayMode>& mode) {
return mode->size() == resolution;
});
if (iter == modes.end()) {
LOG(WARNING) << "Unsupported resolution was requested:"
<< resolution.ToString();
return scoped_refptr<ManagedDisplayMode>();
}
return *iter;
}
scoped_refptr<ManagedDisplayMode> GetDisplayModeForNextUIScale(
const ManagedDisplayInfo& info,
bool up) {
const ManagedDisplayInfo::ManagedDisplayModeList& modes =
info.display_modes();
ScaleComparator comparator(info.configured_ui_scale());
auto iter = std::find_if(modes.begin(), modes.end(), comparator);
return FindNextMode(modes, iter - modes.begin(), up);
}
scoped_refptr<ManagedDisplayMode> GetDisplayModeForNextResolution(
const ManagedDisplayInfo& info,
bool up) {
if (Display::IsInternalDisplayId(info.id()))
return scoped_refptr<ManagedDisplayMode>();
const ManagedDisplayInfo::ManagedDisplayModeList& modes =
info.display_modes();
scoped_refptr<ManagedDisplayMode> tmp = new ManagedDisplayMode(
info.size_in_pixel(), 0.0, false, false, 1.0, info.device_scale_factor());
gfx::Size resolution = tmp->GetSizeInDIP(false);
auto iter =
std::find_if(modes.begin(), modes.end(),
[resolution](const scoped_refptr<ManagedDisplayMode>& mode) {
return mode->GetSizeInDIP(false) == resolution;
});
return FindNextMode(modes, iter - modes.begin(), up);
}
bool HasDisplayModeForUIScale(const ManagedDisplayInfo& info, float ui_scale) {
ScaleComparator comparator(ui_scale);
const ManagedDisplayInfo::ManagedDisplayModeList& modes =
info.display_modes();
return std::find_if(modes.begin(), modes.end(), comparator) != modes.end();
}
bool ComputeBoundary(const Display& a_display,
const Display& b_display,
gfx::Rect* a_edge_in_screen,
gfx::Rect* b_edge_in_screen) {
const gfx::Rect& a_bounds = a_display.bounds();
const gfx::Rect& b_bounds = b_display.bounds();
// Find touching side.
int rx = std::max(a_bounds.x(), b_bounds.x());
int ry = std::max(a_bounds.y(), b_bounds.y());
int rr = std::min(a_bounds.right(), b_bounds.right());
int rb = std::min(a_bounds.bottom(), b_bounds.bottom());
DisplayPlacement::Position position;
if ((rb - ry) == 0) {
// top bottom
if (a_bounds.bottom() == b_bounds.y()) {
position = DisplayPlacement::BOTTOM;
} else if (a_bounds.y() == b_bounds.bottom()) {
position = DisplayPlacement::TOP;
} else {
return false;
}
} else {
// left right
if (a_bounds.right() == b_bounds.x()) {
position = DisplayPlacement::RIGHT;
} else if (a_bounds.x() == b_bounds.right()) {
position = DisplayPlacement::LEFT;
} else {
DCHECK_NE(rr, rx);
return false;
}
}
switch (position) {
case DisplayPlacement::TOP:
case DisplayPlacement::BOTTOM: {
int left = std::max(a_bounds.x(), b_bounds.x());
int right = std::min(a_bounds.right(), b_bounds.right());
if (position == DisplayPlacement::TOP) {
a_edge_in_screen->SetRect(left, a_bounds.y(), right - left, 1);
b_edge_in_screen->SetRect(left, b_bounds.bottom() - 1, right - left, 1);
} else {
a_edge_in_screen->SetRect(left, a_bounds.bottom() - 1, right - left, 1);
b_edge_in_screen->SetRect(left, b_bounds.y(), right - left, 1);
}
break;
}
case DisplayPlacement::LEFT:
case DisplayPlacement::RIGHT: {
int top = std::max(a_bounds.y(), b_bounds.y());
int bottom = std::min(a_bounds.bottom(), b_bounds.bottom());
if (position == DisplayPlacement::LEFT) {
a_edge_in_screen->SetRect(a_bounds.x(), top, 1, bottom - top);
b_edge_in_screen->SetRect(b_bounds.right() - 1, top, 1, bottom - top);
} else {
a_edge_in_screen->SetRect(a_bounds.right() - 1, top, 1, bottom - top);
b_edge_in_screen->SetRect(b_bounds.x(), top, 1, bottom - top);
}
break;
}
}
return true;
}
DisplayIdList CreateDisplayIdList(const Displays& list) {
return GenerateDisplayIdList(
list.begin(), list.end(),
[](const Display& display) { return display.id(); });
}
void SortDisplayIdList(DisplayIdList* ids) {
std::sort(ids->begin(), ids->end(),
[](int64_t a, int64_t b) { return CompareDisplayIds(a, b); });
}
std::string DisplayIdListToString(const DisplayIdList& list) {
std::stringstream s;
const char* sep = "";
for (int64_t id : list) {
s << sep << id;
sep = ",";
}
return s.str();
}
bool CompareDisplayIds(int64_t id1, int64_t id2) {
DCHECK_NE(id1, id2);
// Output index is stored in the first 8 bits. See GetDisplayIdFromEDID
// in edid_parser.cc.
int index_1 = id1 & 0xFF;
int index_2 = id2 & 0xFF;
DCHECK_NE(index_1, index_2) << id1 << " and " << id2;
return Display::IsInternalDisplayId(id1) ||
(index_1 < index_2 && !Display::IsInternalDisplayId(id2));
}
} // namespace display