[go: nahoru, domu]

blob: 84e1304811dceeb17a1dee0b88d8cd39a5958274 [file] [log] [blame]
// Copyright 2014 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_util.h"
#include <stddef.h>
#include <algorithm>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/types/display_snapshot.h"
namespace display {
namespace {
// The maximum logical resolution width allowed when zooming out for a display.
constexpr int kDefaultMaxZoomWidth = 4096;
// The minimum logical resolution width allowed when zooming in for a display.
constexpr int kDefaultMinZoomWidth = 640;
// The total number of display zoom factors to enumerate.
constexpr int kNumOfZoomFactors = 9;
bool WithinEpsilon(float a, float b) {
return std::abs(a - b) < std::numeric_limits<float>::epsilon();
}
// Clamps the delta between consecutive zoom factors to a user friendly and UI
// friendly value.
float ClampToUserFriendlyDelta(float delta) {
// NOTE: If these thresholds are updated, please also update aura-shell.xml.
// The list of deltas between two consecutive zoom level. Any display must
// have one of these values as the difference between two consecutive zoom
// level.
// The array of pair represents which user friendly delta value must the given
// raw |delta| be clamped to.
// std::pair::first - represents the threshold.
// std::pair::second - represents the user friendly clamped delta
constexpr std::array<std::pair<float, float>, 7> kZoomFactorDeltas = {
{{0.05f, 0.05f},
{0.1f, 0.1f},
{0.15f, 0.15f},
{0.2f, 0.2f},
{0.25f, 0.25f},
{0.7f, 0.3f},
{1.f, 0.5f}}};
std::size_t delta_index = 0;
while (delta_index < kZoomFactorDeltas.size() &&
delta >= kZoomFactorDeltas[delta_index].first) {
delta_index++;
}
return kZoomFactorDeltas[delta_index - 1].second;
}
} // namespace
std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) {
switch (state) {
case chromeos::DISPLAY_POWER_ALL_ON:
return "ALL_ON";
case chromeos::DISPLAY_POWER_ALL_OFF:
return "ALL_OFF";
case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON:
return "INTERNAL_OFF_EXTERNAL_ON";
case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF:
return "INTERNAL_ON_EXTERNAL_OFF";
default:
return "unknown (" + base::IntToString(state) + ")";
}
}
std::string MultipleDisplayStateToString(MultipleDisplayState state) {
switch (state) {
case MULTIPLE_DISPLAY_STATE_INVALID:
return "INVALID";
case MULTIPLE_DISPLAY_STATE_HEADLESS:
return "HEADLESS";
case MULTIPLE_DISPLAY_STATE_SINGLE:
return "SINGLE";
case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR:
return "DUAL_MIRROR";
case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED:
return "MULTI_EXTENDED";
}
NOTREACHED() << "Unknown state " << state;
return "INVALID";
}
int GetDisplayPower(const std::vector<DisplaySnapshot*>& displays,
chromeos::DisplayPowerState state,
std::vector<bool>* display_power) {
int num_on_displays = 0;
if (display_power)
display_power->resize(displays.size());
for (size_t i = 0; i < displays.size(); ++i) {
bool internal = displays[i]->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
bool on =
state == chromeos::DISPLAY_POWER_ALL_ON ||
(state == chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON &&
!internal) ||
(state == chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && internal);
if (display_power)
(*display_power)[i] = on;
if (on)
num_on_displays++;
}
return num_on_displays;
}
bool IsPhysicalDisplayType(DisplayConnectionType type) {
return !(type & DISPLAY_CONNECTION_TYPE_NETWORK);
}
std::vector<float> GetDisplayZoomFactors(const ManagedDisplayMode& mode) {
const int effective_width = std::round(
static_cast<float>(mode.size().width()) / mode.device_scale_factor());
// We want to support displays greater than 4K. This is added to ensure the
// zoom does not break in such cases.
const int max_width = std::max(effective_width, kDefaultMaxZoomWidth);
const int min_width = std::min(effective_width, kDefaultMinZoomWidth);
// The logical resolution will vary from half of the mode resolution to double
// the mode resolution.
int max_effective_width =
std::min(static_cast<int>(std::round(effective_width * 2.f)), max_width);
int min_effective_width =
std::max(static_cast<int>(std::round(effective_width / 2.f)), min_width);
// If either the maximum width or minimum width was reached in the above step
// and clamping was performed, then update the total range of logical
// resolutions and ensure that everything lies within the maximum and minimum
// resolution range.
const int interval = std::round(static_cast<float>(effective_width) * 1.5f);
if (max_effective_width == max_width)
min_effective_width = std::max(max_effective_width - interval, min_width);
if (min_effective_width == min_width)
max_effective_width = std::min(min_effective_width + interval, max_width);
float max_zoom = static_cast<float>(effective_width) /
static_cast<float>(min_effective_width);
float min_zoom = static_cast<float>(effective_width) /
static_cast<float>(max_effective_width);
float delta =
(max_zoom - min_zoom) / static_cast<float>(kNumOfZoomFactors - 1);
// Number of zoom values above 100% zoom.
const int zoom_in_count = std::round((max_zoom - 1.f) / delta);
// Number of zoom values below 100% zoom.
const int zoom_out_count = kNumOfZoomFactors - zoom_in_count - 1;
delta = ClampToUserFriendlyDelta(delta);
// Populate the zoom values list.
min_zoom = 1.f - delta * zoom_out_count;
std::vector<float> zoom_values;
for (int i = 0; i < kNumOfZoomFactors; i++)
zoom_values.push_back(min_zoom + i * delta);
// Make sure the inverse of the internal device scale factor is included in
// the list. This ensures that the users have a way to go to the native
// resolution and 1.0 effective device scale factor.
InsertDsfIntoList(&zoom_values, 1.f / mode.device_scale_factor());
DCHECK_EQ(zoom_values.size(), static_cast<std::size_t>(kNumOfZoomFactors));
return zoom_values;
}
void InsertDsfIntoList(std::vector<float>* zoom_values, float dsf) {
// 1.0 is already in the list of |zoom_values|. We do not need to add it.
if (WithinEpsilon(dsf, 1.f))
return;
if (dsf > 1.f && WithinEpsilon(*(zoom_values->rbegin()), 1.f)) {
// If the last element of the vector is 1 then |dsf|, which is greater than
// 1, will simply be inserted after that.
zoom_values->push_back(dsf);
zoom_values->erase(zoom_values->begin());
return;
} else if (dsf < 1.f && WithinEpsilon(*(zoom_values->begin()), 1.f)) {
// If the first element in the list is 1 then |dsf|, which is less than 1,
// will simply be inseted before that.
zoom_values->insert(zoom_values->begin(), dsf);
zoom_values->pop_back();
return;
}
// We dont need to add |dsf| to the list if it is already in the list.
auto it = std::lower_bound(zoom_values->begin(), zoom_values->end(), dsf);
if (WithinEpsilon(*it, dsf))
return;
if (it == zoom_values->begin()) {
DCHECK_LT(dsf, 1.f);
*(zoom_values->begin()) = dsf;
} else if (it == zoom_values->end()) {
DCHECK_LT(dsf, 1.f);
*(zoom_values->rbegin()) = dsf;
} else {
// There can only be 1 entry for 1.f value.
DCHECK(!(WithinEpsilon(*(it - 1), 1.f) && WithinEpsilon(*it, 1.f)));
// True if |dsf| is closer to |it| than it is to |it-1|.
const bool dsf_closer_to_it =
std::abs(*it - dsf) < std::abs(*(it - 1) - dsf);
if (WithinEpsilon(*(it - 1), 1.f) ||
(dsf_closer_to_it && !WithinEpsilon(*it, 1.f))) {
*it = dsf;
} else {
*(it - 1) = dsf;
}
}
}
} // namespace display