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

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

Issue 2573673003: Detect and fix overlapping displays (Closed)
Patch Set: Nits 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 or Y axes for the target display
185 // with |target_bounds| to de-intersect with the source display with
186 // |source_bounds|.
187 // These functions assume both displays already intersect.
188 int CalculateOffsetX(const gfx::Rect& source_bounds,
189 const gfx::Rect& target_bounds) {
190 if (target_bounds.x() >= 0) {
191 // Target display moves along the +ve X direction.
192 return source_bounds.right() - target_bounds.x();
193 }
194
195 // Target display moves along the -ve X direction.
196 return -(target_bounds.right() - source_bounds.x());
197 }
198 int CalculateOffsetY(const gfx::Rect& source_bounds,
199 const gfx::Rect& target_bounds) {
200 if (target_bounds.y() >= 0) {
201 // Target display moves along the +ve Y direction.
202 return source_bounds.bottom() - target_bounds.y();
203 }
204
205 // Target display moves along the -ve Y direction.
206 return -(target_bounds.bottom() - source_bounds.y());
207 }
208
209 // Fixes any overlapping displays and reparents displays if necessary.
210 void DeIntersectDisplays(int64_t primary_id,
211 Displays* display_list,
212 std::vector<DisplayPlacement>* placement_list,
213 std::set<int64_t>* updated_displays) {
214 std::map<int64_t, int64_t> display_to_parent_ids_map;
215 for (const DisplayPlacement& placement : *placement_list) {
216 display_to_parent_ids_map.insert(
217 std::make_pair(placement.display_id, placement.parent_display_id));
218 }
219
220 std::vector<Display*> sorted_displays;
221 for (Display& display : *display_list)
222 sorted_displays.push_back(&display);
223
224 // Sort the displays first by their depth in the display hierarchy tree, and
225 // then by distance of their top left points from the origin. This way we
226 // process the displays starting at the root (the primary display), in the
227 // order of their decendence spanning out from the primary display.
228 std::sort(sorted_displays.begin(), sorted_displays.end(), [&](Display* d1,
229 Display* d2) {
230 const int d1_depth =
231 GetDisplayTreeDepth(d1->id(), primary_id, display_to_parent_ids_map);
232 const int d2_depth =
233 GetDisplayTreeDepth(d2->id(), primary_id, display_to_parent_ids_map);
234
235 if (d1_depth != d2_depth)
236 return d1_depth < d2_depth;
237
238 return d1->bounds().OffsetFromOrigin().LengthSquared() <
239 d2->bounds().OffsetFromOrigin().LengthSquared();
240 });
241 // This must result in the primary display being at the front of the list.
242 DCHECK_EQ(sorted_displays.front()->id(), primary_id);
243
244 for (int i = 1; i < static_cast<int>(sorted_displays.size()); ++i) {
245 Display* target_display = sorted_displays[i];
246 const Display* last_intersecting_source_display = nullptr;
247 int last_offset_x = 0;
248 int last_offset_y = 0;
249 for (int j = i - 1; j >= 0; --j) {
250 const Display* source_display = sorted_displays[j];
251 const gfx::Rect source_bounds = source_display->bounds();
252 const gfx::Rect target_bounds = target_display->bounds();
253
254 gfx::Rect intersection = source_bounds;
255 intersection.Intersect(target_bounds);
256
257 if (intersection.IsEmpty())
258 continue;
259
260 // Calculate offsets along both X and Y axes such that either can remove
261 // the overlap, but choose and apply the smaller offset. This way we have
262 // more predictable results.
263 int offset_x = 0;
264 int offset_y = 0;
265 if (intersection.width())
266 offset_x = CalculateOffsetX(source_bounds, target_bounds);
267 if (intersection.height())
268 offset_y = CalculateOffsetY(source_bounds, target_bounds);
269
270 if (offset_x == 0 && offset_y == 0)
271 continue;
272
273 // Choose the smaller offset.
274 if (std::abs(offset_x) <= std::abs(offset_y))
275 offset_y = 0;
276 else
277 offset_x = 0;
278
279 OffsetDisplay(target_display, offset_x, offset_y);
280 updated_displays->insert(target_display->id());
281
282 // The most recent performed de-intersection data.
283 last_intersecting_source_display = source_display;
284 last_offset_x = offset_x;
285 last_offset_y = offset_y;
286 }
287
288 if (!last_intersecting_source_display)
289 continue;
290
291 MaybeReparentTargetDisplay(last_offset_x, last_offset_y,
292 last_intersecting_source_display, target_display,
293 &display_to_parent_ids_map, display_list,
294 placement_list);
295 }
296
297 // The offsets might have changed and we must update them.
298 UpdateOffsets(display_list, placement_list);
44 } 299 }
45 300
46 } // namespace 301 } // namespace
47 302
48 //////////////////////////////////////////////////////////////////////////////// 303 ////////////////////////////////////////////////////////////////////////////////
49 // DisplayPlacement 304 // DisplayPlacement
50 305
51 DisplayPlacement::DisplayPlacement() 306 DisplayPlacement::DisplayPlacement()
52 : DisplayPlacement(kInvalidDisplayId, 307 : DisplayPlacement(kInvalidDisplayId,
53 kInvalidDisplayId, 308 kInvalidDisplayId,
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 //////////////////////////////////////////////////////////////////////////////// 430 ////////////////////////////////////////////////////////////////////////////////
176 // DisplayLayout 431 // DisplayLayout
177 432
178 DisplayLayout::DisplayLayout() 433 DisplayLayout::DisplayLayout()
179 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {} 434 : mirrored(false), default_unified(true), primary_id(kInvalidDisplayId) {}
180 435
181 DisplayLayout::~DisplayLayout() {} 436 DisplayLayout::~DisplayLayout() {}
182 437
183 void DisplayLayout::ApplyToDisplayList(Displays* display_list, 438 void DisplayLayout::ApplyToDisplayList(Displays* display_list,
184 std::vector<int64_t>* updated_ids, 439 std::vector<int64_t>* updated_ids,
185 int minimum_offset_overlap) const { 440 int minimum_offset_overlap) {
441 if (placement_list.empty())
442 return;
443
444 if (!DisplayLayout::Validate(DisplayListToDisplayIdList(*display_list),
445 *this)) {
446 // Prevent invalid and non-relevant display layouts.
447 return;
448 }
449
186 // Layout from primary, then dependent displays. 450 // Layout from primary, then dependent displays.
187 std::set<int64_t> parents; 451 std::set<int64_t> parents;
452 std::set<int64_t> updated_displays;
188 parents.insert(primary_id); 453 parents.insert(primary_id);
189 while (parents.size()) { 454 while (parents.size()) {
190 int64_t parent_id = *parents.begin(); 455 int64_t parent_id = *parents.begin();
191 parents.erase(parent_id); 456 parents.erase(parent_id);
192 for (const DisplayPlacement& placement : placement_list) { 457 for (const DisplayPlacement& placement : placement_list) {
193 if (placement.parent_display_id == parent_id) { 458 if (placement.parent_display_id == parent_id) {
194 if (ApplyDisplayPlacement(placement, display_list, 459 if (ApplyDisplayPlacement(placement, display_list,
195 minimum_offset_overlap) && 460 minimum_offset_overlap)) {
196 updated_ids) { 461 updated_displays.insert(placement.display_id);
197 updated_ids->push_back(placement.display_id);
198 } 462 }
199 parents.insert(placement.display_id); 463 parents.insert(placement.display_id);
200 } 464 }
201 } 465 }
202 } 466 }
467
468 // Now that all the placements have been applied, we must detect and fix any
469 // overlapping displays.
470 DeIntersectDisplays(primary_id, display_list, &placement_list,
471 &updated_displays);
472
473 if (updated_ids) {
474 updated_ids->insert(updated_ids->begin(), updated_displays.begin(),
475 updated_displays.end());
476 }
203 } 477 }
204 478
205 // static 479 // static
206 bool DisplayLayout::Validate(const DisplayIdList& list, 480 bool DisplayLayout::Validate(const DisplayIdList& list,
207 const DisplayLayout& layout) { 481 const DisplayLayout& layout) {
208 // The primary display should be in the list. 482 // The primary display should be in the list.
209 DCHECK(IsIdInList(layout.primary_id, list)); 483 if (!IsIdInList(layout.primary_id, list)) {
484 LOG(ERROR) << "The primary id: " << layout.primary_id
485 << " is not in the id list.";
486 return false;
487 }
210 488
211 // Unified mode, or mirror mode switched from unified mode, 489 // Unified mode, or mirror mode switched from unified mode,
212 // may not have the placement yet. 490 // may not have the placement yet.
213 if (layout.placement_list.size() == 0u) 491 if (layout.placement_list.size() == 0u)
214 return true; 492 return true;
215 493
216 bool has_primary_as_parent = false; 494 bool has_primary_as_parent = false;
217 int64_t prev_id = std::numeric_limits<int64_t>::min(); 495 int64_t prev_id = std::numeric_limits<int64_t>::min();
218 for (const auto& placement : layout.placement_list) { 496 for (const auto& placement : layout.placement_list) {
219 // Placements are sorted by display_id. 497 // Placements are sorted by display_id.
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 634
357 gfx::Insets insets = target_display->GetWorkAreaInsets(); 635 gfx::Insets insets = target_display->GetWorkAreaInsets();
358 target_display->set_bounds( 636 target_display->set_bounds(
359 gfx::Rect(new_target_origin, target_bounds.size())); 637 gfx::Rect(new_target_origin, target_bounds.size()));
360 target_display->UpdateWorkAreaFromInsets(insets); 638 target_display->UpdateWorkAreaFromInsets(insets);
361 639
362 return old_bounds != target_display->bounds(); 640 return old_bounds != target_display->bounds();
363 } 641 }
364 642
365 } // namespace display 643 } // 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