// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/display/display_layout.h" #include <algorithm> #include <sstream> #include "ash/ash_switches.h" #include "ash/display/display_pref_util.h" #include "ash/shell.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "ui/gfx/display.h" namespace ash { namespace { // DisplayPlacement Positions const char kTop[] = "top"; const char kRight[] = "right"; const char kBottom[] = "bottom"; const char kLeft[] = "left"; const char kUnknown[] = "unknown"; // The maximum value for 'offset' in DisplayLayout in case of outliers. Need // to change this value in case to support even larger displays. const int kMaxValidOffset = 10000; bool IsIdInList(int64_t id, const DisplayIdList& list) { const auto iter = std::find_if(list.begin(), list.end(), [id](int64_t display_id) { return display_id == id; }); return iter != list.end(); } } // namespace //////////////////////////////////////////////////////////////////////////////// // DisplayPlacement DisplayPlacement::DisplayPlacement() : display_id(gfx::Display::kInvalidDisplayID), parent_display_id(gfx::Display::kInvalidDisplayID), position(DisplayPlacement::RIGHT), offset(0) {} DisplayPlacement::DisplayPlacement(Position pos, int offset) : display_id(gfx::Display::kInvalidDisplayID), parent_display_id(gfx::Display::kInvalidDisplayID), position(pos), offset(offset) { DCHECK_LE(TOP, position); DCHECK_GE(LEFT, position); // Set the default value to |position| in case position is invalid. DCHECKs // above doesn't stop in Release builds. if (TOP > position || LEFT < position) this->position = RIGHT; DCHECK_GE(kMaxValidOffset, abs(offset)); } DisplayPlacement::DisplayPlacement(const DisplayPlacement& placement) : display_id(placement.display_id), parent_display_id(placement.parent_display_id), position(placement.position), offset(placement.offset) {} DisplayPlacement& DisplayPlacement::Swap() { switch (position) { case TOP: position = BOTTOM; break; case BOTTOM: position = TOP; break; case RIGHT: position = LEFT; break; case LEFT: position = RIGHT; break; } offset = -offset; std::swap(display_id, parent_display_id); return *this; } std::string DisplayPlacement::ToString() const { std::stringstream s; if (display_id != gfx::Display::kInvalidDisplayID) s << "id=" << display_id << ", "; if (parent_display_id != gfx::Display::kInvalidDisplayID) s << "parent=" << parent_display_id << ", "; s << PositionToString(position) << ", "; s << offset; return s.str(); } // static std::string DisplayPlacement::PositionToString(Position position) { switch (position) { case TOP: return kTop; case RIGHT: return kRight; case BOTTOM: return kBottom; case LEFT: return kLeft; } return kUnknown; } // static bool DisplayPlacement::StringToPosition(const base::StringPiece& string, Position* position) { if (string == kTop) { *position = TOP; return true; } if (string == kRight) { *position = RIGHT; return true; } if (string == kBottom) { *position = BOTTOM; return true; } if (string == kLeft) { *position = LEFT; return true; } LOG(ERROR) << "Invalid position value:" << string; return false; } //////////////////////////////////////////////////////////////////////////////// // DisplayLayout DisplayLayout::DisplayLayout() : mirrored(false), default_unified(true), primary_id(gfx::Display::kInvalidDisplayID) {} DisplayLayout::~DisplayLayout() {} // static bool DisplayLayout::Validate(const DisplayIdList& list, const DisplayLayout& layout) { // The primary display should be in the list. DCHECK(IsIdInList(layout.primary_id, list)); // Unified mode, or mirror mode switched from unified mode, // may not have the placement yet. if (layout.placement_list.size() == 0u) return true; bool has_primary_as_parent = false; int64_t id = 0; for (const auto& placement : layout.placement_list) { // Placements are sorted by display_id. if (id >= placement.display_id) { LOG(ERROR) << "PlacementList must be sorted by display_id"; return false; } if (placement.display_id == gfx::Display::kInvalidDisplayID) { LOG(ERROR) << "display_id is not initialized"; return false; } if (placement.parent_display_id == gfx::Display::kInvalidDisplayID) { LOG(ERROR) << "display_parent_id is not initialized"; return false; } if (placement.display_id == placement.parent_display_id) { LOG(ERROR) << "display_id must not be same as parent_display_id"; return false; } if (!IsIdInList(placement.display_id, list)) { LOG(ERROR) << "display_id is not in the id list:" << placement.ToString(); return false; } if (!IsIdInList(placement.parent_display_id, list)) { LOG(ERROR) << "parent_display_id is not in the id list:" << placement.ToString(); return false; } has_primary_as_parent |= layout.primary_id == placement.parent_display_id; } if (!has_primary_as_parent) LOG(ERROR) << "At least, one placement must have the primary as a parent."; return has_primary_as_parent; } scoped_ptr<DisplayLayout> DisplayLayout::Copy() const { scoped_ptr<DisplayLayout> copy(new DisplayLayout); for (const auto& placement : placement_list) copy->placement_list.push_back(placement); copy->mirrored = mirrored; copy->default_unified = default_unified; copy->primary_id = primary_id; return copy; } bool DisplayLayout::HasSamePlacementList(const DisplayLayout& layout) const { if (placement_list.size() != layout.placement_list.size()) return false; for (size_t index = 0; index < placement_list.size(); index++) { const DisplayPlacement& placement1 = placement_list[index]; const DisplayPlacement& placement2 = layout.placement_list[index]; if (placement1.position != placement2.position || placement1.offset != placement2.offset || placement1.display_id != placement2.display_id || placement1.parent_display_id != placement2.parent_display_id) { return false; } } return true; } std::string DisplayLayout::ToString() const { std::stringstream s; s << "primary=" << primary_id; if (mirrored) s << ", mirrored"; if (default_unified) s << ", default_unified"; bool added = false; for (const auto& placement : placement_list) { s << (added ? "),(" : " [("); s << placement.ToString(); added = true; } if (added) s << ")]"; return s.str(); } DisplayPlacement DisplayLayout::FindPlacementById(int64_t display_id) const { const auto iter = std::find_if(placement_list.begin(), placement_list.end(), [display_id](const DisplayPlacement& placement) { return placement.display_id == display_id; }); return (iter == placement_list.end()) ? DisplayPlacement() : DisplayPlacement(*iter); } } // namespace ash