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

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: . 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::Insets;
24 using api::system_info_display::SetDisplayUnitInfoParams;
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 = 0x3fff;
Greg Spencer (Chromium) 2013/06/12 23:14:15 Why in hex? Isn't this in "pixels"?
tbarzic 2013/06/12 23:40:41 oh, I forgot to post a comment here: What would be
Greg Spencer (Chromium) 2013/06/13 00:11:47 I think the point was to limit the amount of overs
tbarzic 2013/06/13 00:18:25 yeah, we have that check for insets.. this is used
Greg Spencer (Chromium) 2013/06/13 00:57:04 Oh, duh. Sorry, I thought it was being used for t
tbarzic 2013/06/13 17:50:29 set to 200 000
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 // Created ash::DisplayLayout value for |rectangle| compared to the |reference|
113 // rectangle.
114 // The layout consists of two values:
115 // - position: Whether the rectangle is positioned left, right, over or under
116 // the reference.
117 // - offset: The rectangle's offset from the reference origin along the axis
118 // opposite the position direction (if the rectangle is left or right along
119 // y-axis, otherwise along x-axis).
120 // The rectangle's position is calculated by dividing the space in areas defined
121 // by the |reference|'s diagonals and finding the area |rectangle|'s center
122 // point belongs. If the |rectangle| in the calculated layout does not share a
123 // part of the bounds with the |reference|, the |rectangle| position in set to
124 // the more suitable neighboring position (e.g. if |rectangle| is completely
125 // over the |reference| top bound, it will be set to TOP) and the layout is
126 // recalculated with the new position. This is to handle case where the
127 // rectangle shares an edge with the reference, but it's center is not in the
128 // same area as the reference's edge, e.g.
129 //
130 // +---------------------+
131 // | |
132 // | REFERENCE |
133 // | |
134 // | |
135 // +---------------------+
136 // +-------------------------------------------------+
137 // | RECTANGLE x |
138 // +-------------------------------------------------+
139 //
140 // The rectangle shares an egde with the reference's bottom edge, but it's
141 // center point is in the left area.
142 ash::DisplayLayout GetLayoutForRectangles(const gfx::Rect& reference,
143 const gfx::Rect& rectangle) {
144 // Translate coordinate system so origin is in the reference's top left point
145 // (so the reference's down-diagonal vector starts in the (0, 0)) and scale it
146 // up by two (to avoid division when calculating the rectangle's center
147 // point).
148 gfx::Point center(2 * (rectangle.x() - reference.x()) + rectangle.width(),
149 2 * (rectangle.y() - reference.y()) + rectangle.height());
150 gfx::Point down_diag(2 * reference.width(), 2 * reference.height());
151
152 // The point is over the diagonal if the scalar product of it's radius-vector
153 // and the diagonal's orthogonal vector facing the area over the diagonal is
154 // non-negative (the points on the diagonal are considered to be over the
155 // diagonal).
156 // An orthogonal vector of (a, b) is (b, -a), as the scalar product of these
157 // two is 0.
158 // So, (x, y) is over (a, b) if x * b + y * (-a) >= 0, which is equivalent to
159 // x * b >= y * a.
160 bool is_top_right = center.x() * down_diag.y() >= center.y() * down_diag.x();
161
162 // Translate the coordinating system again, so the bottom right point of the
163 // reference is origin (so the references up-diagonal starts at (0, 0)).
164 // Note that the coordinate system is scaled by 2.
165 center.Offset(0, -2 * reference.height());
166 gfx::Point up_diag(2 * reference.width(), -2 * reference.height());
167
168 // Apply the same logic as for down-diagonal.
169 bool is_top_left = center.x() * up_diag.y() > center.y() * up_diag.x();
170
171 ash::DisplayLayout::Position position;
172 if (is_top_right) {
173 position = is_top_left ? ash::DisplayLayout::TOP :
174 ash::DisplayLayout::RIGHT;
175 } else {
176 position =
177 is_top_left ? ash::DisplayLayout::LEFT : ash::DisplayLayout::BOTTOM;
178 }
179
180 // If the rectangle with the calculated position would not have common side
181 // with the reference, try to position it so it shares another edge with the
182 // reference.
183 if (is_top_right != is_top_left) {
184 if (rectangle.y() > reference.y() + reference.height()) {
185 // The rectangle is left or right, but completely under the reference.
186 position = ash::DisplayLayout::BOTTOM;
187 } else if (rectangle.y() + rectangle.height() < reference.y()) {
188 // The rectangle is left or right, but completely over the reference.
189 position = ash::DisplayLayout::TOP;
190 }
191 } else {
192 if (rectangle.x() > reference.x() + reference.width()) {
193 // The rectangle is over or under, but completely right of the reference.
194 position = ash::DisplayLayout::RIGHT;
195 } else if (rectangle.x() + rectangle.width() < reference.x()){
196 // The rectangle is over or under, but completely left of the reference.
197 position = ash::DisplayLayout::LEFT;
198 }
199 }
200
201 if (position == ash::DisplayLayout::LEFT ||
202 position == ash::DisplayLayout::RIGHT) {
203 return ash::DisplayLayout::FromInts(position, rectangle.y());
204 } else {
205 return ash::DisplayLayout::FromInts(position, rectangle.x());
206 }
207 }
208
209 // Updates the display layout for the target display in reference to the primary
210 // display.
211 void UpdateDisplayLayout(const gfx::Rect& primary_display_bounds,
212 int primary_display_id,
213 const gfx::Rect& target_display_bounds,
214 int target_display_id) {
215 ash::DisplayLayout layout = GetLayoutForRectangles(primary_display_bounds,
216 target_display_bounds);
217 ash::DisplayController* display_controller =
218 ash::Shell::GetInstance()->display_controller();
219 display_controller->SetLayoutForCurrentDisplays(layout);
220 }
221
222 // Validates that parameters passed to the SetInfo function are valid for the
223 // desired display and the current display manager state.
224 // Returns whether the parameters are valid. On failure |error| is set to the
225 // error message.
226 bool ValidateParamsForDisplay(const SetDisplayUnitInfoParams& info,
227 const gfx::Display& display,
228 DisplayManager* display_manager,
229 int64 primary_display_id,
230 std::string* error) {
231 bool is_primary = display.id() == primary_display_id ||
232 (info.is_primary && *info.is_primary);
233
234 // If mirroring source id is set, a display with the given id should exist,
235 // and if should not be the same as the target display's id.
236 if (info.mirroring_source_id && !info.mirroring_source_id->empty()) {
237 int64 mirroring_id;
238 if (!base::StringToInt64(*info.mirroring_source_id, &mirroring_id) ||
239 display_manager->GetDisplayForId(mirroring_id).id() ==
240 gfx::Display::kInvalidDisplayID) {
241 *error = "Display " + *info.mirroring_source_id + " not found.";
242 return false;
243 }
244
245 if (*info.mirroring_source_id == base::Int64ToString(display.id())) {
246 *error = "Not allowed to mirror self.";
247 return false;
248 }
249 }
250
251 // If mirroring source parameter is specified, no other parameter should be
252 // set as when the mirroring is applied the display list could change.
253 if (info.mirroring_source_id && (info.is_primary || info.bounds_origin_x ||
254 info.bounds_origin_y || info.rotation || info.overscan)) {
255 *error = "No other parameter should be set alongside mirroringSourceId.";
256 return false;
257 }
258
259 // The bounds cannot be changed for the primary display and should be inside
260 // a reasonable bounds. Note that the display is considered primary if the
261 // info has 'isPrimary' parameter set, as this will be applied before bounds
262 // origin changes.
263 if (info.bounds_origin_x || info.bounds_origin_y) {
264 if (is_primary) {
265 *error = "Bounds origin not allowed for the primary display.";
266 return false;
267 }
268 if (info.bounds_origin_x &&
269 (*info.bounds_origin_x > kMaxBoundsOrigin ||
270 *info.bounds_origin_x < -kMaxBoundsOrigin)) {
271 *error = "Bounds origin x out of bounds.";
272 return false;
273 }
274 if (info.bounds_origin_y &&
275 (*info.bounds_origin_y > kMaxBoundsOrigin ||
276 *info.bounds_origin_y < -kMaxBoundsOrigin)) {
277 *error = "Bounds origin y out of bounds.";
278 return false;
279 }
280 }
281
282 // Verify the rotation value is valid.
283 if (info.rotation && !IsValidRotationValue(*info.rotation)) {
284 *error = "Invalid rotation.";
285 return false;
286 }
287
288 // Verify that rotation is enabled.
289 if (info.rotation && !display_manager->IsDisplayRotationEnabled()) {
290 *error = "Display rotation is not enabled.";
291 return false;
292 }
293
294 // Overscan cannot be changed for the internal display, and should be at most
295 // half of the screen size.
296 if (info.overscan) {
297 if (display.IsInternal()) {
298 *error = "Overscan changes not allowed for the internal monitor.";
299 return false;
300 }
301
302 if (info.overscan->left < 0 || info.overscan->top < 0 ||
303 info.overscan->right < 0 || info.overscan->bottom < 0) {
304 *error = "Negative overscan not allowed.";
305 return false;
306 }
307
308 const gfx::Insets overscan =
309 display_manager->GetOverscanInsets(display.id());
310 int screen_width = display.bounds().width() + overscan.width();
311 int screen_height = display.bounds().height() + overscan.height();
312
313 if ((info.overscan->left + info.overscan->right) * 2 > screen_width) {
314 *error = "Horizontal overscan is more than half of the screen width.";
315 return false;
316 }
317
318 if ((info.overscan->top + info.overscan->bottom) * 2 > screen_height) {
319 *error = "Vertical overscan is more than half of the screen height.";
320 return false;
321 }
322 }
323 return true;
324 }
325
326 // Gets the display with the provided string id.
327 const gfx::Display& GetTargetDisplay(const std::string& display_id_str,
328 DisplayManager* manager) {
329 int64 display_id;
330 if (!base::StringToInt64(display_id_str, &display_id)) {
331 // This should return invalid display.
332 return manager->GetDisplayForId(gfx::Display::kInvalidDisplayID);
333 }
334 return manager->GetDisplayForId(display_id);
335 }
336
337 // Updates the display with |display_id_str| id according to |params|. Returns
338 // whether the display was successfully updates. On failure, no display
Greg Spencer (Chromium) 2013/06/12 23:14:15 updates->updated
tbarzic 2013/06/12 23:40:41 Done.
339 // parameters should be changed, and |error| should be set to the error string.
340 bool SetInfoImpl(const std::string& display_id_str,
341 const SetDisplayUnitInfoParams& params,
342 std::string* error) {
343 DisplayManager* display_manager =
344 ash::Shell::GetInstance()->display_manager();
345 DCHECK(display_manager);
346 ash::DisplayController* display_controller =
347 ash::Shell::GetInstance()->display_controller();
348 DCHECK(display_controller);
349
350 const gfx::Display& target =
351 GetTargetDisplay(display_id_str, display_manager);
352
353 if (target.id() == gfx::Display::kInvalidDisplayID) {
354 *error = "Display not found.";
355 return false;
356 }
357
358 int64 display_id = target.id();
359 const gfx::Display& primary = ash::Shell::GetScreen()->GetPrimaryDisplay();
360
361 if (!ValidateParamsForDisplay(params, target, display_manager, primary.id(),
362 error)) {
363 return false;
364 }
365
366 // Process 'isPrimary' parameter.
367 if (params.is_primary && *params.is_primary && target.id() != primary.id())
368 display_controller->SetPrimaryDisplayId(display_id);
369
370 // Process 'mirroringSourceId' parameter.
371 if (params.mirroring_source_id &&
372 params.mirroring_source_id->empty() == display_manager->IsMirrored()) {
373 display_controller->ToggleMirrorMode();
374 }
375
376 // Process 'overscan' parameter.
377 if (params.overscan) {
378 display_manager->SetOverscanInsets(
379 display_id,
380 gfx::Insets(params.overscan->top, params.overscan->left,
381 params.overscan->bottom, params.overscan->right));
382 }
383
384 // Process 'rotation' parameter.
385 if (params.rotation) {
386 display_manager->SetDisplayRotation(display_id,
387 DegreesToRotation(*params.rotation));
388 }
389
390 // Process new display origin parameters.
391 gfx::Point new_bounds_origin = target.bounds().origin();
392 if (params.bounds_origin_x)
393 new_bounds_origin.set_x(*params.bounds_origin_x);
394 if (params.bounds_origin_y)
395 new_bounds_origin.set_y(*params.bounds_origin_y);
396
397 if (new_bounds_origin != target.bounds().origin()) {
398 gfx::Rect target_bounds = target.bounds();
399 target_bounds.Offset(new_bounds_origin.x() - target.bounds().x(),
400 new_bounds_origin.y() - target.bounds().y());
401 UpdateDisplayLayout(primary.bounds(), primary.id(),
402 target_bounds, target.id());
403 }
404
405 return true;
406 }
407
39 } // namespace 408 } // namespace
40 409
41 using api::system_info_display::Bounds;
42 using api::system_info_display::DisplayUnitInfo;
43
44 void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) { 410 void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
45 DisplayInfo requested_info; 411 DisplayInfo requested_info;
46 bool success = QueryInfo(&requested_info); 412 bool success = QueryInfo(&requested_info);
47 413
48 base::MessageLoopProxy::current()->PostTask( 414 base::MessageLoopProxy::current()->PostTask(
49 FROM_HERE, 415 FROM_HERE,
50 base::Bind(callback, requested_info, success)); 416 base::Bind(callback, requested_info, success));
51 } 417 }
52 418
419 void DisplayInfoProvider::SetInfo(const std::string& display_id,
420 const SetDisplayUnitInfoParams& info,
421 const SetInfoCallback& callback) {
422 std::string error;
423 bool success = SetInfoImpl(display_id, info, &error);
424 base::MessageLoopProxy::current()->PostTask(
425 FROM_HERE,
426 base::Bind(callback, success, error));
427 }
428
53 bool DisplayInfoProvider::QueryInfo(DisplayInfo* info) { 429 bool DisplayInfoProvider::QueryInfo(DisplayInfo* info) {
54 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
55 DCHECK(info); 430 DCHECK(info);
56 info->clear(); 431 info->clear();
57 432
58 DisplayManager* display_manager = 433 DisplayManager* display_manager =
59 ash::Shell::GetInstance()->display_manager(); 434 ash::Shell::GetInstance()->display_manager();
60 DCHECK(display_manager); 435 DCHECK(display_manager);
61 436
62 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id(); 437 int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
63 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { 438 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
64 linked_ptr<DisplayUnitInfo> unit(new DisplayUnitInfo()); 439 AddInfoForDisplay(*display_manager->GetDisplayAt(i), display_manager,
65 const gfx::Display* display = display_manager->GetDisplayAt(i); 440 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 } 441 }
99 442
100 return true; 443 return true;
101 } 444 }
102 445
103 } // namespace extensions 446 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698