1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/display_info_provider_chromeos.h"
7 #include "ash/display/display_manager.h"
8 #include "ash/display/window_tree_host_manager.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "extensions/common/api/system_display.h"
12 #include "ui/gfx/display.h"
13 #include "ui/gfx/geometry/point.h"
14 #include "ui/gfx/geometry/rect.h"
16 using ash::DisplayManager
;
18 namespace extensions
{
20 using api::system_display::Bounds
;
21 using api::system_display::DisplayUnitInfo
;
22 using api::system_display::DisplayProperties
;
23 using api::system_display::Insets
;
27 // Maximum allowed bounds origin absolute value.
28 const int kMaxBoundsOrigin
= 200 * 1000;
30 // Checks if the given integer value is valid display rotation in degrees.
31 bool IsValidRotationValue(int rotation
) {
32 return rotation
== 0 || rotation
== 90 || rotation
== 180 || rotation
== 270;
35 // Converts integer integer value in degrees to Rotation enum value.
36 gfx::Display::Rotation
DegreesToRotation(int degrees
) {
37 DCHECK(IsValidRotationValue(degrees
));
40 return gfx::Display::ROTATE_0
;
42 return gfx::Display::ROTATE_90
;
44 return gfx::Display::ROTATE_180
;
46 return gfx::Display::ROTATE_270
;
48 return gfx::Display::ROTATE_0
;
52 // Checks if the given point is over the radius vector described by it's end
53 // point |vector|. The point is over a vector if it's on its positive (left)
54 // side. The method sees a point on the same line as the vector as being over
56 bool PointIsOverRadiusVector(const gfx::Point
& point
,
57 const gfx::Point
& vector
) {
58 // |point| is left of |vector| if its radius vector's scalar product with a
59 // vector orthogonal (and facing the positive side) to |vector| is positive.
61 // An orthogonal vector of (a, b) is (b, -a), as the scalar product of these
63 // So, (x, y) is over (a, b) if x * b + y * (-a) >= 0, which is equivalent to
65 return static_cast<int64
>(point
.x()) * static_cast<int64
>(vector
.y()) >=
66 static_cast<int64
>(point
.y()) * static_cast<int64
>(vector
.x());
69 // Created ash::DisplayLayout value for |rectangle| compared to the |reference|
71 // The layout consists of two values:
72 // - position: Whether the rectangle is positioned left, right, over or under
74 // - offset: The rectangle's offset from the reference origin along the axis
75 // opposite the position direction (if the rectangle is left or right along
76 // y-axis, otherwise along x-axis).
77 // The rectangle's position is calculated by dividing the space in areas defined
78 // by the |reference|'s diagonals and finding the area |rectangle|'s center
79 // point belongs. If the |rectangle| in the calculated layout does not share a
80 // part of the bounds with the |reference|, the |rectangle| position in set to
81 // the more suitable neighboring position (e.g. if |rectangle| is completely
82 // over the |reference| top bound, it will be set to TOP) and the layout is
83 // recalculated with the new position. This is to handle case where the
84 // rectangle shares an edge with the reference, but it's center is not in the
85 // same area as the reference's edge, e.g.
87 // +---------------------+
92 // +---------------------+
93 // +-------------------------------------------------+
95 // +-------------------------------------------------+
97 // The rectangle shares an egde with the reference's bottom edge, but it's
98 // center point is in the left area.
99 ash::DisplayLayout
GetLayoutForRectangles(const gfx::Rect
& reference
,
100 const gfx::Rect
& rectangle
) {
101 // Translate coordinate system so origin is in the reference's top left point
102 // (so the reference's down-diagonal vector starts in the (0, 0)) and scale it
103 // up by two (to avoid division when calculating the rectangle's center
105 gfx::Point
center(2 * (rectangle
.x() - reference
.x()) + rectangle
.width(),
106 2 * (rectangle
.y() - reference
.y()) + rectangle
.height());
107 gfx::Point
down_diag(2 * reference
.width(), 2 * reference
.height());
109 bool is_top_right
= PointIsOverRadiusVector(center
, down_diag
);
111 // Translate the coordinating system again, so the bottom right point of the
112 // reference is origin (so the references up-diagonal starts at (0, 0)).
113 // Note that the coordinate system is scaled by 2.
114 center
.Offset(0, -2 * reference
.height());
115 // Choose the vector orientation so the points on the diagonal are considered
117 gfx::Point
up_diag(-2 * reference
.width(), 2 * reference
.height());
119 bool is_bottom_right
= PointIsOverRadiusVector(center
, up_diag
);
121 ash::DisplayLayout::Position position
;
124 is_bottom_right
? ash::DisplayLayout::RIGHT
: ash::DisplayLayout::TOP
;
127 is_bottom_right
? ash::DisplayLayout::BOTTOM
: ash::DisplayLayout::LEFT
;
130 // If the rectangle with the calculated position would not have common side
131 // with the reference, try to position it so it shares another edge with the
133 if (is_top_right
== is_bottom_right
) {
134 if (rectangle
.y() > reference
.y() + reference
.height()) {
135 // The rectangle is left or right, but completely under the reference.
136 position
= ash::DisplayLayout::BOTTOM
;
137 } else if (rectangle
.y() + rectangle
.height() < reference
.y()) {
138 // The rectangle is left or right, but completely over the reference.
139 position
= ash::DisplayLayout::TOP
;
142 if (rectangle
.x() > reference
.x() + reference
.width()) {
143 // The rectangle is over or under, but completely right of the reference.
144 position
= ash::DisplayLayout::RIGHT
;
145 } else if (rectangle
.x() + rectangle
.width() < reference
.x()) {
146 // The rectangle is over or under, but completely left of the reference.
147 position
= ash::DisplayLayout::LEFT
;
151 if (position
== ash::DisplayLayout::LEFT
||
152 position
== ash::DisplayLayout::RIGHT
) {
153 return ash::DisplayLayout::FromInts(position
, rectangle
.y());
155 return ash::DisplayLayout::FromInts(position
, rectangle
.x());
159 // Updates the display layout for the target display in reference to the primary
161 void UpdateDisplayLayout(const gfx::Rect
& primary_display_bounds
,
162 int primary_display_id
,
163 const gfx::Rect
& target_display_bounds
,
164 int target_display_id
) {
165 ash::DisplayLayout layout
=
166 GetLayoutForRectangles(primary_display_bounds
, target_display_bounds
);
167 ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays(
171 // Validates that parameters passed to the SetInfo function are valid for the
172 // desired display and the current display manager state.
173 // Returns whether the parameters are valid. On failure |error| is set to the
175 bool ValidateParamsForDisplay(const DisplayProperties
& info
,
176 const gfx::Display
& display
,
177 DisplayManager
* display_manager
,
178 int64 primary_display_id
,
179 std::string
* error
) {
180 bool is_primary
= display
.id() == primary_display_id
||
181 (info
.is_primary
&& *info
.is_primary
);
183 // If mirroring source id is set, a display with the given id should exist,
184 // and if should not be the same as the target display's id.
185 if (info
.mirroring_source_id
&& !info
.mirroring_source_id
->empty()) {
187 if (!base::StringToInt64(*info
.mirroring_source_id
, &mirroring_id
) ||
188 display_manager
->GetDisplayForId(mirroring_id
).id() ==
189 gfx::Display::kInvalidDisplayID
) {
190 *error
= "Display " + *info
.mirroring_source_id
+ " not found.";
194 if (*info
.mirroring_source_id
== base::Int64ToString(display
.id())) {
195 *error
= "Not allowed to mirror self.";
200 // If mirroring source parameter is specified, no other parameter should be
201 // set as when the mirroring is applied the display list could change.
202 if (info
.mirroring_source_id
&&
203 (info
.is_primary
|| info
.bounds_origin_x
|| info
.bounds_origin_y
||
204 info
.rotation
|| info
.overscan
)) {
205 *error
= "No other parameter should be set alongside mirroringSourceId.";
209 // The bounds cannot be changed for the primary display and should be inside
210 // a reasonable bounds. Note that the display is considered primary if the
211 // info has 'isPrimary' parameter set, as this will be applied before bounds
213 if (info
.bounds_origin_x
|| info
.bounds_origin_y
) {
215 *error
= "Bounds origin not allowed for the primary display.";
218 if (info
.bounds_origin_x
&& (*info
.bounds_origin_x
> kMaxBoundsOrigin
||
219 *info
.bounds_origin_x
< -kMaxBoundsOrigin
)) {
220 *error
= "Bounds origin x out of bounds.";
223 if (info
.bounds_origin_y
&& (*info
.bounds_origin_y
> kMaxBoundsOrigin
||
224 *info
.bounds_origin_y
< -kMaxBoundsOrigin
)) {
225 *error
= "Bounds origin y out of bounds.";
230 // Verify the rotation value is valid.
231 if (info
.rotation
&& !IsValidRotationValue(*info
.rotation
)) {
232 *error
= "Invalid rotation.";
236 // Overscan cannot be changed for the internal display, and should be at most
237 // half of the screen size.
239 if (display
.IsInternal()) {
240 *error
= "Overscan changes not allowed for the internal monitor.";
244 if (info
.overscan
->left
< 0 || info
.overscan
->top
< 0 ||
245 info
.overscan
->right
< 0 || info
.overscan
->bottom
< 0) {
246 *error
= "Negative overscan not allowed.";
250 const gfx::Insets overscan
=
251 display_manager
->GetOverscanInsets(display
.id());
252 int screen_width
= display
.bounds().width() + overscan
.width();
253 int screen_height
= display
.bounds().height() + overscan
.height();
255 if ((info
.overscan
->left
+ info
.overscan
->right
) * 2 > screen_width
) {
256 *error
= "Horizontal overscan is more than half of the screen width.";
260 if ((info
.overscan
->top
+ info
.overscan
->bottom
) * 2 > screen_height
) {
261 *error
= "Vertical overscan is more than half of the screen height.";
268 // Gets the display with the provided string id.
269 gfx::Display
GetTargetDisplay(const std::string
& display_id_str
,
270 DisplayManager
* manager
) {
272 if (!base::StringToInt64(display_id_str
, &display_id
)) {
273 // This should return invalid display.
274 return gfx::Display();
276 return manager
->GetDisplayForId(display_id
);
281 DisplayInfoProviderChromeOS::DisplayInfoProviderChromeOS() {
284 DisplayInfoProviderChromeOS::~DisplayInfoProviderChromeOS() {
287 bool DisplayInfoProviderChromeOS::SetInfo(const std::string
& display_id_str
,
288 const DisplayProperties
& info
,
289 std::string
* error
) {
290 DisplayManager
* display_manager
=
291 ash::Shell::GetInstance()->display_manager();
292 DCHECK(display_manager
);
293 ash::WindowTreeHostManager
* window_tree_host_manager
=
294 ash::Shell::GetInstance()->window_tree_host_manager();
295 DCHECK(window_tree_host_manager
);
297 const gfx::Display target
= GetTargetDisplay(display_id_str
, display_manager
);
299 if (target
.id() == gfx::Display::kInvalidDisplayID
) {
300 *error
= "Display not found.";
304 int64 display_id
= target
.id();
305 // TODO(scottmg): Native is wrong http://crbug.com/133312
306 const gfx::Display
& primary
=
307 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
309 if (!ValidateParamsForDisplay(
310 info
, target
, display_manager
, primary
.id(), error
)) {
314 // Process 'isPrimary' parameter.
315 if (info
.is_primary
&& *info
.is_primary
&& target
.id() != primary
.id())
316 window_tree_host_manager
->SetPrimaryDisplayId(display_id
);
318 // Process 'mirroringSourceId' parameter.
319 if (info
.mirroring_source_id
&&
320 info
.mirroring_source_id
->empty() == display_manager
->IsInMirrorMode()) {
321 window_tree_host_manager
->ToggleMirrorMode();
324 // Process 'overscan' parameter.
326 display_manager
->SetOverscanInsets(display_id
,
327 gfx::Insets(info
.overscan
->top
,
329 info
.overscan
->bottom
,
330 info
.overscan
->right
));
333 // Process 'rotation' parameter.
335 display_manager
->SetDisplayRotation(display_id
,
336 DegreesToRotation(*info
.rotation
),
337 gfx::Display::ROTATION_SOURCE_ACTIVE
);
340 // Process new display origin parameters.
341 gfx::Point new_bounds_origin
= target
.bounds().origin();
342 if (info
.bounds_origin_x
)
343 new_bounds_origin
.set_x(*info
.bounds_origin_x
);
344 if (info
.bounds_origin_y
)
345 new_bounds_origin
.set_y(*info
.bounds_origin_y
);
347 if (new_bounds_origin
!= target
.bounds().origin()) {
348 gfx::Rect target_bounds
= target
.bounds();
349 target_bounds
.Offset(new_bounds_origin
.x() - target
.bounds().x(),
350 new_bounds_origin
.y() - target
.bounds().y());
352 primary
.bounds(), primary
.id(), target_bounds
, target
.id());
358 void DisplayInfoProviderChromeOS::UpdateDisplayUnitInfoForPlatform(
359 const gfx::Display
& display
,
360 extensions::api::system_display::DisplayUnitInfo
* unit
) {
361 ash::DisplayManager
* display_manager
=
362 ash::Shell::GetInstance()->display_manager();
363 unit
->name
= display_manager
->GetDisplayNameForId(display
.id());
364 if (display_manager
->IsInMirrorMode()) {
365 unit
->mirroring_source_id
=
366 base::Int64ToString(display_manager
->mirroring_display_id());
369 // TODO(hshi): determine the DPI of the screen.
370 const float kDpi96
= 96.0;
372 const float dpi
= display
.device_scale_factor() * kDpi96
;
376 const gfx::Insets overscan_insets
=
377 display_manager
->GetOverscanInsets(display
.id());
378 unit
->overscan
.left
= overscan_insets
.left();
379 unit
->overscan
.top
= overscan_insets
.top();
380 unit
->overscan
.right
= overscan_insets
.right();
381 unit
->overscan
.bottom
= overscan_insets
.bottom();
384 gfx::Screen
* DisplayInfoProviderChromeOS::GetActiveScreen() {
385 return ash::Shell::GetScreen();
389 DisplayInfoProvider
* DisplayInfoProvider::Create() {
390 return new DisplayInfoProviderChromeOS();
393 } // namespace extensions