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

Side by Side Diff: ui/display/display_layout.cc

Issue 2573673003: Detect and fix overlapping displays (Closed)
Patch Set: Nit Created 3 years, 10 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "ui/display/display_layout.h" 5 #include "ui/display/display_layout.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <map>
8 #include <set> 9 #include <set>
9 #include <sstream> 10 #include <sstream>
10 11
11 #include "base/logging.h" 12 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringprintf.h" 14 #include "base/strings/stringprintf.h"
14 #include "base/values.h" 15 #include "base/values.h"
15 #include "ui/display/display.h" 16 #include "ui/display/display.h"
16 #include "ui/gfx/geometry/insets.h" 17 #include "ui/gfx/geometry/insets.h"
17 18
(...skipping 11 matching lines...) Expand all
29 // to change this value in case to support even larger displays. 30 // to change this value in case to support even larger displays.
30 const int kMaxValidOffset = 10000; 31 const int kMaxValidOffset = 10000;
31 32
32 bool IsIdInList(int64_t id, const DisplayIdList& list) { 33 bool IsIdInList(int64_t id, const DisplayIdList& list) {
33 const auto iter = 34 const auto iter =
34 std::find_if(list.begin(), list.end(), 35 std::find_if(list.begin(), list.end(),
35 [id](int64_t display_id) { return display_id == id; }); 36 [id](int64_t display_id) { return display_id == id; });
36 return iter != list.end(); 37 return iter != list.end();
37 } 38 }
38 39
40 // Extracts the displays IDs list from the displays list.
41 DisplayIdList DisplayListToDisplayIdList(const Displays& displays) {
42 DisplayIdList list;
43 for (const auto& display : displays)
44 list.emplace_back(display.id());
45
46 return list;
47 }
48
49 // Ruturns nullptr if display with |id| is not found.
39 Display* FindDisplayById(Displays* display_list, int64_t id) { 50 Display* FindDisplayById(Displays* display_list, int64_t id) {
40 auto iter = 51 auto iter =
41 std::find_if(display_list->begin(), display_list->end(), 52 std::find_if(display_list->begin(), display_list->end(),
42 [id](const Display& display) { return display.id() == id; }); 53 [id](const Display& display) { return display.id() == id; });
43 return &(*iter); 54 return iter == display_list->end() ? nullptr : &(*iter);
55 }
56
57 // Returns the tree depth of the display with ID |display_id| from the tree root
58 // (i.e. from the primary display).
59 int GetDisplayTreeDepth(
60 int64_t display_id,
61 int64_t primary_id,
62 const std::map<int64_t, int64_t>& display_to_parent_ids_map) {
63 if (display_id == primary_id)
64 return 0;
65
66 int64_t current_id = display_id;
67 int depth = 0;
68 while (display_to_parent_ids_map.count(current_id)) {
stevenjb 2017/01/31 21:58:56 This is a little confusing. I believe this test is
afakhry 2017/01/31 23:23:44 I removed that check entirely. This error would be
69 ++depth;
70 int64_t parent_id = display_to_parent_ids_map.at(current_id);
71 if (parent_id == primary_id)
72 break;
73 current_id = parent_id;
74 }
75
76 return depth;
77 }
78
79 // Returns true if the child and parent displays are sharing a border that
80 // matches the child's relative position to its parent.
81 bool AreDisplaysTouching(const Display& child_display,
82 const Display& parent_display,
83 DisplayPlacement::Position child_position) {
84 const gfx::Rect& a_bounds = child_display.bounds();
85 const gfx::Rect& b_bounds = parent_display.bounds();
86
87 int rx = std::max(a_bounds.x(), b_bounds.x());
88 int ry = std::max(a_bounds.y(), b_bounds.y());
89 int rr = std::min(a_bounds.right(), b_bounds.right());
90 int rb = std::min(a_bounds.bottom(), b_bounds.bottom());
91
92 if (child_position == DisplayPlacement::TOP ||
93 child_position == DisplayPlacement::BOTTOM) {
94 return rb == ry;
stevenjb 2017/01/31 21:58:56 Calculate just rb and ry inside the if, and rr and
afakhry 2017/01/31 23:23:44 Done.
95 }
96
97 return rr == rx;
98 }
99
100 // After the layout has been applied to the |display_list| and any possible
101 // overlaps have been fixed, this function is called to update the offsets in
102 // the |placement_list|.
103 void UpdateOffsets(Displays* display_list,
104 std::vector<DisplayPlacement>* placement_list) {
105 for (DisplayPlacement& placement : *placement_list) {
106 const Display* child_display =
107 FindDisplayById(display_list, placement.display_id);
108 const Display* parent_display =
109 FindDisplayById(display_list, placement.parent_display_id);
110
111 if (!child_display || !parent_display)
112 continue;
113
114 const gfx::Rect& child_bounds = child_display->bounds();
115 const gfx::Rect& parent_bounds = parent_display->bounds();
116
117 if (placement.position == DisplayPlacement::TOP ||
118 placement.position == DisplayPlacement::BOTTOM) {
119 placement.offset = child_bounds.x() - parent_bounds.x();
120 } else {
121 placement.offset = child_bounds.y() - parent_bounds.y();
122 }
123 }
124 }
125
126 // Reparents |target_display| to |last_intersecting_source_display| if it's not
127 // touching with its current parent.
128 void MaybeReparentTargetDisplay(
129 int last_offset_x,
130 int last_offset_y,
131 const Display* last_intersecting_source_display,
132 const Display* target_display,
133 std::map<int64_t, int64_t>* display_to_parent_ids_map,
134 Displays* display_list,
135 std::vector<DisplayPlacement>* placement_list) {
136 if (!last_intersecting_source_display)
137 return;
stevenjb 2017/01/31 21:58:56 nit: Test this before calling instead.
afakhry 2017/01/31 23:23:44 Done.
138
139 // A de-intersection was performed.
140 // The offset target display may have moved such that it no longer touches
141 // its parent. Reparent if necessary.
142 const int64_t parent_display_id =
143 display_to_parent_ids_map->at(target_display->id());
144 if (parent_display_id == last_intersecting_source_display->id()) {
145 // It was just de-intersected with the source display in such a way that
146 // they're touching, and the source display is its parent. So no need to
147 // do any reparenting.
148 return;
149 }
150
151 Display* parent_display = FindDisplayById(display_list, parent_display_id);
152 DCHECK(parent_display);
153
154 auto target_display_placement_itr =
155 std::find_if(placement_list->begin(), placement_list->end(),
156 [&target_display](const DisplayPlacement& p) {
157 return p.display_id == target_display->id();
158 });
159 DCHECK(target_display_placement_itr != placement_list->end());
160 DisplayPlacement& target_display_placement = *target_display_placement_itr;
161 if (AreDisplaysTouching(*target_display, *parent_display,
162 target_display_placement.position)) {
163 return;
164 }
165
166 // Reparent the target to source and update the position. No need to
167 // update the offset here as it will be done later when UpdateOffsets()
168 // is called.
169 target_display_placement.parent_display_id =
170 last_intersecting_source_display->id();
171 // Update the map.
172 (*display_to_parent_ids_map)[target_display->id()] =
173 last_intersecting_source_display->id();
174
175 if (last_offset_x) {
176 target_display_placement.position =
177 last_offset_x > 0 ? DisplayPlacement::RIGHT : DisplayPlacement::LEFT;
178 } else {
179 target_display_placement.position =
180 last_offset_y > 0 ? DisplayPlacement::BOTTOM : DisplayPlacement::TOP;
181 }
182 }
183
184 // Fixes any overlapping displays and repared displays if necessary.
stevenjb 2017/01/31 21:58:56 repairs
afakhry 2017/01/31 23:23:44 I think I meant to say, "reparents".
stevenjb 2017/01/31 23:40:55 That makes more sense :)
185 void DeIntersectDisplays(int64_t primary_id,
186 Displays* display_list,
187 std::vector<DisplayPlacement>* placement_list,
188 std::set<int64_t>* updated_displays) {
189 std::map<int64_t, int64_t> display_to_parent_ids_map;
190 for (const DisplayPlacement& placement : *placement_list) {
191 display_to_parent_ids_map.insert(
192 std::make_pair(placement.display_id, placement.parent_display_id));
193 }
194
195 std::vector<Display*> sorted_displays;
196 for (Display& display : *display_list)
197 sorted_displays.push_back(&display);
198
199 // Sort the displays first by their depth in the display hierarchy tree, and
200 // then by distance of their top left points from the origin. This way we
201 // process the displays starting at the root (the primary display), in the
202 // order of their decendence spanning out from the primary display.
203 std::sort(sorted_displays.begin(), sorted_displays.end(), [&](Display* d1,
204 Display* d2) {
205 const int d1_depth =
206 GetDisplayTreeDepth(d1->id(), primary_id, display_to_parent_ids_map);
207 const int d2_depth =
208 GetDisplayTreeDepth(d2->id(), primary_id, display_to_parent_ids_map);
209
210 if (d1_depth != d2_depth)
211 return d1_depth < d2_depth;
212
213 return d1->bounds().OffsetFromOrigin().LengthSquared() <
214 d2->bounds().OffsetFromOrigin().LengthSquared();
215 });
216 // This must result in the primary display being at the front of the list.
217 DCHECK_EQ(sorted_displays.front()->id(), primary_id);
218
219 for (int i = 1; i < static_cast<int>(sorted_displays.size()); ++i) {
220 Display* target_display = sorted_displays[i];
221 const Display* last_intersecting_source_display = nullptr;
222 int last_offset_x = 0;
223 int last_offset_y = 0;
224 for (int j = i - 1; j >= 0; --j) {
225 const Display* source_display = sorted_displays[j];
226 const gfx::Rect source_bounds = source_display->bounds();
227 const gfx::Rect target_bounds = target_display->bounds();
228
229 gfx::Rect intersection = source_bounds;
230 intersection.Intersect(target_bounds);
231
232 if (intersection.IsEmpty())
233 continue;
234
stevenjb 2017/01/31 21:58:56 It would be nice to move some or all of the rest o
afakhry 2017/01/31 23:23:44 Done.
235 // Calculate offsets along both X and Y axes such that either can remove
236 // the overlap, but choose and apply the smaller offset. This way we have
237 // a more predictable results.
238 int offset_x = 0;
239 int offset_y = 0;
240 if (intersection.width()) {
241 if (target_bounds.x() >= 0) {
242 // Target display moves along the +ve X direction.
243 offset_x = source_bounds.right() - target_bounds.x();
244 } else {
245 // Target display moves along the -ve X direction.
246 offset_x = -(target_bounds.right() - source_bounds.x());
247 }
248 }
249
250 if (intersection.height()) {
251 if (target_bounds.y() >= 0) {
252 // Target display moves along the +ve Y direction.
253 offset_y = source_bounds.bottom() - target_bounds.y();
254 } else {
255 // Target display moves along the -ve Y direction.
256 offset_y = -(target_bounds.bottom() - source_bounds.y());
257 }
258 }
259
260 if (offset_x == 0 && offset_y == 0)
261 continue;
262
263 // Choose the smaller offset.
264 if (std::abs(offset_x) <= std::abs(offset_y))
265 offset_y = 0;
266 else
267 offset_x = 0;
268
269 gfx::Point new_origin = target_bounds.origin();
270 new_origin.Offset(offset_x, offset_y);
271 gfx::Insets insets = target_display->GetWorkAreaInsets();
272 target_display->set_bounds(gfx::Rect(new_origin, target_bounds.size()));
273 target_display->UpdateWorkAreaFromInsets(insets);
274
275 if (updated_displays)
stevenjb 2017/01/31 21:58:56 This appears to always be true?
afakhry 2017/01/31 23:23:44 Correct. Done.
276 updated_displays->insert(target_display->id());
277
278 // The most recent performed de-intersection data.
279 last_intersecting_source_display = source_display;
280 last_offset_x = offset_x;
281 last_offset_y = offset_y;
282 }
283
284 MaybeReparentTargetDisplay(last_offset_x, last_offset_y,
285 last_intersecting_source_display, target_display,
286 &display_to_parent_ids_map, display_list,
287 placement_list);
288 }
289
290 // The offsets might have changed and we must update them.
291 UpdateOffsets(display_list, placement_list);
44 } 292 }
45 293
46 } // namespace 294 } // namespace
47 295
48 //////////////////////////////////////////////////////////////////////////////// 296 ////////////////////////////////////////////////////////////////////////////////
49 // DisplayPlacement 297 // DisplayPlacement
50 298
51 DisplayPlacement::DisplayPlacement() 299 DisplayPlacement::DisplayPlacement()
52 : DisplayPlacement(kInvalidDisplayId, 300 : DisplayPlacement(kInvalidDisplayId,
53 kInvalidDisplayId, 301 kInvalidDisplayId,
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 //////////////////////////////////////////////////////////////////////////////// 423 ////////////////////////////////////////////////////////////////////////////////
176 // DisplayLayout 424 // DisplayLayout
177 425
178 DisplayLayout::DisplayLayout() 426 DisplayLayout::DisplayLayout()
179 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {} 427 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {}
180 428
181 DisplayLayout::~DisplayLayout() {} 429 DisplayLayout::~DisplayLayout() {}
182 430
183 void DisplayLayout::ApplyToDisplayList(Displays* display_list, 431 void DisplayLayout::ApplyToDisplayList(Displays* display_list,
184 std::vector<int64_t>* updated_ids, 432 std::vector<int64_t>* updated_ids,
185 int minimum_offset_overlap) const { 433 int minimum_offset_overlap) {
434 if (placement_list.empty())
435 return;
436
437 if (!DisplayLayout::Validate(DisplayListToDisplayIdList(*display_list),
438 *this)) {
439 // Prevent invalid and non-relevant display layouts.
440 return;
441 }
442
186 // Layout from primary, then dependent displays. 443 // Layout from primary, then dependent displays.
187 std::set<int64_t> parents; 444 std::set<int64_t> parents;
445 std::set<int64_t> updated_displays;
188 parents.insert(primary_id); 446 parents.insert(primary_id);
189 while (parents.size()) { 447 while (parents.size()) {
190 int64_t parent_id = *parents.begin(); 448 int64_t parent_id = *parents.begin();
191 parents.erase(parent_id); 449 parents.erase(parent_id);
192 for (const DisplayPlacement& placement : placement_list) { 450 for (const DisplayPlacement& placement : placement_list) {
193 if (placement.parent_display_id == parent_id) { 451 if (placement.parent_display_id == parent_id) {
194 if (ApplyDisplayPlacement(placement, display_list, 452 if (ApplyDisplayPlacement(placement, display_list,
195 minimum_offset_overlap) && 453 minimum_offset_overlap)) {
196 updated_ids) { 454 updated_displays.insert(placement.display_id);
197 updated_ids->push_back(placement.display_id);
198 } 455 }
199 parents.insert(placement.display_id); 456 parents.insert(placement.display_id);
200 } 457 }
201 } 458 }
202 } 459 }
460
461 // Now that all the placements have been applied, we must detect and fix any
462 // overlapping displays.
463 DeIntersectDisplays(primary_id, display_list, &placement_list,
464 &updated_displays);
465
466 if (updated_ids) {
467 updated_ids->insert(updated_ids->begin(), updated_displays.begin(),
468 updated_displays.end());
469 }
203 } 470 }
204 471
205 // static 472 // static
206 bool DisplayLayout::Validate(const DisplayIdList& list, 473 bool DisplayLayout::Validate(const DisplayIdList& list,
207 const DisplayLayout& layout) { 474 const DisplayLayout& layout) {
208 // The primary display should be in the list. 475 // The primary display should be in the list.
209 DCHECK(IsIdInList(layout.primary_id, list)); 476 if (!IsIdInList(layout.primary_id, list)) {
477 LOG(ERROR) << "The primary id: " << layout.primary_id
478 << " is not in the id list.";
479 return false;
480 }
210 481
211 // Unified mode, or mirror mode switched from unified mode, 482 // Unified mode, or mirror mode switched from unified mode,
212 // may not have the placement yet. 483 // may not have the placement yet.
213 if (layout.placement_list.size() == 0u) 484 if (layout.placement_list.size() == 0u)
214 return true; 485 return true;
215 486
216 bool has_primary_as_parent = false; 487 bool has_primary_as_parent = false;
217 int64_t prev_id = std::numeric_limits<int64_t>::min(); 488 int64_t prev_id = std::numeric_limits<int64_t>::min();
218 for (const auto& placement : layout.placement_list) { 489 for (const auto& placement : layout.placement_list) {
219 // Placements are sorted by display_id. 490 // Placements are sorted by display_id.
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 627
357 gfx::Insets insets = target_display->GetWorkAreaInsets(); 628 gfx::Insets insets = target_display->GetWorkAreaInsets();
358 target_display->set_bounds( 629 target_display->set_bounds(
359 gfx::Rect(new_target_origin, target_bounds.size())); 630 gfx::Rect(new_target_origin, target_bounds.size()));
360 target_display->UpdateWorkAreaFromInsets(insets); 631 target_display->UpdateWorkAreaFromInsets(insets);
361 632
362 return old_bounds != target_display->bounds(); 633 return old_bounds != target_display->bounds();
363 } 634 }
364 635
365 } // namespace display 636 } // namespace display
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698