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

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

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

Powered by Google App Engine
This is Rietveld 408576698