| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "ash/display/display_manager.h" | 5 #include "ash/display/display_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <string> | 10 #include <string> |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 DisplayManager::DisplayManager() | 111 DisplayManager::DisplayManager() |
| 112 : delegate_(NULL), | 112 : delegate_(NULL), |
| 113 screen_ash_(new ScreenAsh), | 113 screen_ash_(new ScreenAsh), |
| 114 screen_(screen_ash_.get()), | 114 screen_(screen_ash_.get()), |
| 115 layout_store_(new DisplayLayoutStore), | 115 layout_store_(new DisplayLayoutStore), |
| 116 first_display_id_(gfx::Display::kInvalidDisplayID), | 116 first_display_id_(gfx::Display::kInvalidDisplayID), |
| 117 num_connected_displays_(0), | 117 num_connected_displays_(0), |
| 118 force_bounds_changed_(false), | 118 force_bounds_changed_(false), |
| 119 change_display_upon_host_resize_(false), | 119 change_display_upon_host_resize_(false), |
| 120 second_display_mode_(EXTENDED), | 120 second_display_mode_(EXTENDED), |
| 121 mirrored_display_id_(gfx::Display::kInvalidDisplayID), | 121 mirroring_display_id_(gfx::Display::kInvalidDisplayID), |
| 122 registered_internal_display_rotation_lock_(false), | 122 registered_internal_display_rotation_lock_(false), |
| 123 registered_internal_display_rotation_(gfx::Display::ROTATE_0), | 123 registered_internal_display_rotation_(gfx::Display::ROTATE_0), |
| 124 weak_ptr_factory_(this) { | 124 weak_ptr_factory_(this) { |
| 125 | 125 |
| 126 #if defined(OS_CHROMEOS) | 126 #if defined(OS_CHROMEOS) |
| 127 // Enable only on the device so that DisplayManagerFontTest passes. | 127 // Enable only on the device so that DisplayManagerFontTest passes. |
| 128 if (base::SysInfo::IsRunningOnChromeOS()) | 128 if (base::SysInfo::IsRunningOnChromeOS()) |
| 129 DisplayInfo::SetUse125DSFForUIScaling(true); | 129 DisplayInfo::SetUse125DSFForUIScaling(true); |
| 130 | 130 |
| 131 change_display_upon_host_resize_ = !base::SysInfo::IsRunningOnChromeOS(); | 131 change_display_upon_host_resize_ = !base::SysInfo::IsRunningOnChromeOS(); |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 } | 233 } |
| 234 | 234 |
| 235 DisplayIdPair DisplayManager::GetCurrentDisplayIdPair() const { | 235 DisplayIdPair DisplayManager::GetCurrentDisplayIdPair() const { |
| 236 if (IsMirrored()) { | 236 if (IsMirrored()) { |
| 237 if (software_mirroring_enabled()) { | 237 if (software_mirroring_enabled()) { |
| 238 CHECK_EQ(2u, num_connected_displays()); | 238 CHECK_EQ(2u, num_connected_displays()); |
| 239 // This comment is to make it easy to distinguish the crash | 239 // This comment is to make it easy to distinguish the crash |
| 240 // between two checks. | 240 // between two checks. |
| 241 CHECK_EQ(1u, displays_.size()); | 241 CHECK_EQ(1u, displays_.size()); |
| 242 } | 242 } |
| 243 return std::make_pair(displays_[0].id(), mirrored_display_id_); | 243 return std::make_pair(displays_[0].id(), mirroring_display_id_); |
| 244 } else { | 244 } else { |
| 245 CHECK_LE(2u, displays_.size()); | 245 CHECK_LE(2u, displays_.size()); |
| 246 int64 id_at_zero = displays_[0].id(); | 246 int64 id_at_zero = displays_[0].id(); |
| 247 if (id_at_zero == gfx::Display::InternalDisplayId() || | 247 if (id_at_zero == gfx::Display::InternalDisplayId() || |
| 248 id_at_zero == first_display_id()) { | 248 id_at_zero == first_display_id()) { |
| 249 return std::make_pair(id_at_zero, displays_[1].id()); | 249 return std::make_pair(id_at_zero, displays_[1].id()); |
| 250 } else { | 250 } else { |
| 251 return std::make_pair(displays_[1].id(), id_at_zero); | 251 return std::make_pair(displays_[1].id(), id_at_zero); |
| 252 } | 252 } |
| 253 } | 253 } |
| (...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 600 if (updated_displays.size() == 1) { | 600 if (updated_displays.size() == 1) { |
| 601 VLOG(1) << "OnNativeDisplaysChanged(1):" << updated_displays[0].ToString(); | 601 VLOG(1) << "OnNativeDisplaysChanged(1):" << updated_displays[0].ToString(); |
| 602 } else { | 602 } else { |
| 603 VLOG(1) << "OnNativeDisplaysChanged(" << updated_displays.size() | 603 VLOG(1) << "OnNativeDisplaysChanged(" << updated_displays.size() |
| 604 << ") [0]=" << updated_displays[0].ToString() | 604 << ") [0]=" << updated_displays[0].ToString() |
| 605 << ", [1]=" << updated_displays[1].ToString(); | 605 << ", [1]=" << updated_displays[1].ToString(); |
| 606 } | 606 } |
| 607 | 607 |
| 608 bool internal_display_connected = false; | 608 bool internal_display_connected = false; |
| 609 num_connected_displays_ = updated_displays.size(); | 609 num_connected_displays_ = updated_displays.size(); |
| 610 mirrored_display_id_ = gfx::Display::kInvalidDisplayID; | 610 mirroring_display_id_ = gfx::Display::kInvalidDisplayID; |
| 611 mirroring_display_ = gfx::Display(); | 611 mirroring_display_ = gfx::Display(); |
| 612 DisplayInfoList new_display_info_list; | 612 DisplayInfoList new_display_info_list; |
| 613 for (DisplayInfoList::const_iterator iter = updated_displays.begin(); | 613 for (DisplayInfoList::const_iterator iter = updated_displays.begin(); |
| 614 iter != updated_displays.end(); | 614 iter != updated_displays.end(); |
| 615 ++iter) { | 615 ++iter) { |
| 616 if (!internal_display_connected) | 616 if (!internal_display_connected) |
| 617 internal_display_connected = IsInternalDisplayId(iter->id()); | 617 internal_display_connected = IsInternalDisplayId(iter->id()); |
| 618 // Mirrored monitors have the same origins. | 618 // Mirrored monitors have the same origins. |
| 619 gfx::Point origin = iter->bounds_in_native().origin(); | 619 gfx::Point origin = iter->bounds_in_native().origin(); |
| 620 if (origins.find(origin) != origins.end()) { | 620 if (origins.find(origin) != origins.end()) { |
| 621 InsertAndUpdateDisplayInfo(*iter); | 621 InsertAndUpdateDisplayInfo(*iter); |
| 622 mirrored_display_id_ = iter->id(); | 622 mirroring_display_id_ = iter->id(); |
| 623 mirroring_display_ = |
| 624 CreateDisplayFromDisplayInfoById(mirroring_display_id_); |
| 625 mirroring_display_.set_mirrored_display_id(new_display_info_list[0].id()); |
| 623 } else { | 626 } else { |
| 624 origins.insert(origin); | 627 origins.insert(origin); |
| 625 new_display_info_list.push_back(*iter); | 628 new_display_info_list.push_back(*iter); |
| 626 } | 629 } |
| 627 | 630 |
| 628 DisplayMode new_mode; | 631 DisplayMode new_mode; |
| 629 new_mode.size = iter->bounds_in_native().size(); | 632 new_mode.size = iter->bounds_in_native().size(); |
| 630 new_mode.device_scale_factor = iter->device_scale_factor(); | 633 new_mode.device_scale_factor = iter->device_scale_factor(); |
| 631 new_mode.ui_scale = iter->configured_ui_scale(); | 634 new_mode.ui_scale = iter->configured_ui_scale(); |
| 632 const std::vector<DisplayMode>& display_modes = iter->display_modes(); | 635 const std::vector<DisplayMode>& display_modes = iter->display_modes(); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 686 | 689 |
| 687 DisplayList::iterator curr_iter = displays_.begin(); | 690 DisplayList::iterator curr_iter = displays_.begin(); |
| 688 DisplayInfoList::const_iterator new_info_iter = new_display_info_list.begin(); | 691 DisplayInfoList::const_iterator new_info_iter = new_display_info_list.begin(); |
| 689 | 692 |
| 690 DisplayList new_displays; | 693 DisplayList new_displays; |
| 691 | 694 |
| 692 // Use the internal display or 1st as the mirror source, then scale | 695 // Use the internal display or 1st as the mirror source, then scale |
| 693 // the root window so that it matches the external display's | 696 // the root window so that it matches the external display's |
| 694 // resolution. This is necessary in order for scaling to work while | 697 // resolution. This is necessary in order for scaling to work while |
| 695 // mirrored. | 698 // mirrored. |
| 696 int64 mirroing_display_id = gfx::Display::kInvalidDisplayID; | 699 int64 mirrored_display_id = gfx::Display::kInvalidDisplayID; |
| 700 int64 mirroring_display_id = gfx::Display::kInvalidDisplayID; |
| 697 | 701 |
| 698 if (second_display_mode_ != EXTENDED && new_display_info_list.size() == 2) { | 702 if (second_display_mode_ != EXTENDED && new_display_info_list.size() == 2) { |
| 699 bool zero_is_source = | 703 bool zero_is_source = |
| 700 first_display_id_ == new_display_info_list[0].id() || | 704 first_display_id_ == new_display_info_list[0].id() || |
| 701 gfx::Display::InternalDisplayId() == new_display_info_list[0].id(); | 705 gfx::Display::InternalDisplayId() == new_display_info_list[0].id(); |
| 702 DCHECK_EQ(MIRRORING, second_display_mode_); | 706 DCHECK_EQ(MIRRORING, second_display_mode_); |
| 703 mirrored_display_id_ = new_display_info_list[zero_is_source ? 1 : 0].id(); | 707 mirrored_display_id = new_display_info_list[zero_is_source ? 0 : 1].id(); |
| 704 mirroing_display_id = mirrored_display_id_; | 708 mirroring_display_id = new_display_info_list[zero_is_source ? 1 : 0].id(); |
| 709 mirroring_display_id_ = mirroring_display_id; |
| 705 } | 710 } |
| 706 | 711 |
| 707 while (curr_iter != displays_.end() || | 712 while (curr_iter != displays_.end() || |
| 708 new_info_iter != new_display_info_list.end()) { | 713 new_info_iter != new_display_info_list.end()) { |
| 709 if (new_info_iter != new_display_info_list.end() && | 714 if (new_info_iter != new_display_info_list.end() && |
| 710 mirroing_display_id == new_info_iter->id()) { | 715 mirroring_display_id == new_info_iter->id()) { |
| 711 DisplayInfo info = *new_info_iter; | 716 DisplayInfo info = *new_info_iter; |
| 712 info.SetOverscanInsets(gfx::Insets()); | 717 info.SetOverscanInsets(gfx::Insets()); |
| 713 InsertAndUpdateDisplayInfo(info); | 718 InsertAndUpdateDisplayInfo(info); |
| 714 mirroring_display_ = | 719 mirroring_display_ = |
| 715 CreateDisplayFromDisplayInfoById(mirroing_display_id); | 720 CreateDisplayFromDisplayInfoById(mirroring_display_id); |
| 721 mirroring_display_.set_mirrored_display_id(mirrored_display_id); |
| 716 ++new_info_iter; | 722 ++new_info_iter; |
| 717 // Remove existing external display if it is going to be used as | 723 // Remove existing external display if it is going to be used as |
| 718 // mirroring display. | 724 // mirroring display. |
| 719 if (curr_iter != displays_.end() && | 725 if (curr_iter != displays_.end() && |
| 720 curr_iter->id() == mirroing_display_id) { | 726 curr_iter->id() == mirroring_display_id) { |
| 721 removed_displays.push_back(*curr_iter); | 727 removed_displays.push_back(*curr_iter); |
| 722 ++curr_iter; | 728 ++curr_iter; |
| 723 } | 729 } |
| 724 continue; | 730 continue; |
| 725 } | 731 } |
| 726 | 732 |
| 727 if (curr_iter == displays_.end()) { | 733 if (curr_iter == displays_.end()) { |
| 728 // more displays in new list. | 734 // more displays in new list. |
| 729 added_display_indices.push_back(new_displays.size()); | 735 added_display_indices.push_back(new_displays.size()); |
| 730 InsertAndUpdateDisplayInfo(*new_info_iter); | 736 InsertAndUpdateDisplayInfo(*new_info_iter); |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 926 DisplayLayout layout = layout_store_->GetRegisteredDisplayLayout( | 932 DisplayLayout layout = layout_store_->GetRegisteredDisplayLayout( |
| 927 GetCurrentDisplayIdPair()); | 933 GetCurrentDisplayIdPair()); |
| 928 return GetDisplayForId(layout.primary_id); | 934 return GetDisplayForId(layout.primary_id); |
| 929 } | 935 } |
| 930 | 936 |
| 931 size_t DisplayManager::GetNumDisplays() const { | 937 size_t DisplayManager::GetNumDisplays() const { |
| 932 return displays_.size(); | 938 return displays_.size(); |
| 933 } | 939 } |
| 934 | 940 |
| 935 bool DisplayManager::IsMirrored() const { | 941 bool DisplayManager::IsMirrored() const { |
| 936 return mirrored_display_id_ != gfx::Display::kInvalidDisplayID; | 942 return mirroring_display_id_ != gfx::Display::kInvalidDisplayID; |
| 937 } | 943 } |
| 938 | 944 |
| 939 const DisplayInfo& DisplayManager::GetDisplayInfo(int64 display_id) const { | 945 const DisplayInfo& DisplayManager::GetDisplayInfo(int64 display_id) const { |
| 940 DCHECK_NE(gfx::Display::kInvalidDisplayID, display_id); | 946 DCHECK_NE(gfx::Display::kInvalidDisplayID, display_id); |
| 941 | 947 |
| 942 std::map<int64, DisplayInfo>::const_iterator iter = | 948 std::map<int64, DisplayInfo>::const_iterator iter = |
| 943 display_info_.find(display_id); | 949 display_info_.find(display_id); |
| 944 CHECK(iter != display_info_.end()) << display_id; | 950 CHECK(iter != display_info_.end()) << display_id; |
| 945 return iter->second; | 951 return iter->second; |
| 946 } | 952 } |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1003 new_display_info_list.push_back(first_display); | 1009 new_display_info_list.push_back(first_display); |
| 1004 // Add if there is only one display connected. | 1010 // Add if there is only one display connected. |
| 1005 if (num_connected_displays() == 1) { | 1011 if (num_connected_displays() == 1) { |
| 1006 // Layout the 2nd display below the primary as with the real device. | 1012 // Layout the 2nd display below the primary as with the real device. |
| 1007 gfx::Rect host_bounds = first_display.bounds_in_native(); | 1013 gfx::Rect host_bounds = first_display.bounds_in_native(); |
| 1008 new_display_info_list.push_back(DisplayInfo::CreateFromSpec( | 1014 new_display_info_list.push_back(DisplayInfo::CreateFromSpec( |
| 1009 base::StringPrintf( | 1015 base::StringPrintf( |
| 1010 "%d+%d-500x400", host_bounds.x(), host_bounds.bottom()))); | 1016 "%d+%d-500x400", host_bounds.x(), host_bounds.bottom()))); |
| 1011 } | 1017 } |
| 1012 num_connected_displays_ = new_display_info_list.size(); | 1018 num_connected_displays_ = new_display_info_list.size(); |
| 1013 mirrored_display_id_ = gfx::Display::kInvalidDisplayID; | 1019 mirroring_display_id_ = gfx::Display::kInvalidDisplayID; |
| 1014 mirroring_display_ = gfx::Display(); | 1020 mirroring_display_ = gfx::Display(); |
| 1015 UpdateDisplays(new_display_info_list); | 1021 UpdateDisplays(new_display_info_list); |
| 1016 } | 1022 } |
| 1017 | 1023 |
| 1018 void DisplayManager::ToggleDisplayScaleFactor() { | 1024 void DisplayManager::ToggleDisplayScaleFactor() { |
| 1019 DCHECK(!displays_.empty()); | 1025 DCHECK(!displays_.empty()); |
| 1020 std::vector<DisplayInfo> new_display_info_list; | 1026 std::vector<DisplayInfo> new_display_info_list; |
| 1021 for (DisplayList::const_iterator iter = displays_.begin(); | 1027 for (DisplayList::const_iterator iter = displays_.begin(); |
| 1022 iter != displays_.end(); ++iter) { | 1028 iter != displays_.end(); ++iter) { |
| 1023 DisplayInfo display_info = GetDisplayInfo(iter->id()); | 1029 DisplayInfo display_info = GetDisplayInfo(iter->id()); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1034 SetSecondDisplayMode(enabled ? MIRRORING : EXTENDED); | 1040 SetSecondDisplayMode(enabled ? MIRRORING : EXTENDED); |
| 1035 } | 1041 } |
| 1036 | 1042 |
| 1037 bool DisplayManager::SoftwareMirroringEnabled() const { | 1043 bool DisplayManager::SoftwareMirroringEnabled() const { |
| 1038 return software_mirroring_enabled(); | 1044 return software_mirroring_enabled(); |
| 1039 } | 1045 } |
| 1040 #endif | 1046 #endif |
| 1041 | 1047 |
| 1042 void DisplayManager::SetSecondDisplayMode(SecondDisplayMode mode) { | 1048 void DisplayManager::SetSecondDisplayMode(SecondDisplayMode mode) { |
| 1043 second_display_mode_ = mode; | 1049 second_display_mode_ = mode; |
| 1044 mirrored_display_id_ = gfx::Display::kInvalidDisplayID; | 1050 mirroring_display_id_ = gfx::Display::kInvalidDisplayID; |
| 1045 mirroring_display_ = gfx::Display(); | 1051 mirroring_display_ = gfx::Display(); |
| 1046 } | 1052 } |
| 1047 | 1053 |
| 1048 bool DisplayManager::UpdateDisplayBounds(int64 display_id, | 1054 bool DisplayManager::UpdateDisplayBounds(int64 display_id, |
| 1049 const gfx::Rect& new_bounds) { | 1055 const gfx::Rect& new_bounds) { |
| 1050 if (change_display_upon_host_resize_) { | 1056 if (change_display_upon_host_resize_) { |
| 1051 display_info_[display_id].SetBounds(new_bounds); | 1057 display_info_[display_id].SetBounds(new_bounds); |
| 1052 // Don't notify observers if the mirrored window has changed. | 1058 // Don't notify observers if the mirrored window has changed. |
| 1053 if (software_mirroring_enabled() && mirrored_display_id_ == display_id) | 1059 if (software_mirroring_enabled() && mirroring_display_id_ == display_id) |
| 1054 return false; | 1060 return false; |
| 1055 gfx::Display* display = FindDisplayForId(display_id); | 1061 gfx::Display* display = FindDisplayForId(display_id); |
| 1056 display->SetSize(display_info_[display_id].size_in_pixel()); | 1062 display->SetSize(display_info_[display_id].size_in_pixel()); |
| 1057 screen_ash_->NotifyMetricsChanged( | 1063 screen_ash_->NotifyMetricsChanged( |
| 1058 *display, gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS); | 1064 *display, gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS); |
| 1059 return true; | 1065 return true; |
| 1060 } | 1066 } |
| 1061 return false; | 1067 return false; |
| 1062 } | 1068 } |
| 1063 | 1069 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1088 DisplayInfo* info = &display_info_[gfx::Display::InternalDisplayId()]; | 1094 DisplayInfo* info = &display_info_[gfx::Display::InternalDisplayId()]; |
| 1089 SetInternalDisplayModeList(info); | 1095 SetInternalDisplayModeList(info); |
| 1090 } | 1096 } |
| 1091 | 1097 |
| 1092 gfx::Display* DisplayManager::FindDisplayForId(int64 id) { | 1098 gfx::Display* DisplayManager::FindDisplayForId(int64 id) { |
| 1093 for (DisplayList::iterator iter = displays_.begin(); | 1099 for (DisplayList::iterator iter = displays_.begin(); |
| 1094 iter != displays_.end(); ++iter) { | 1100 iter != displays_.end(); ++iter) { |
| 1095 if ((*iter).id() == id) | 1101 if ((*iter).id() == id) |
| 1096 return &(*iter); | 1102 return &(*iter); |
| 1097 } | 1103 } |
| 1104 |
| 1105 if (id == mirroring_display_id_) |
| 1106 return &mirroring_display_; |
| 1107 |
| 1098 DLOG(WARNING) << "Could not find display:" << id; | 1108 DLOG(WARNING) << "Could not find display:" << id; |
| 1099 return NULL; | 1109 return NULL; |
| 1100 } | 1110 } |
| 1101 | 1111 |
| 1102 void DisplayManager::AddMirrorDisplayInfoIfAny( | 1112 void DisplayManager::AddMirrorDisplayInfoIfAny( |
| 1103 std::vector<DisplayInfo>* display_info_list) { | 1113 std::vector<DisplayInfo>* display_info_list) { |
| 1104 if (software_mirroring_enabled() && IsMirrored()) | 1114 if (software_mirroring_enabled() && IsMirrored()) |
| 1105 display_info_list->push_back(GetDisplayInfo(mirrored_display_id_)); | 1115 display_info_list->push_back(GetDisplayInfo(mirroring_display_id_)); |
| 1106 } | 1116 } |
| 1107 | 1117 |
| 1108 void DisplayManager::InsertAndUpdateDisplayInfo(const DisplayInfo& new_info) { | 1118 void DisplayManager::InsertAndUpdateDisplayInfo(const DisplayInfo& new_info) { |
| 1109 std::map<int64, DisplayInfo>::iterator info = | 1119 std::map<int64, DisplayInfo>::iterator info = |
| 1110 display_info_.find(new_info.id()); | 1120 display_info_.find(new_info.id()); |
| 1111 if (info != display_info_.end()) { | 1121 if (info != display_info_.end()) { |
| 1112 info->second.Copy(new_info); | 1122 info->second.Copy(new_info); |
| 1113 } else { | 1123 } else { |
| 1114 display_info_[new_info.id()] = new_info; | 1124 display_info_[new_info.id()] = new_info; |
| 1115 display_info_[new_info.id()].set_native(false); | 1125 display_info_[new_info.id()].set_native(false); |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1255 new_secondary_origin.Offset(-secondary_bounds.width(), offset); | 1265 new_secondary_origin.Offset(-secondary_bounds.width(), offset); |
| 1256 break; | 1266 break; |
| 1257 } | 1267 } |
| 1258 gfx::Insets insets = secondary_display->GetWorkAreaInsets(); | 1268 gfx::Insets insets = secondary_display->GetWorkAreaInsets(); |
| 1259 secondary_display->set_bounds( | 1269 secondary_display->set_bounds( |
| 1260 gfx::Rect(new_secondary_origin, secondary_bounds.size())); | 1270 gfx::Rect(new_secondary_origin, secondary_bounds.size())); |
| 1261 secondary_display->UpdateWorkAreaFromInsets(insets); | 1271 secondary_display->UpdateWorkAreaFromInsets(insets); |
| 1262 } | 1272 } |
| 1263 | 1273 |
| 1264 } // namespace ash | 1274 } // namespace ash |
| OLD | NEW |