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

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

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