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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/system_info_display/display_info_provide r.h" 5 #include "chrome/browser/extensions/api/system_info_display/display_info_provide r.h"
6 6
7 #include "ash/display/display_controller.h"
7 #include "ash/display/display_manager.h" 8 #include "ash/display/display_manager.h"
8 #include "ash/shell.h" 9 #include "ash/shell.h"
9 #include "base/message_loop_proxy.h" 10 #include "base/message_loop_proxy.h"
10 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_number_conversions.h"
11 #include "content/public/browser/browser_thread.h" 12 #include "content/public/browser/browser_thread.h"
12 #include "ui/gfx/display.h" 13 #include "ui/gfx/display.h"
14 #include "ui/gfx/point.h"
13 #include "ui/gfx/rect.h" 15 #include "ui/gfx/rect.h"
14 16
15 using ash::internal::DisplayManager; 17 using ash::internal::DisplayManager;
16 18
17 namespace extensions { 19 namespace extensions {
18 20
21 using api::system_info_display::Bounds;
22 using api::system_info_display::DisplayUnitInfo;
23 using api::system_info_display::DisplayProperties;
24 using api::system_info_display::Insets;
25
19 namespace { 26 namespace {
20 27
21 // TODO(hshi): determine the DPI of the screen. 28 // TODO(hshi): determine the DPI of the screen.
22 const float kDpi96 = 96.0; 29 const float kDpi96 = 96.0;
30 // Maximum allowed bounds origin absolute value.
31 const int kMaxBoundsOrigin = 200 * 1000;
32
33 // Checks if the given integer value is valid display rotation in degrees.
34 bool IsValidRotationValue(int rotation) {
35 return rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270;
36 }
23 37
24 // Converts Rotation enum to integer. 38 // Converts Rotation enum to integer.
25 int RotationToDegrees(gfx::Display::Rotation rotation) { 39 int RotationToDegrees(gfx::Display::Rotation rotation) {
26 switch (rotation) { 40 switch (rotation) {
27 case gfx::Display::ROTATE_0: 41 case gfx::Display::ROTATE_0:
28 return 0; 42 return 0;
29 case gfx::Display::ROTATE_90: 43 case gfx::Display::ROTATE_90:
30 return 90; 44 return 90;
31 case gfx::Display::ROTATE_180: 45 case gfx::Display::ROTATE_180:
32 return 180; 46 return 180;
33 case gfx::Display::ROTATE_270: 47 case gfx::Display::ROTATE_270:
34 return 270; 48 return 270;
35 } 49 }
36 return 0; 50 return 0;
37 } 51 }
38 52
53 // Converts integer integer value in degrees to Rotation enum value.
54 gfx::Display::Rotation DegreesToRotation(int degrees) {
55 DCHECK(IsValidRotationValue(degrees));
56 switch (degrees) {
57 case 0:
58 return gfx::Display::ROTATE_0;
59 case 90:
60 return gfx::Display::ROTATE_90;
61 case 180:
62 return gfx::Display::ROTATE_180;
63 case 270:
64 return gfx::Display::ROTATE_270;
65 default:
66 return gfx::Display::ROTATE_0;
67 }
68 }
69
70 // Creates new DisplayUnitInfo struct for |display| and adds it at the end of
71 // |list|.
72 void AddInfoForDisplay(const gfx::Display& display,
73 DisplayManager* display_manager,
74 int64 primary_display_id,
75 DisplayInfo* list) {
76 linked_ptr<extensions::api::system_info_display::DisplayUnitInfo> unit(
77 new extensions::api::system_info_display::DisplayUnitInfo());
78 const gfx::Rect& bounds = display.bounds();
79 const gfx::Rect& work_area = display.work_area();
80 const float dpi = display.device_scale_factor() * kDpi96;
81 const gfx::Insets overscan_insets =
82 display_manager->GetOverscanInsets(display.id());
83
84 unit->id = base::Int64ToString(display.id());
85 unit->name = display_manager->GetDisplayNameForId(display.id());
86 unit->is_primary = (display.id() == primary_display_id);
87 unit->is_internal = display.IsInternal();
88 unit->is_enabled = true;
89 if (display_manager->IsMirrored()) {
90 unit->mirroring_source_id =
91 base::Int64ToString(display_manager->mirrored_display().id());
92 }
93 unit->dpi_x = dpi;
94 unit->dpi_y = dpi;
95 unit->rotation = RotationToDegrees(display.rotation());
96 unit->bounds.left = bounds.x();
97 unit->bounds.top = bounds.y();
98 unit->bounds.width = bounds.width();
99 unit->bounds.height = bounds.height();
100 unit->overscan.left = overscan_insets.left();
101 unit->overscan.top = overscan_insets.top();
102 unit->overscan.right = overscan_insets.right();
103 unit->overscan.bottom = overscan_insets.bottom();
104 unit->work_area.left = work_area.x();
105 unit->work_area.top = work_area.y();
106 unit->work_area.width = work_area.width();
107 unit->work_area.height = work_area.height();
108
109 list->push_back(unit);
110 }
111
112 // Checks if the given point is over the radius vector described by it's end
113 // point |vector|. The point is over a vector if it's on its positive (left)
114 // side. The method sees a point on the same line as the vector as being over
115 // the vector.
116 bool PointIsOverRadiusVector(const gfx::Point& point,
117 const gfx::Point& vector) {
118 // |point| is left of |vector| if its radius vector's scalar product with a
119 // vector orthogonal (and facing the positive side) to |vector| is positive.
120 //
121 // An orthogonal vector of (a, b) is (b, -a), as the scalar product of these
122 // two is 0.
123 // So, (x, y) is over (a, b) if x * b + y * (-a) >= 0, which is equivalent to
124 // x * b >= y * a.
125 return static_cast<int64>(point.x()) * static_cast<int64>(vector.y()) >=
126 static_cast<int64>(point.y()) * static_cast<int64>(vector.x());
127 }
128
129 // Created ash::DisplayLayout value for |rectangle| compared to the |reference|
130 // rectangle.
131 // The layout consists of two values:
132 // - position: Whether the rectangle is positioned left, right, over or under
133 // the reference.
134 // - offset: The rectangle's offset from the reference origin along the axis
135 // opposite the position direction (if the rectangle is left or right along
136 // y-axis, otherwise along x-axis).
137 // The rectangle's position is calculated by dividing the space in areas defined
138 // by the |reference|'s diagonals and finding the area |rectangle|'s center
139 // point belongs. If the |rectangle| in the calculated layout does not share a
140 // part of the bounds with the |reference|, the |rectangle| position in set to
141 // the more suitable neighboring position (e.g. if |rectangle| is completely
142 // over the |reference| top bound, it will be set to TOP) and the layout is
143 // recalculated with the new position. This is to handle case where the
144 // rectangle shares an edge with the reference, but it's center is not in the
145 // same area as the reference's edge, e.g.
146 //
147 // +---------------------+
148 // | |
149 // | REFERENCE |
150 // | |
151 // | |
152 // +---------------------+
153 // +-------------------------------------------------+
154 // | RECTANGLE x |
155 // +-------------------------------------------------+
156 //
157 // The rectangle shares an egde with the reference's bottom edge, but it's
158 // center point is in the left area.
159 ash::DisplayLayout GetLayoutForRectangles(const gfx::Rect& reference,
160 const gfx::Rect& rectangle) {
161 // Translate coordinate system so origin is in the reference's top left point
162 // (so the reference's down-diagonal vector starts in the (0, 0)) and scale it
163 // up by two (to avoid division when calculating the rectangle's center
164 // point).
165 gfx::Point center(2 * (rectangle.x() - reference.x()) + rectangle.width(),
166 2 * (rectangle.y() - reference.y()) + rectangle.height());
167 gfx::Point down_diag(2 * reference.width(), 2 * reference.height());
168
169 bool is_top_right = PointIsOverRadiusVector(center, down_diag);
170
171 // Translate the coordinating system again, so the bottom right point of the
172 // reference is origin (so the references up-diagonal starts at (0, 0)).
173 // Note that the coordinate system is scaled by 2.
174 center.Offset(0, -2 * reference.height());
175 // Choose the vector orientation so the points on the diagonal are considered
176 // to be left.
177 gfx::Point up_diag(-2 * reference.width(), 2 * reference.height());
178
179 bool is_bottom_right = PointIsOverRadiusVector(center, up_diag);
180
181 ash::DisplayLayout::Position position;
182 if (is_top_right) {
183 position = is_bottom_right ? ash::DisplayLayout::RIGHT :
184 ash::DisplayLayout::TOP;
185 } else {
186 position =
187 is_bottom_right ? ash::DisplayLayout::BOTTOM : ash::DisplayLayout::LEFT;
188 }
189
190 // If the rectangle with the calculated position would not have common side
191 // with the reference, try to position it so it shares another edge with the
192 // reference.
193 if (is_top_right == is_bottom_right) {
194 if (rectangle.y() > reference.y() + reference.height()) {
195 // The rectangle is left or right, but completely under the reference.
196 position = ash::DisplayLayout::BOTTOM;
197 } else if (rectangle.y() + rectangle.height() < reference.y()) {
198 // The rectangle is left or right, but completely over the reference.
199 position = ash::DisplayLayout::TOP;
200 }
201 } else {
202 if (rectangle.x() > reference.x() + reference.width()) {
203 // The rectangle is over or under, but completely right of the reference.
204 position = ash::DisplayLayout::RIGHT;
205 } else if (rectangle.x() + rectangle.width() < reference.x()) {
206 // The rectangle is over or under, but completely left of the reference.
207 position = ash::DisplayLayout::LEFT;
208 }
209 }
210
211 if (position == ash::DisplayLayout::LEFT ||
212 position == ash::DisplayLayout::RIGHT) {
213 return ash::DisplayLayout::FromInts(position, rectangle.y());
214 } else {
215 return ash::DisplayLayout::FromInts(position, rectangle.x());
216 }
217 }
218
219 // Updates the display layout for the target display in reference to the primary
220 // display.
221 void UpdateDisplayLayout(const gfx::Rect& primary_display_bounds,
222 int primary_display_id,
223 const gfx::Rect& target_display_bounds,
224 int target_display_id) {
225 ash::DisplayLayout layout = GetLayoutForRectangles(primary_display_bounds,
226 target_display_bounds);
227 ash::DisplayController* display_controller =
228 ash::Shell::GetInstance()->display_controller();
229 display_controller->SetLayoutForCurrentDisplays(layout);
230 }
231
232 // Validates that parameters passed to the SetInfo function are valid for the
233 // desired display and the current display manager state.
234 // Returns whether the parameters are valid. On failure |error| is set to the
235 // error message.
236 bool ValidateParamsForDisplay(const DisplayProperties& info,
237 const gfx::Display& display,
238 DisplayManager* display_manager,
239 int64 primary_display_id,
240 std::string* error) {
241 bool is_primary = display.id() == primary_display_id ||
242 (info.is_primary && *info.is_primary);
243
244 // If mirroring source id is set, a display with the given id should exist,
245 // and if should not be the same as the target display's id.
246 if (info.mirroring_source_id && !info.mirroring_source_id->empty()) {
247 int64 mirroring_id;
248 if (!base::StringToInt64(*info.mirroring_source_id, &mirroring_id) ||
249 display_manager->GetDisplayForId(mirroring_id).id() ==
250 gfx::Display::kInvalidDisplayID) {
251 *error = "Display " + *info.mirroring_source_id + " not found.";
252 return false;
253 }
254
255 if (*info.mirroring_source_id == base::Int64ToString(display.id())) {
256 *error = "Not allowed to mirror self.";
257 return false;
258 }
259 }
260
261 // If mirroring source parameter is specified, no other parameter should be
262 // set as when the mirroring is applied the display list could change.
263 if (info.mirroring_source_id && (info.is_primary || info.bounds_origin_x ||
264 info.bounds_origin_y || info.rotation || info.overscan)) {
265 *error = "No other parameter should be set alongside mirroringSourceId.";
266 return false;
267 }
268
269 // The bounds cannot be changed for the primary display and should be inside
270 // a reasonable bounds. Note that the display is considered primary if the
271 // info has 'isPrimary' parameter set, as this will be applied before bounds
272 // origin changes.
273 if (info.bounds_origin_x || info.bounds_origin_y) {
274 if (is_primary) {
275 *error = "Bounds origin not allowed for the primary display.";
276 return false;
277 }
278 if (info.bounds_origin_x &&
279 (*info.bounds_origin_x > kMaxBoundsOrigin ||
280 *info.bounds_origin_x < -kMaxBoundsOrigin)) {
281 *error = "Bounds origin x out of bounds.";
282 return false;
283 }
284 if (info.bounds_origin_y &&
285 (*info.bounds_origin_y > kMaxBoundsOrigin ||
286 *info.bounds_origin_y < -kMaxBoundsOrigin)) {
287 *error = "Bounds origin y out of bounds.";
288 return false;
289 }
290 }
291
292 // Verify the rotation value is valid.
293 if (info.rotation && !IsValidRotationValue(*info.rotation)) {
294 *error = "Invalid rotation.";
295 return false;
296 }
297
298 // Overscan cannot be changed for the internal display, and should be at most
299 // half of the screen size.
300 if (info.overscan) {
301 if (display.IsInternal()) {
302 *error = "Overscan changes not allowed for the internal monitor.";
303 return false;
304 }
305
306 if (info.overscan->left < 0 || info.overscan->top < 0 ||
307 info.overscan->right < 0 || info.overscan->bottom < 0) {
308 *error = "Negative overscan not allowed.";
309 return false;
310 }
311
312 const gfx::Insets overscan =
313 display_manager->GetOverscanInsets(display.id());
314 int screen_width = display.bounds().width() + overscan.width();
315 int screen_height = display.bounds().height() + overscan.height();
316
317 if ((info.overscan->left + info.overscan->right) * 2 > screen_width) {
318 *error = "Horizontal overscan is more than half of the screen width.";
319 return false;
320 }
321
322 if ((info.overscan->top + info.overscan->bottom) * 2 > screen_height) {
323 *error = "Vertical overscan is more than half of the screen height.";
324 return false;
325 }
326 }
327 return true;
328 }
329
330 // Gets the display with the provided string id.
331 gfx::Display GetTargetDisplay(const std::string& display_id_str,
332 DisplayManager* manager) {
333 int64 display_id;
334 if (!base::StringToInt64(display_id_str, &display_id)) {
335 // This should return invalid display.
336 return gfx::Display();
337 }
338 return manager->GetDisplayForId(display_id);
339 }
340
341 // Updates the display with |display_id_str| id according to |info|. Returns
342 // whether the display was successfully updated. On failure, no display
343 // parameters should be changed, and |error| should be set to the error string.
344 bool SetInfoImpl(const std::string& display_id_str,
345 const DisplayProperties& info,
346 std::string* error) {
347 DisplayManager* display_manager =
348 ash::Shell::GetInstance()->display_manager();
349 DCHECK(display_manager);
350 ash::DisplayController* display_controller =
351 ash::Shell::GetInstance()->display_controller();
352 DCHECK(display_controller);
353
354 const gfx::Display target = GetTargetDisplay(display_id_str, display_manager);
355
356 if (target.id() == gfx::Display::kInvalidDisplayID) {
357 *error = "Display not found.";
358 return false;
359 }
360
361 int64 display_id = target.id();
362 const gfx::Display& primary = ash::Shell::GetScreen()->GetPrimaryDisplay();
363
364 if (!ValidateParamsForDisplay(info, target, display_manager, primary.id(),
365 error)) {
366 return false;
367 }
368
369 // Process 'isPrimary' parameter.
370 if (info.is_primary && *info.is_primary && target.id() != primary.id())
371 display_controller->SetPrimaryDisplayId(display_id);
372
373 // Process 'mirroringSourceId' parameter.
374 if (info.mirroring_source_id &&
375 info.mirroring_source_id->empty() == display_manager->IsMirrored()) {
376 display_controller->ToggleMirrorMode();
377 }
378
379 // Process 'overscan' parameter.
380 if (info.overscan) {
381 display_manager->SetOverscanInsets(
382 display_id,
383 gfx::Insets(info.overscan->top, info.overscan->left,
384 info.overscan->bottom, info.overscan->right));
385 }
386
387 // Process 'rotation' parameter.
388 if (info.rotation) {
389 display_manager->SetDisplayRotation(display_id,
390 DegreesToRotation(*info.rotation));
391 }
392
393 // Process new display origin parameters.
394 gfx::Point new_bounds_origin = target.bounds().origin();
395 if (info.bounds_origin_x)
396 new_bounds_origin.set_x(*info.bounds_origin_x);
397 if (info.bounds_origin_y)
398 new_bounds_origin.set_y(*info.bounds_origin_y);
399
400 if (new_bounds_origin != target.bounds().origin()) {
401 gfx::Rect target_bounds = target.bounds();
402 target_bounds.Offset(new_bounds_origin.x() - target.bounds().x(),
403 new_bounds_origin.y() - target.bounds().y());
404 UpdateDisplayLayout(primary.bounds(), primary.id(),
405 target_bounds, target.id());
406 }
407
408 return true;
409 }
410
39 } // namespace 411 } // namespace
40 412
41 using api::system_info_display::Bounds;
42 using api::system_info_display::DisplayUnitInfo;
43
44 void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) { 413 void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
45 DisplayInfo requested_info; 414 DisplayInfo requested_info;
46 bool success = QueryInfo(&requested_info); 415 bool success = QueryInfo(&requested_info);
47 416
48 base::MessageLoopProxy::current()->PostTask( 417 base::MessageLoopProxy::current()->PostTask(
49 FROM_HERE, 418 FROM_HERE,
50 base::Bind(callback, requested_info, success)); 419 base::Bind(callback, requested_info, success));
51 } 420 }
52 421
422 void DisplayInfoProvider::SetInfo(const std::string& display_id,
423 const DisplayProperties& info,
424 const SetInfoCallback& callback) {
425 std::string error;
426 bool success = SetInfoImpl(display_id, info, &error);
427 base::MessageLoopProxy::current()->PostTask(
428 FROM_HERE,
429 base::Bind(callback, success, error));
430 }
431
53 bool DisplayInfoProvider::QueryInfo(DisplayInfo* info) { 432 bool DisplayInfoProvider::QueryInfo(DisplayInfo* info) {
54 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
55 DCHECK(info); 433 DCHECK(info);
56 info->clear(); 434 info->clear();
57 435
58 DisplayManager* display_manager = 436 DisplayManager* display_manager =
59 ash::Shell::GetInstance()->display_manager(); 437 ash::Shell::GetInstance()->display_manager();
60 DCHECK(display_manager); 438 DCHECK(display_manager);
61 439
62 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id(); 440 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
63 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { 441 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
64 linked_ptr<DisplayUnitInfo> unit(new DisplayUnitInfo()); 442 AddInfoForDisplay(*display_manager->GetDisplayAt(i), display_manager,
65 const gfx::Display* display = display_manager->GetDisplayAt(i); 443 primary_id, info);
66 const gfx::Rect& bounds = display->bounds();
67 const gfx::Rect& work_area = display->work_area();
68 const float dpi = display->device_scale_factor() * kDpi96;
69 const gfx::Insets overscan_insets =
70 display_manager->GetOverscanInsets(display->id());
71
72 unit->id = base::Int64ToString(display->id());
73 unit->name = display_manager->GetDisplayNameForId(display->id());
74 unit->is_primary = (display->id() == primary_id);
75 if (display_manager->IsMirrored() &&
76 display_manager->mirrored_display().id() != display->id()) {
77 unit->mirroring_source_id =
78 base::Int64ToString(display_manager->mirrored_display().id());
79 }
80 unit->is_internal = display->IsInternal();
81 unit->is_enabled = true;
82 unit->dpi_x = dpi;
83 unit->dpi_y = dpi;
84 unit->rotation = RotationToDegrees(display->rotation());
85 unit->bounds.left = bounds.x();
86 unit->bounds.top = bounds.y();
87 unit->bounds.width = bounds.width();
88 unit->bounds.height = bounds.height();
89 unit->overscan.left = overscan_insets.left();
90 unit->overscan.top = overscan_insets.top();
91 unit->overscan.right = overscan_insets.right();
92 unit->overscan.bottom = overscan_insets.bottom();
93 unit->work_area.left = work_area.x();
94 unit->work_area.top = work_area.y();
95 unit->work_area.width = work_area.width();
96 unit->work_area.height = work_area.height();
97 info->push_back(unit);
98 } 444 }
99 445
100 return true; 446 return true;
101 } 447 }
102 448
103 } // namespace extensions 449 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698