Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(896)

Unified Diff: chrome/browser/extensions/api/system_info_display/display_info_provider_chromeos.cc

Issue 16817006: Add ability to change display settings to chrome.systemInfo.display (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: win compile Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/api/system_info_display/display_info_provider_chromeos.cc
diff --git a/chrome/browser/extensions/api/system_info_display/display_info_provider_chromeos.cc b/chrome/browser/extensions/api/system_info_display/display_info_provider_chromeos.cc
index feb735dac512ee2a7282ec4999d242f378e9ead2..5fb380bc0b029f1a388798dab0adc64b07e6bf7a 100644
--- a/chrome/browser/extensions/api/system_info_display/display_info_provider_chromeos.cc
+++ b/chrome/browser/extensions/api/system_info_display/display_info_provider_chromeos.cc
@@ -4,22 +4,36 @@
#include "chrome/browser/extensions/api/system_info_display/display_info_provider.h"
+#include "ash/display/display_controller.h"
#include "ash/display/display_manager.h"
#include "ash/shell.h"
#include "base/message_loop_proxy.h"
#include "base/strings/string_number_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "ui/gfx/display.h"
+#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
using ash::internal::DisplayManager;
namespace extensions {
+using api::system_info_display::Bounds;
+using api::system_info_display::DisplayUnitInfo;
+using api::system_info_display::DisplayProperties;
+using api::system_info_display::Insets;
+
namespace {
// TODO(hshi): determine the DPI of the screen.
const float kDpi96 = 96.0;
+// Maximum allowed bounds origin absolute value.
+const int kMaxBoundsOrigin = 200 * 1000;
+
+// Checks if the given integer value is valid display rotation in degrees.
+bool IsValidRotationValue(int rotation) {
+ return rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270;
+}
// Converts Rotation enum to integer.
int RotationToDegrees(gfx::Display::Rotation rotation) {
@@ -36,10 +50,365 @@ int RotationToDegrees(gfx::Display::Rotation rotation) {
return 0;
}
-} // namespace
+// Converts integer integer value in degrees to Rotation enum value.
+gfx::Display::Rotation DegreesToRotation(int degrees) {
+ DCHECK(IsValidRotationValue(degrees));
+ switch (degrees) {
+ case 0:
+ return gfx::Display::ROTATE_0;
+ case 90:
+ return gfx::Display::ROTATE_90;
+ case 180:
+ return gfx::Display::ROTATE_180;
+ case 270:
+ return gfx::Display::ROTATE_270;
+ default:
+ return gfx::Display::ROTATE_0;
+ }
+}
-using api::system_info_display::Bounds;
-using api::system_info_display::DisplayUnitInfo;
+// Creates new DisplayUnitInfo struct for |display| and adds it at the end of
+// |list|.
+void AddInfoForDisplay(const gfx::Display& display,
+ DisplayManager* display_manager,
+ int64 primary_display_id,
+ DisplayInfo* list) {
+ linked_ptr<extensions::api::system_info_display::DisplayUnitInfo> unit(
+ new extensions::api::system_info_display::DisplayUnitInfo());
+ const gfx::Rect& bounds = display.bounds();
+ const gfx::Rect& work_area = display.work_area();
+ const float dpi = display.device_scale_factor() * kDpi96;
+ const gfx::Insets overscan_insets =
+ display_manager->GetOverscanInsets(display.id());
+
+ unit->id = base::Int64ToString(display.id());
+ unit->name = display_manager->GetDisplayNameForId(display.id());
+ unit->is_primary = (display.id() == primary_display_id);
+ unit->is_internal = display.IsInternal();
+ unit->is_enabled = true;
+ if (display_manager->IsMirrored()) {
+ unit->mirroring_source_id =
+ base::Int64ToString(display_manager->mirrored_display().id());
+ }
+ unit->dpi_x = dpi;
+ unit->dpi_y = dpi;
+ unit->rotation = RotationToDegrees(display.rotation());
+ unit->bounds.left = bounds.x();
+ unit->bounds.top = bounds.y();
+ unit->bounds.width = bounds.width();
+ unit->bounds.height = bounds.height();
+ unit->overscan.left = overscan_insets.left();
+ unit->overscan.top = overscan_insets.top();
+ unit->overscan.right = overscan_insets.right();
+ unit->overscan.bottom = overscan_insets.bottom();
+ unit->work_area.left = work_area.x();
+ unit->work_area.top = work_area.y();
+ unit->work_area.width = work_area.width();
+ unit->work_area.height = work_area.height();
+
+ list->push_back(unit);
+}
+
+// Checks if the given point is over the radius vector described by it's end
+// point |vector|. The point is over a vector if it's on its positive (left)
+// side. The method sees a point on the same line as the vector as being over
+// the vector.
+bool PointIsOverRadiusVector(const gfx::Point& point,
+ const gfx::Point& vector) {
+ // |point| is left of |vector| if its radius vector's scalar product with a
+ // vector orthogonal (and facing the positive side) to |vector| is positive.
+ //
+ // An orthogonal vector of (a, b) is (b, -a), as the scalar product of these
+ // two is 0.
+ // So, (x, y) is over (a, b) if x * b + y * (-a) >= 0, which is equivalent to
+ // x * b >= y * a.
+ return static_cast<int64>(point.x()) * static_cast<int64>(vector.y()) >=
+ static_cast<int64>(point.y()) * static_cast<int64>(vector.x());
+}
+
+// Created ash::DisplayLayout value for |rectangle| compared to the |reference|
+// rectangle.
+// The layout consists of two values:
+// - position: Whether the rectangle is positioned left, right, over or under
+// the reference.
+// - offset: The rectangle's offset from the reference origin along the axis
+// opposite the position direction (if the rectangle is left or right along
+// y-axis, otherwise along x-axis).
+// The rectangle's position is calculated by dividing the space in areas defined
+// by the |reference|'s diagonals and finding the area |rectangle|'s center
+// point belongs. If the |rectangle| in the calculated layout does not share a
+// part of the bounds with the |reference|, the |rectangle| position in set to
+// the more suitable neighboring position (e.g. if |rectangle| is completely
+// over the |reference| top bound, it will be set to TOP) and the layout is
+// recalculated with the new position. This is to handle case where the
+// rectangle shares an edge with the reference, but it's center is not in the
+// same area as the reference's edge, e.g.
+//
+// +---------------------+
+// | |
+// | REFERENCE |
+// | |
+// | |
+// +---------------------+
+// +-------------------------------------------------+
+// | RECTANGLE x |
+// +-------------------------------------------------+
+//
+// The rectangle shares an egde with the reference's bottom edge, but it's
+// center point is in the left area.
+ash::DisplayLayout GetLayoutForRectangles(const gfx::Rect& reference,
+ const gfx::Rect& rectangle) {
+ // Translate coordinate system so origin is in the reference's top left point
+ // (so the reference's down-diagonal vector starts in the (0, 0)) and scale it
+ // up by two (to avoid division when calculating the rectangle's center
+ // point).
+ gfx::Point center(2 * (rectangle.x() - reference.x()) + rectangle.width(),
+ 2 * (rectangle.y() - reference.y()) + rectangle.height());
+ gfx::Point down_diag(2 * reference.width(), 2 * reference.height());
+
+ bool is_top_right = PointIsOverRadiusVector(center, down_diag);
+
+ // Translate the coordinating system again, so the bottom right point of the
+ // reference is origin (so the references up-diagonal starts at (0, 0)).
+ // Note that the coordinate system is scaled by 2.
+ center.Offset(0, -2 * reference.height());
+ // Choose the vector orientation so the points on the diagonal are considered
+ // to be left.
+ gfx::Point up_diag(-2 * reference.width(), 2 * reference.height());
+
+ bool is_bottom_right = PointIsOverRadiusVector(center, up_diag);
+
+ ash::DisplayLayout::Position position;
+ if (is_top_right) {
+ position = is_bottom_right ? ash::DisplayLayout::RIGHT :
+ ash::DisplayLayout::TOP;
+ } else {
+ position =
+ is_bottom_right ? ash::DisplayLayout::BOTTOM : ash::DisplayLayout::LEFT;
+ }
+
+ // If the rectangle with the calculated position would not have common side
+ // with the reference, try to position it so it shares another edge with the
+ // reference.
+ if (is_top_right == is_bottom_right) {
+ if (rectangle.y() > reference.y() + reference.height()) {
+ // The rectangle is left or right, but completely under the reference.
+ position = ash::DisplayLayout::BOTTOM;
+ } else if (rectangle.y() + rectangle.height() < reference.y()) {
+ // The rectangle is left or right, but completely over the reference.
+ position = ash::DisplayLayout::TOP;
+ }
+ } else {
+ if (rectangle.x() > reference.x() + reference.width()) {
+ // The rectangle is over or under, but completely right of the reference.
+ position = ash::DisplayLayout::RIGHT;
+ } else if (rectangle.x() + rectangle.width() < reference.x()) {
+ // The rectangle is over or under, but completely left of the reference.
+ position = ash::DisplayLayout::LEFT;
+ }
+ }
+
+ if (position == ash::DisplayLayout::LEFT ||
+ position == ash::DisplayLayout::RIGHT) {
+ return ash::DisplayLayout::FromInts(position, rectangle.y());
+ } else {
+ return ash::DisplayLayout::FromInts(position, rectangle.x());
+ }
+}
+
+// Updates the display layout for the target display in reference to the primary
+// display.
+void UpdateDisplayLayout(const gfx::Rect& primary_display_bounds,
+ int primary_display_id,
+ const gfx::Rect& target_display_bounds,
+ int target_display_id) {
+ ash::DisplayLayout layout = GetLayoutForRectangles(primary_display_bounds,
+ target_display_bounds);
+ ash::DisplayController* display_controller =
+ ash::Shell::GetInstance()->display_controller();
+ display_controller->SetLayoutForCurrentDisplays(layout);
+}
+
+// Validates that parameters passed to the SetInfo function are valid for the
+// desired display and the current display manager state.
+// Returns whether the parameters are valid. On failure |error| is set to the
+// error message.
+bool ValidateParamsForDisplay(const DisplayProperties& info,
+ const gfx::Display& display,
+ DisplayManager* display_manager,
+ int64 primary_display_id,
+ std::string* error) {
+ bool is_primary = display.id() == primary_display_id ||
+ (info.is_primary && *info.is_primary);
+
+ // If mirroring source id is set, a display with the given id should exist,
+ // and if should not be the same as the target display's id.
+ if (info.mirroring_source_id && !info.mirroring_source_id->empty()) {
+ int64 mirroring_id;
+ if (!base::StringToInt64(*info.mirroring_source_id, &mirroring_id) ||
+ display_manager->GetDisplayForId(mirroring_id).id() ==
+ gfx::Display::kInvalidDisplayID) {
+ *error = "Display " + *info.mirroring_source_id + " not found.";
+ return false;
+ }
+
+ if (*info.mirroring_source_id == base::Int64ToString(display.id())) {
+ *error = "Not allowed to mirror self.";
+ return false;
+ }
+ }
+
+ // If mirroring source parameter is specified, no other parameter should be
+ // set as when the mirroring is applied the display list could change.
+ if (info.mirroring_source_id && (info.is_primary || info.bounds_origin_x ||
+ info.bounds_origin_y || info.rotation || info.overscan)) {
+ *error = "No other parameter should be set alongside mirroringSourceId.";
+ return false;
+ }
+
+ // The bounds cannot be changed for the primary display and should be inside
+ // a reasonable bounds. Note that the display is considered primary if the
+ // info has 'isPrimary' parameter set, as this will be applied before bounds
+ // origin changes.
+ if (info.bounds_origin_x || info.bounds_origin_y) {
+ if (is_primary) {
+ *error = "Bounds origin not allowed for the primary display.";
+ return false;
+ }
+ if (info.bounds_origin_x &&
+ (*info.bounds_origin_x > kMaxBoundsOrigin ||
+ *info.bounds_origin_x < -kMaxBoundsOrigin)) {
+ *error = "Bounds origin x out of bounds.";
+ return false;
+ }
+ if (info.bounds_origin_y &&
+ (*info.bounds_origin_y > kMaxBoundsOrigin ||
+ *info.bounds_origin_y < -kMaxBoundsOrigin)) {
+ *error = "Bounds origin y out of bounds.";
+ return false;
+ }
+ }
+
+ // Verify the rotation value is valid.
+ if (info.rotation && !IsValidRotationValue(*info.rotation)) {
+ *error = "Invalid rotation.";
+ return false;
+ }
+
+ // Overscan cannot be changed for the internal display, and should be at most
+ // half of the screen size.
+ if (info.overscan) {
+ if (display.IsInternal()) {
+ *error = "Overscan changes not allowed for the internal monitor.";
+ return false;
+ }
+
+ if (info.overscan->left < 0 || info.overscan->top < 0 ||
+ info.overscan->right < 0 || info.overscan->bottom < 0) {
+ *error = "Negative overscan not allowed.";
+ return false;
+ }
+
+ const gfx::Insets overscan =
+ display_manager->GetOverscanInsets(display.id());
+ int screen_width = display.bounds().width() + overscan.width();
+ int screen_height = display.bounds().height() + overscan.height();
+
+ if ((info.overscan->left + info.overscan->right) * 2 > screen_width) {
+ *error = "Horizontal overscan is more than half of the screen width.";
+ return false;
+ }
+
+ if ((info.overscan->top + info.overscan->bottom) * 2 > screen_height) {
+ *error = "Vertical overscan is more than half of the screen height.";
+ return false;
+ }
+ }
+ return true;
+}
+
+// Gets the display with the provided string id.
+gfx::Display GetTargetDisplay(const std::string& display_id_str,
+ DisplayManager* manager) {
+ int64 display_id;
+ if (!base::StringToInt64(display_id_str, &display_id)) {
+ // This should return invalid display.
+ return gfx::Display();
+ }
+ return manager->GetDisplayForId(display_id);
+}
+
+// Updates the display with |display_id_str| id according to |info|. Returns
+// whether the display was successfully updated. On failure, no display
+// parameters should be changed, and |error| should be set to the error string.
+bool SetInfoImpl(const std::string& display_id_str,
+ const DisplayProperties& info,
+ std::string* error) {
+ DisplayManager* display_manager =
+ ash::Shell::GetInstance()->display_manager();
+ DCHECK(display_manager);
+ ash::DisplayController* display_controller =
+ ash::Shell::GetInstance()->display_controller();
+ DCHECK(display_controller);
+
+ const gfx::Display target = GetTargetDisplay(display_id_str, display_manager);
+
+ if (target.id() == gfx::Display::kInvalidDisplayID) {
+ *error = "Display not found.";
+ return false;
+ }
+
+ int64 display_id = target.id();
+ const gfx::Display& primary = ash::Shell::GetScreen()->GetPrimaryDisplay();
+
+ if (!ValidateParamsForDisplay(info, target, display_manager, primary.id(),
+ error)) {
+ return false;
+ }
+
+ // Process 'isPrimary' parameter.
+ if (info.is_primary && *info.is_primary && target.id() != primary.id())
+ display_controller->SetPrimaryDisplayId(display_id);
+
+ // Process 'mirroringSourceId' parameter.
+ if (info.mirroring_source_id &&
+ info.mirroring_source_id->empty() == display_manager->IsMirrored()) {
+ display_controller->ToggleMirrorMode();
+ }
+
+ // Process 'overscan' parameter.
+ if (info.overscan) {
+ display_manager->SetOverscanInsets(
+ display_id,
+ gfx::Insets(info.overscan->top, info.overscan->left,
+ info.overscan->bottom, info.overscan->right));
+ }
+
+ // Process 'rotation' parameter.
+ if (info.rotation) {
+ display_manager->SetDisplayRotation(display_id,
+ DegreesToRotation(*info.rotation));
+ }
+
+ // Process new display origin parameters.
+ gfx::Point new_bounds_origin = target.bounds().origin();
+ if (info.bounds_origin_x)
+ new_bounds_origin.set_x(*info.bounds_origin_x);
+ if (info.bounds_origin_y)
+ new_bounds_origin.set_y(*info.bounds_origin_y);
+
+ if (new_bounds_origin != target.bounds().origin()) {
+ gfx::Rect target_bounds = target.bounds();
+ target_bounds.Offset(new_bounds_origin.x() - target.bounds().x(),
+ new_bounds_origin.y() - target.bounds().y());
+ UpdateDisplayLayout(primary.bounds(), primary.id(),
+ target_bounds, target.id());
+ }
+
+ return true;
+}
+
+} // namespace
void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
DisplayInfo requested_info;
@@ -50,8 +419,17 @@ void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
base::Bind(callback, requested_info, success));
}
+void DisplayInfoProvider::SetInfo(const std::string& display_id,
+ const DisplayProperties& info,
+ const SetInfoCallback& callback) {
+ std::string error;
+ bool success = SetInfoImpl(display_id, info, &error);
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, success, error));
+}
+
bool DisplayInfoProvider::QueryInfo(DisplayInfo* info) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(info);
info->clear();
@@ -61,40 +439,8 @@ bool DisplayInfoProvider::QueryInfo(DisplayInfo* info) {
int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
- linked_ptr<DisplayUnitInfo> unit(new DisplayUnitInfo());
- const gfx::Display* display = display_manager->GetDisplayAt(i);
- const gfx::Rect& bounds = display->bounds();
- const gfx::Rect& work_area = display->work_area();
- const float dpi = display->device_scale_factor() * kDpi96;
- const gfx::Insets overscan_insets =
- display_manager->GetOverscanInsets(display->id());
-
- unit->id = base::Int64ToString(display->id());
- unit->name = display_manager->GetDisplayNameForId(display->id());
- unit->is_primary = (display->id() == primary_id);
- if (display_manager->IsMirrored() &&
- display_manager->mirrored_display().id() != display->id()) {
- unit->mirroring_source_id =
- base::Int64ToString(display_manager->mirrored_display().id());
- }
- unit->is_internal = display->IsInternal();
- unit->is_enabled = true;
- unit->dpi_x = dpi;
- unit->dpi_y = dpi;
- unit->rotation = RotationToDegrees(display->rotation());
- unit->bounds.left = bounds.x();
- unit->bounds.top = bounds.y();
- unit->bounds.width = bounds.width();
- unit->bounds.height = bounds.height();
- unit->overscan.left = overscan_insets.left();
- unit->overscan.top = overscan_insets.top();
- unit->overscan.right = overscan_insets.right();
- unit->overscan.bottom = overscan_insets.bottom();
- unit->work_area.left = work_area.x();
- unit->work_area.top = work_area.y();
- unit->work_area.width = work_area.width();
- unit->work_area.height = work_area.height();
- info->push_back(unit);
+ AddInfoForDisplay(*display_manager->GetDisplayAt(i), display_manager,
+ primary_id, info);
}
return true;

Powered by Google App Engine
This is Rietveld 408576698