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

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

Issue 2573673003: Detect and fix overlapping displays (Closed)
Patch Set: Reparent once 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
« no previous file with comments | « ui/display/display_layout.h ('k') | ui/display/manager/display_manager.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // Fixes any overlapping displays and repared displays if necessary.
126 void DeIntersectDisplays(int64_t primary_id,
127 Displays* display_list,
128 std::vector<DisplayPlacement>* placement_list,
129 std::set<int64_t>* updated_displays) {
130 std::map<int64_t, int64_t> display_to_parent_ids_map;
131 for (const DisplayPlacement& placement : *placement_list) {
132 display_to_parent_ids_map[placement.display_id] =
133 placement.parent_display_id;
134 }
135
136 std::vector<Display*> sorted_displays;
137 for (Display& display : *display_list)
138 sorted_displays.push_back(&display);
139
140 // Sort the displays first by their depth in the display hierarchy tree, and
141 // then by distance of their top left points from the origin. This way we
142 // process the displays starting at the root (the primary display), in the
143 // order of their decendence spanning out from the primary display.
144 std::sort(sorted_displays.begin(), sorted_displays.end(), [&](Display* d1,
145 Display* d2) {
146 const int d1_depth =
147 GetDisplayTreeDepth(d1->id(), primary_id, display_to_parent_ids_map);
148 const int d2_depth =
149 GetDisplayTreeDepth(d2->id(), primary_id, display_to_parent_ids_map);
150
151 if (d1_depth != d2_depth)
152 return d1_depth < d2_depth;
153
154 return d1->bounds().OffsetFromOrigin().LengthSquared() <
155 d2->bounds().OffsetFromOrigin().LengthSquared();
156 });
157 // This must result in the primary display being at the front of the list.
158 DCHECK_EQ(sorted_displays.front()->id(), primary_id);
159
160 for (size_t i = 1; i < sorted_displays.size(); ++i) {
afakhry 2017/01/30 22:25:57 Actually I need to use int here. I need to be able
oshima 2017/01/31 16:55:43 ok. You can actually iterate back on sorted_displa
161 Display* target_display = sorted_displays[i];
162 const Display* last_intersecting_source_display = nullptr;
163 int last_offset_x = 0;
164 int last_offset_y = 0;
165 for (int j = i - 1; j >= 0; --j) {
166 const Display* source_display = sorted_displays[j];
167 const gfx::Rect source_bounds = source_display->bounds();
168 const gfx::Rect target_bounds = target_display->bounds();
169
170 gfx::Rect intersection = source_bounds;
171 intersection.Intersect(target_bounds);
172
173 if (intersection.IsEmpty())
174 continue;
175
176 // Calculate offsets along both X and Y axes such that either can remove
177 // the overlap, but choose and apply the smaller offset. This way we have
178 // a more predictable results.
179 int offset_x = 0;
180 int offset_y = 0;
181 if (intersection.width()) {
182 if (target_bounds.x() >= 0) {
183 // Target display moves along the +ve X direction.
184 offset_x = source_bounds.right() - target_bounds.x();
185 } else {
186 // Target display moves along the -ve X direction.
187 offset_x = -(target_bounds.right() - source_bounds.x());
188 }
189 }
190
191 if (intersection.height()) {
192 if (target_bounds.y() >= 0) {
193 // Target display moves along the +ve Y direction.
194 offset_y = source_bounds.bottom() - target_bounds.y();
195 } else {
196 // Target display moves along the -ve Y direction.
197 offset_y = -(target_bounds.bottom() - source_bounds.y());
198 }
199 }
200
201 if (offset_x == 0 && offset_y == 0)
202 continue;
203
204 // Choose the smaller offset.
205 if (std::abs(offset_x) <= std::abs(offset_y))
206 offset_y = 0;
207 else
208 offset_x = 0;
209
210 gfx::Point new_origin = target_bounds.origin();
211 new_origin.Offset(offset_x, offset_y);
212 gfx::Insets insets = target_display->GetWorkAreaInsets();
213 target_display->set_bounds(gfx::Rect(new_origin, target_bounds.size()));
214 target_display->UpdateWorkAreaFromInsets(insets);
215
216 if (updated_displays)
217 updated_displays->insert(target_display->id());
oshima 2017/01/30 20:30:43 this can add the target display more than once if
afakhry 2017/01/30 22:25:57 This shouldn't be a problem since I already change
218
219 // The most recent performed de-intersection data.
220 last_intersecting_source_display = source_display;
221 last_offset_x = offset_x;
222 last_offset_y = offset_y;
223 }
224
225 if (!last_intersecting_source_display)
226 continue;
227
228 // A de-intersection was performed.
229 // The offset target display may have moved such that it no longer touches
230 // its parent. Reparent if necessary.
231 const int64_t parent_display_id =
232 display_to_parent_ids_map.at(target_display->id());
233 if (parent_display_id == last_intersecting_source_display->id()) {
234 // It was just de-intersected with the source display in such a way that
235 // they're touching, and the source display is its parent. So no need to
236 // do any reparenting.
237 continue;
238 }
239
240 Display* parent_display =
241 FindDisplayById(display_list, parent_display_id);
242 DCHECK(parent_display);
243
244 auto target_display_placement_itr =
245 std::find_if(placement_list->begin(), placement_list->end(),
246 [&target_display](const DisplayPlacement& p) {
247 return p.display_id == target_display->id();
248 });
249 DCHECK(target_display_placement_itr != placement_list->end());
250 DisplayPlacement& target_display_placement =
251 *target_display_placement_itr;
252 if (AreDisplaysTouching(*target_display, *parent_display,
253 target_display_placement.position)) {
254 continue;
255 }
256
257 // Reparent the target to source and update the position. No need to
258 // update the offset here as it will be done later when UpdateOffsets()
259 // is called.
260 target_display_placement.parent_display_id =
261 last_intersecting_source_display->id();
262 if (last_offset_x) {
263 target_display_placement.position =
264 last_offset_x > 0 ? DisplayPlacement::RIGHT : DisplayPlacement::LEFT;
265 } else {
266 target_display_placement.position =
267 last_offset_y > 0 ? DisplayPlacement::BOTTOM : DisplayPlacement::TOP;
268 }
oshima 2017/01/30 20:30:43 refactor "reparent" part to a method?
afakhry 2017/01/30 22:25:57 Done.
269 }
270
271 // The offsets might have changed and we must update them.
272 UpdateOffsets(display_list, placement_list);
44 } 273 }
45 274
46 } // namespace 275 } // namespace
47 276
48 //////////////////////////////////////////////////////////////////////////////// 277 ////////////////////////////////////////////////////////////////////////////////
49 // DisplayPlacement 278 // DisplayPlacement
50 279
51 DisplayPlacement::DisplayPlacement() 280 DisplayPlacement::DisplayPlacement()
52 : DisplayPlacement(kInvalidDisplayId, 281 : DisplayPlacement(kInvalidDisplayId,
53 kInvalidDisplayId, 282 kInvalidDisplayId,
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 //////////////////////////////////////////////////////////////////////////////// 404 ////////////////////////////////////////////////////////////////////////////////
176 // DisplayLayout 405 // DisplayLayout
177 406
178 DisplayLayout::DisplayLayout() 407 DisplayLayout::DisplayLayout()
179 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {} 408 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {}
180 409
181 DisplayLayout::~DisplayLayout() {} 410 DisplayLayout::~DisplayLayout() {}
182 411
183 void DisplayLayout::ApplyToDisplayList(Displays* display_list, 412 void DisplayLayout::ApplyToDisplayList(Displays* display_list,
184 std::vector<int64_t>* updated_ids, 413 std::vector<int64_t>* updated_ids,
185 int minimum_offset_overlap) const { 414 int minimum_offset_overlap) {
415 if (placement_list.empty())
416 return;
417
418 if (!DisplayLayout::Validate(DisplayListToDisplayIdList(*display_list),
419 *this)) {
420 // Prevent invalid and non-relevant display layouts.
421 return;
422 }
423
186 // Layout from primary, then dependent displays. 424 // Layout from primary, then dependent displays.
187 std::set<int64_t> parents; 425 std::set<int64_t> parents;
426 std::set<int64_t> updated_displays;
188 parents.insert(primary_id); 427 parents.insert(primary_id);
189 while (parents.size()) { 428 while (parents.size()) {
190 int64_t parent_id = *parents.begin(); 429 int64_t parent_id = *parents.begin();
191 parents.erase(parent_id); 430 parents.erase(parent_id);
192 for (const DisplayPlacement& placement : placement_list) { 431 for (const DisplayPlacement& placement : placement_list) {
193 if (placement.parent_display_id == parent_id) { 432 if (placement.parent_display_id == parent_id) {
194 if (ApplyDisplayPlacement(placement, display_list, 433 if (ApplyDisplayPlacement(placement, display_list,
195 minimum_offset_overlap) && 434 minimum_offset_overlap)) {
196 updated_ids) { 435 updated_displays.insert(placement.display_id);
197 updated_ids->push_back(placement.display_id);
198 } 436 }
199 parents.insert(placement.display_id); 437 parents.insert(placement.display_id);
200 } 438 }
201 } 439 }
202 } 440 }
441
442 // Now that all the placements have been applied, we must detect and fix any
443 // overlapping displays.
444 DeIntersectDisplays(primary_id, display_list, &placement_list,
445 &updated_displays);
446
447 if (updated_ids) {
448 updated_ids->insert(updated_ids->begin(), updated_displays.begin(),
449 updated_displays.end());
450 }
203 } 451 }
204 452
205 // static 453 // static
206 bool DisplayLayout::Validate(const DisplayIdList& list, 454 bool DisplayLayout::Validate(const DisplayIdList& list,
207 const DisplayLayout& layout) { 455 const DisplayLayout& layout) {
208 // The primary display should be in the list. 456 // The primary display should be in the list.
209 DCHECK(IsIdInList(layout.primary_id, list)); 457 if (!IsIdInList(layout.primary_id, list)) {
458 LOG(ERROR) << "The primary id: " << layout.primary_id
459 << " is not in the id list.";
460 return false;
461 }
210 462
211 // Unified mode, or mirror mode switched from unified mode, 463 // Unified mode, or mirror mode switched from unified mode,
212 // may not have the placement yet. 464 // may not have the placement yet.
213 if (layout.placement_list.size() == 0u) 465 if (layout.placement_list.size() == 0u)
214 return true; 466 return true;
215 467
216 bool has_primary_as_parent = false; 468 bool has_primary_as_parent = false;
217 int64_t prev_id = std::numeric_limits<int64_t>::min(); 469 int64_t prev_id = std::numeric_limits<int64_t>::min();
218 for (const auto& placement : layout.placement_list) { 470 for (const auto& placement : layout.placement_list) {
219 // Placements are sorted by display_id. 471 // Placements are sorted by display_id.
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 608
357 gfx::Insets insets = target_display->GetWorkAreaInsets(); 609 gfx::Insets insets = target_display->GetWorkAreaInsets();
358 target_display->set_bounds( 610 target_display->set_bounds(
359 gfx::Rect(new_target_origin, target_bounds.size())); 611 gfx::Rect(new_target_origin, target_bounds.size()));
360 target_display->UpdateWorkAreaFromInsets(insets); 612 target_display->UpdateWorkAreaFromInsets(insets);
361 613
362 return old_bounds != target_display->bounds(); 614 return old_bounds != target_display->bounds();
363 } 615 }
364 616
365 } // namespace display 617 } // namespace display
OLDNEW
« no previous file with comments | « ui/display/display_layout.h ('k') | ui/display/manager/display_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698