| // Copyright (c) 2012 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_prefs.h" |
| |
| #include <stddef.h> |
| |
| #include <string> |
| |
| #include "ash/constants/ash_switches.h" |
| #include "ash/public/cpp/ash_pref_names.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "base/command_line.h" |
| #include "base/containers/contains.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/system/sys_info.h" |
| #include "base/values.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| #include "ui/display/display_features.h" |
| #include "ui/display/display_switches.h" |
| #include "ui/display/manager/display_layout_store.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/display/manager/display_manager_utilities.h" |
| #include "ui/display/manager/json_converter.h" |
| #include "ui/display/types/display_constants.h" |
| #include "ui/gfx/geometry/insets.h" |
| #include "url/url_canon.h" |
| #include "url/url_util.h" |
| |
| using chromeos::DisplayPowerState; |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kInsetsTopKey[] = "insets_top"; |
| constexpr char kInsetsLeftKey[] = "insets_left"; |
| constexpr char kInsetsBottomKey[] = "insets_bottom"; |
| constexpr char kInsetsRightKey[] = "insets_right"; |
| |
| constexpr char kTouchCalibrationWidth[] = "touch_calibration_width"; |
| constexpr char kTouchCalibrationHeight[] = "touch_calibration_height"; |
| constexpr char kTouchCalibrationPointPairs[] = "touch_calibration_point_pairs"; |
| |
| constexpr char kTouchAssociationTimestamp[] = "touch_association_timestamp"; |
| constexpr char kTouchAssociationCalibrationData[] = |
| "touch_association_calibration_data"; |
| |
| constexpr char kTouchDeviceIdentifier[] = "touch_device_identifer"; |
| constexpr char kPortAssociationDisplayId[] = "port_association_display_id"; |
| |
| constexpr char kMirroringSourceId[] = "mirroring_source_id"; |
| constexpr char kMirroringDestinationIds[] = "mirroring_destination_ids"; |
| |
| constexpr char kDisplayZoom[] = "display_zoom_factor"; |
| |
| constexpr char kDisplayPowerAllOn[] = "all_on"; |
| constexpr char kDisplayPowerInternalOffExternalOn[] = |
| "internal_off_external_on"; |
| constexpr char kDisplayPowerInternalOnExternalOff[] = |
| "internal_on_external_off"; |
| |
| // This kind of boilerplates should be done by base::JSONValueConverter but it |
| // doesn't support classes like gfx::Insets for now. |
| // TODO(mukai): fix base::JSONValueConverter and use it here. |
| bool ValueToInsets(const base::DictionaryValue& value, gfx::Insets* insets) { |
| DCHECK(insets); |
| int top = 0; |
| int left = 0; |
| int bottom = 0; |
| int right = 0; |
| if (value.GetInteger(kInsetsTopKey, &top) && |
| value.GetInteger(kInsetsLeftKey, &left) && |
| value.GetInteger(kInsetsBottomKey, &bottom) && |
| value.GetInteger(kInsetsRightKey, &right)) { |
| insets->Set(top, left, bottom, right); |
| return true; |
| } |
| return false; |
| } |
| |
| void InsetsToValue(const gfx::Insets& insets, base::DictionaryValue* value) { |
| DCHECK(value); |
| value->SetInteger(kInsetsTopKey, insets.top()); |
| value->SetInteger(kInsetsLeftKey, insets.left()); |
| value->SetInteger(kInsetsBottomKey, insets.bottom()); |
| value->SetInteger(kInsetsRightKey, insets.right()); |
| } |
| |
| // Unmarshalls the string containing CalibrationPointPairQuad and populates |
| // |point_pair_quad| with the unmarshalled data. |
| bool ParseTouchCalibrationStringValue( |
| const std::string& str, |
| display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad) { |
| DCHECK(point_pair_quad); |
| int x = 0, y = 0; |
| std::vector<std::string> parts = base::SplitString( |
| str, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| size_t total = point_pair_quad->size(); |
| gfx::Point display_point, touch_point; |
| for (std::size_t row = 0; row < total; row++) { |
| if (!base::StringToInt(parts[row * total], &x) || |
| !base::StringToInt(parts[row * total + 1], &y)) { |
| return false; |
| } |
| display_point.SetPoint(x, y); |
| |
| if (!base::StringToInt(parts[row * total + 2], &x) || |
| !base::StringToInt(parts[row * total + 3], &y)) { |
| return false; |
| } |
| touch_point.SetPoint(x, y); |
| |
| (*point_pair_quad)[row] = std::make_pair(display_point, touch_point); |
| } |
| return true; |
| } |
| |
| // Retrieves touch calibration associated data from the dictionary and stores |
| // it in an instance of TouchCalibrationData struct. |
| bool ValueToTouchData(const base::DictionaryValue& value, |
| display::TouchCalibrationData* touch_calibration_data) { |
| display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad = |
| &(touch_calibration_data->point_pairs); |
| |
| std::string str; |
| if (!value.GetString(kTouchCalibrationPointPairs, &str)) |
| return false; |
| |
| if (!ParseTouchCalibrationStringValue(str, point_pair_quad)) |
| return false; |
| |
| int width, height; |
| if (!value.GetInteger(kTouchCalibrationWidth, &width) || |
| !value.GetInteger(kTouchCalibrationHeight, &height)) { |
| return false; |
| } |
| touch_calibration_data->bounds = gfx::Size(width, height); |
| return true; |
| } |
| |
| // Stores the touch calibration data into the dictionary. |
| void TouchDataToValue( |
| const display::TouchCalibrationData& touch_calibration_data, |
| base::DictionaryValue* value) { |
| DCHECK(value); |
| std::string str; |
| for (std::size_t row = 0; row < touch_calibration_data.point_pairs.size(); |
| row++) { |
| str += base::NumberToString( |
| touch_calibration_data.point_pairs[row].first.x()) + |
| " "; |
| str += base::NumberToString( |
| touch_calibration_data.point_pairs[row].first.y()) + |
| " "; |
| str += base::NumberToString( |
| touch_calibration_data.point_pairs[row].second.x()) + |
| " "; |
| str += base::NumberToString( |
| touch_calibration_data.point_pairs[row].second.y()); |
| if (row != touch_calibration_data.point_pairs.size() - 1) |
| str += " "; |
| } |
| value->SetString(kTouchCalibrationPointPairs, str); |
| value->SetInteger(kTouchCalibrationWidth, |
| touch_calibration_data.bounds.width()); |
| value->SetInteger(kTouchCalibrationHeight, |
| touch_calibration_data.bounds.height()); |
| } |
| |
| display::DisplayManager* GetDisplayManager() { |
| return Shell::Get()->display_manager(); |
| } |
| |
| // Returns true if the current user can write display preferences to |
| // Local State. |
| bool UserCanSaveDisplayPreference() { |
| SessionControllerImpl* controller = Shell::Get()->session_controller(); |
| auto user_type = controller->GetUserType(); |
| if (!user_type) |
| return false; |
| |
| return *user_type == user_manager::USER_TYPE_REGULAR || |
| *user_type == user_manager::USER_TYPE_CHILD || |
| *user_type == user_manager::USER_TYPE_KIOSK_APP || |
| (*user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT && |
| Shell::Get()->local_state()->GetBoolean( |
| prefs::kAllowMGSToStoreDisplayProperties)); |
| } |
| |
| void LoadDisplayLayouts(PrefService* local_state) { |
| display::DisplayLayoutStore* layout_store = |
| GetDisplayManager()->layout_store(); |
| |
| const base::Value* layouts = local_state->Get(prefs::kSecondaryDisplays); |
| for (const auto& it : layouts->DictItems()) { |
| std::unique_ptr<display::DisplayLayout> layout(new display::DisplayLayout); |
| if (!display::JsonToDisplayLayout(it.second, layout.get())) { |
| LOG(WARNING) << "Invalid preference value for " << it.first; |
| continue; |
| } |
| |
| if (it.first.find(",") != std::string::npos) { |
| std::vector<std::string> ids_str = base::SplitString( |
| it.first, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| std::vector<int64_t> ids; |
| for (std::string id_str : ids_str) { |
| int64_t id; |
| if (!base::StringToInt64(id_str, &id)) |
| continue; |
| ids.push_back(id); |
| } |
| display::DisplayIdList list = |
| display::GenerateDisplayIdList(ids.begin(), ids.end()); |
| layout_store->RegisterLayoutForDisplayIdList(list, std::move(layout)); |
| } |
| } |
| } |
| |
| void LoadDisplayProperties(PrefService* local_state) { |
| const base::Value* properties = local_state->Get(prefs::kDisplayProperties); |
| for (const auto& it : properties->DictItems()) { |
| const base::DictionaryValue* dict_value = nullptr; |
| if (!it.second.GetAsDictionary(&dict_value) || dict_value == nullptr) |
| continue; |
| int64_t id = display::kInvalidDisplayId; |
| if (!base::StringToInt64(it.first, &id) || |
| id == display::kInvalidDisplayId) { |
| continue; |
| } |
| display::Display::Rotation rotation = display::Display::ROTATE_0; |
| const gfx::Insets* insets_to_set = nullptr; |
| |
| int rotation_value = 0; |
| if (dict_value->GetInteger("rotation", &rotation_value)) { |
| rotation = static_cast<display::Display::Rotation>(rotation_value); |
| } |
| |
| int width = 0, height = 0; |
| dict_value->GetInteger("width", &width); |
| dict_value->GetInteger("height", &height); |
| gfx::Size resolution_in_pixels(width, height); |
| |
| float device_scale_factor = 1.0; |
| int dsf_value = 0; |
| if (dict_value->GetInteger("device-scale-factor", &dsf_value)) |
| device_scale_factor = static_cast<float>(dsf_value) / 1000.0f; |
| |
| // Default refresh rate is 60 Hz, until |
| // DisplayManager::OnNativeDisplaysChanged() updates us with the actual |
| // display info. |
| double refresh_rate = 60.0; |
| bool is_interlaced = false; |
| if (display::features::IsListAllDisplayModesEnabled()) { |
| dict_value->GetDouble("refresh-rate", &refresh_rate); |
| dict_value->GetBoolean("interlaced", &is_interlaced); |
| } |
| |
| gfx::Insets insets; |
| if (ValueToInsets(*dict_value, &insets)) |
| insets_to_set = &insets; |
| |
| double display_zoom = 1.0; |
| dict_value->GetDouble(kDisplayZoom, &display_zoom); |
| |
| GetDisplayManager()->RegisterDisplayProperty( |
| id, rotation, insets_to_set, resolution_in_pixels, device_scale_factor, |
| display_zoom, refresh_rate, is_interlaced); |
| } |
| } |
| |
| void LoadDisplayRotationState(PrefService* local_state) { |
| const base::Value* properties = local_state->Get(prefs::kDisplayRotationLock); |
| DCHECK(properties->is_dict()); |
| const base::Value* rotation_lock = |
| properties->FindKeyOfType("lock", base::Value::Type::BOOLEAN); |
| if (!rotation_lock) |
| return; |
| |
| const base::Value* rotation = |
| properties->FindKeyOfType("orientation", base::Value::Type::INTEGER); |
| if (!rotation) |
| return; |
| |
| GetDisplayManager()->RegisterDisplayRotationProperties( |
| rotation_lock->GetBool(), |
| static_cast<display::Display::Rotation>(rotation->GetInt())); |
| } |
| |
| void LoadDisplayTouchAssociations(PrefService* local_state) { |
| const base::Value* properties = |
| local_state->Get(prefs::kDisplayTouchAssociations); |
| DCHECK(properties->is_dict()); |
| |
| display::TouchDeviceManager::TouchAssociationMap touch_associations; |
| for (const auto& item : properties->DictItems()) { |
| uint32_t identifier_raw; |
| if (!base::StringToUint(item.first, &identifier_raw)) |
| continue; |
| display::TouchDeviceIdentifier identifier(identifier_raw); |
| touch_associations.emplace( |
| identifier, display::TouchDeviceManager::AssociationInfoMap()); |
| if (!item.second.is_dict()) |
| continue; |
| for (const auto& association_info_item : item.second.DictItems()) { |
| display::TouchDeviceManager::TouchAssociationInfo info; |
| int64_t display_id; |
| if (!base::StringToInt64(association_info_item.first, &display_id)) |
| continue; |
| auto* value = |
| association_info_item.second.FindKey(kTouchAssociationTimestamp); |
| if (!value->is_double()) |
| continue; |
| info.timestamp = base::Time().FromDoubleT(value->GetDouble()); |
| |
| value = association_info_item.second.FindKey( |
| kTouchAssociationCalibrationData); |
| if (!value->is_dict()) |
| continue; |
| const base::DictionaryValue* calibration_data_dict = nullptr; |
| if (!value->GetAsDictionary(&calibration_data_dict)) |
| continue; |
| ValueToTouchData(*calibration_data_dict, &info.calibration_data); |
| touch_associations.at(identifier).emplace(display_id, info); |
| } |
| } |
| |
| // Retrieve all the legacy format identifiers. This should be removed after |
| // a couple of milestones when everything is stable. |
| const display::TouchDeviceIdentifier& fallback_identifier = |
| display::TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier(); |
| properties = local_state->Get(prefs::kDisplayProperties); |
| for (const auto& it : properties->DictItems()) { |
| const base::DictionaryValue* dict_value = nullptr; |
| if (!it.second.GetAsDictionary(&dict_value) || dict_value == nullptr) |
| continue; |
| int64_t id = display::kInvalidDisplayId; |
| if (!base::StringToInt64(it.first, &id) || |
| id == display::kInvalidDisplayId) { |
| continue; |
| } |
| display::TouchCalibrationData calibration_data; |
| display::TouchCalibrationData* calibration_data_to_set = nullptr; |
| if (ValueToTouchData(*dict_value, &calibration_data)) |
| calibration_data_to_set = &calibration_data; |
| |
| if (calibration_data_to_set) { |
| if (!base::Contains(touch_associations, fallback_identifier)) { |
| touch_associations.emplace( |
| fallback_identifier, |
| display::TouchDeviceManager::AssociationInfoMap()); |
| } |
| display::TouchDeviceManager::TouchAssociationInfo info; |
| info.calibration_data = *calibration_data_to_set; |
| touch_associations.at(fallback_identifier).emplace(id, info); |
| } |
| } |
| |
| // Retrieve port association information. |
| properties = local_state->Get(prefs::kDisplayTouchPortAssociations); |
| display::TouchDeviceManager::PortAssociationMap port_associations; |
| for (const auto& item : properties->DictItems()) { |
| // Retrieve the secondary id that identifies the port. |
| uint32_t secondary_id_raw; |
| if (!base::StringToUint(item.first, &secondary_id_raw)) |
| continue; |
| |
| if (!item.second.is_dict()) |
| continue; |
| |
| // Retrieve the touch device identifier that identifies the touch device. |
| auto* value = item.second.FindKey(kTouchDeviceIdentifier); |
| if (!value->is_string()) |
| continue; |
| uint32_t identifier_raw; |
| if (!base::StringToUint(value->GetString(), &identifier_raw)) |
| continue; |
| |
| // Retrieve the display that the touch device identified by |identifier_raw| |
| // was associated with. |
| value = item.second.FindKey(kPortAssociationDisplayId); |
| if (!value->is_string()) |
| continue; |
| int64_t display_id; |
| if (!base::StringToInt64(value->GetString(), &display_id)) |
| continue; |
| |
| port_associations.emplace( |
| std::piecewise_construct, |
| std::forward_as_tuple(identifier_raw, secondary_id_raw), |
| std::forward_as_tuple(display_id)); |
| } |
| |
| GetDisplayManager()->touch_device_manager()->RegisterTouchAssociations( |
| touch_associations, port_associations); |
| } |
| |
| // Loads mirror info for each external display, the info will later be used to |
| // restore mirror mode. |
| void LoadExternalDisplayMirrorInfo(PrefService* local_state) { |
| const base::Value* pref_data = |
| local_state->Get(prefs::kExternalDisplayMirrorInfo); |
| std::set<int64_t> external_display_mirror_info; |
| for (const auto& it : pref_data->GetList()) { |
| std::string display_id_str; |
| if (!it.GetAsString(&display_id_str)) |
| continue; |
| |
| int64_t display_id; |
| if (!base::StringToInt64(display_id_str, &display_id)) |
| continue; |
| |
| external_display_mirror_info.emplace(display_id); |
| } |
| GetDisplayManager()->set_external_display_mirror_info( |
| external_display_mirror_info); |
| } |
| |
| // Loads mixed mirror mode parameters which will later be used to restore mixed |
| // mirror mode. Return false if the parameters fail to be loaded. |
| void LoadDisplayMixedMirrorModeParams(PrefService* local_state) { |
| const base::Value* pref_data = |
| local_state->Get(prefs::kDisplayMixedMirrorModeParams); |
| |
| // This function is called once for system (re)start, so the parameters should |
| // be empty. |
| DCHECK(!GetDisplayManager()->mixed_mirror_mode_params()); |
| |
| auto* mirroring_source_id_value = pref_data->FindKey(kMirroringSourceId); |
| if (!mirroring_source_id_value) |
| return; |
| |
| DCHECK(mirroring_source_id_value->is_string()); |
| int64_t mirroring_source_id; |
| if (!base::StringToInt64(mirroring_source_id_value->GetString(), |
| &mirroring_source_id)) { |
| return; |
| } |
| |
| auto* mirroring_destination_ids_value = |
| pref_data->FindKey(kMirroringDestinationIds); |
| if (!mirroring_destination_ids_value) |
| return; |
| |
| DCHECK(mirroring_destination_ids_value->is_list()); |
| display::DisplayIdList mirroring_destination_ids; |
| for (const auto& entry : mirroring_destination_ids_value->GetList()) { |
| DCHECK(entry.is_string()); |
| int64_t id; |
| if (!base::StringToInt64(entry.GetString(), &id)) |
| return; |
| mirroring_destination_ids.emplace_back(id); |
| } |
| |
| GetDisplayManager()->set_mixed_mirror_mode_params( |
| absl::optional<display::MixedMirrorModeParams>( |
| absl::in_place, mirroring_source_id, mirroring_destination_ids)); |
| } |
| |
| void StoreDisplayLayoutPref(PrefService* pref_service, |
| const display::DisplayIdList& list, |
| const display::DisplayLayout& display_layout) { |
| DCHECK(display::DisplayLayout::Validate(list, display_layout)); |
| std::string name = display::DisplayIdListToString(list); |
| |
| DictionaryPrefUpdate update(pref_service, prefs::kSecondaryDisplays); |
| base::DictionaryValue* pref_data = update.Get(); |
| base::Value layout_value(base::Value::Type::DICTIONARY); |
| if (pref_data->HasKey(name)) { |
| base::Value* value = nullptr; |
| if (pref_data->Get(name, &value) && value != nullptr) { |
| layout_value = value->Clone(); |
| } |
| } |
| if (display::DisplayLayoutToJson(display_layout, &layout_value)) |
| pref_data->SetPath(name, std::move(layout_value)); |
| } |
| |
| void StoreCurrentDisplayLayoutPrefs(PrefService* pref_service) { |
| display::DisplayManager* display_manager = GetDisplayManager(); |
| if (!UserCanSaveDisplayPreference() || |
| display_manager->num_connected_displays() < 2) { |
| return; |
| } |
| |
| display::DisplayIdList list = display_manager->GetCurrentDisplayIdList(); |
| const display::DisplayLayout& display_layout = |
| display_manager->layout_store()->GetRegisteredDisplayLayout(list); |
| |
| if (!display::DisplayLayout::Validate(list, display_layout)) { |
| // We should never apply an invalid layout, if we do, it persists and the |
| // user has no way of fixing it except by deleting the local state. |
| LOG(ERROR) << "Attempting to store an invalid display layout in the local" |
| << " state. Skipping."; |
| return; |
| } |
| |
| StoreDisplayLayoutPref(pref_service, list, display_layout); |
| } |
| |
| void StoreCurrentDisplayProperties(PrefService* pref_service) { |
| display::DisplayManager* display_manager = GetDisplayManager(); |
| |
| DictionaryPrefUpdate update(pref_service, prefs::kDisplayProperties); |
| base::DictionaryValue* pref_data = update.Get(); |
| |
| // Pre-process data related to legacy touch calibration to opitmize lookup. |
| const display::TouchDeviceIdentifier& fallback_identifier = |
| display::TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier(); |
| display::TouchDeviceManager::AssociationInfoMap legacy_data_map; |
| if (base::Contains( |
| display_manager->touch_device_manager()->touch_associations(), |
| fallback_identifier)) { |
| legacy_data_map = |
| display_manager->touch_device_manager()->touch_associations().at( |
| fallback_identifier); |
| } |
| |
| size_t num = display_manager->GetNumDisplays(); |
| for (size_t i = 0; i < num; ++i) { |
| const display::Display& display = display_manager->GetDisplayAt(i); |
| int64_t id = display.id(); |
| display::ManagedDisplayInfo info = display_manager->GetDisplayInfo(id); |
| |
| base::DictionaryValue property_value; |
| // Don't save the display preference in unified mode because its |
| // size and modes can change depending on the combination of displays. |
| if (display_manager->IsInUnifiedMode()) |
| continue; |
| property_value.SetInteger("rotation", |
| static_cast<int>(info.GetRotation( |
| display::Display::RotationSource::USER))); |
| |
| display::ManagedDisplayMode mode; |
| if (!display.IsInternal() && |
| display_manager->GetSelectedModeForDisplayId(id, &mode) && |
| !mode.native()) { |
| property_value.SetInteger("width", mode.size().width()); |
| property_value.SetInteger("height", mode.size().height()); |
| property_value.SetInteger( |
| "device-scale-factor", |
| static_cast<int>(mode.device_scale_factor() * 1000)); |
| |
| if (display::features::IsListAllDisplayModesEnabled()) { |
| property_value.SetBoolean("interlaced", mode.is_interlaced()); |
| property_value.SetDouble("refresh-rate", mode.refresh_rate()); |
| } |
| } |
| if (!info.overscan_insets_in_dip().IsEmpty()) |
| InsetsToValue(info.overscan_insets_in_dip(), &property_value); |
| |
| // Store the legacy format touch calibration data. This can be removed after |
| // a couple of milestones when every device has migrated to the new format. |
| if (legacy_data_map.size() && base::Contains(legacy_data_map, id)) { |
| TouchDataToValue(legacy_data_map.at(id).calibration_data, |
| &property_value); |
| } |
| |
| property_value.SetDouble(kDisplayZoom, info.zoom_factor()); |
| |
| pref_data->SetKey(base::NumberToString(id), std::move(property_value)); |
| } |
| } |
| |
| bool GetDisplayPowerStateFromString(const std::string& state_string, |
| chromeos::DisplayPowerState* power_state) { |
| if (state_string == kDisplayPowerAllOn) { |
| *power_state = chromeos::DISPLAY_POWER_ALL_ON; |
| } else if (state_string == kDisplayPowerInternalOffExternalOn) { |
| *power_state = chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON; |
| } else if (state_string == kDisplayPowerInternalOnExternalOff) { |
| *power_state = chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF; |
| } else { |
| // Don't restore ALL_OFF state. http://crbug.com/318456. |
| return false; |
| } |
| return true; |
| } |
| |
| void StoreDisplayPowerState(PrefService* pref_service, |
| DisplayPowerState power_state) { |
| const char* state_string = nullptr; |
| switch (power_state) { |
| case chromeos::DISPLAY_POWER_ALL_ON: |
| state_string = kDisplayPowerAllOn; |
| break; |
| case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON: |
| state_string = kDisplayPowerInternalOffExternalOn; |
| break; |
| case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF: |
| state_string = kDisplayPowerInternalOnExternalOff; |
| break; |
| case chromeos::DISPLAY_POWER_ALL_OFF: |
| // Don't store ALL_OFF state. http://crbug.com/318456. |
| break; |
| } |
| if (state_string) |
| pref_service->Set(prefs::kDisplayPowerState, base::Value(state_string)); |
| } |
| |
| void StoreCurrentDisplayPowerState(PrefService* pref_service) { |
| StoreDisplayPowerState( |
| pref_service, |
| Shell::Get()->display_configurator()->GetRequestedPowerState()); |
| } |
| |
| void StoreDisplayRotationPrefs(PrefService* pref_service, |
| display::Display::Rotation rotation, |
| bool rotation_lock) { |
| DictionaryPrefUpdate update(pref_service, prefs::kDisplayRotationLock); |
| base::DictionaryValue* pref_data = update.Get(); |
| pref_data->SetBoolean("lock", rotation_lock); |
| pref_data->SetInteger("orientation", static_cast<int>(rotation)); |
| } |
| |
| void StoreCurrentDisplayRotationLockPrefs(PrefService* pref_service) { |
| if (!display::Display::HasInternalDisplay()) |
| return; |
| display::Display::Rotation rotation = |
| GetDisplayManager() |
| ->GetDisplayInfo(display::Display::InternalDisplayId()) |
| .GetRotation(display::Display::RotationSource::ACCELEROMETER); |
| bool rotation_lock = Shell::Get() |
| ->display_manager() |
| ->registered_internal_display_rotation_lock(); |
| StoreDisplayRotationPrefs(pref_service, rotation, rotation_lock); |
| } |
| |
| void StoreDisplayTouchAssociations(PrefService* pref_service) { |
| display::TouchDeviceManager* touch_device_manager = |
| GetDisplayManager()->touch_device_manager(); |
| |
| DictionaryPrefUpdate update(pref_service, prefs::kDisplayTouchAssociations); |
| base::DictionaryValue* pref_data = update.Get(); |
| pref_data->Clear(); |
| |
| const display::TouchDeviceManager::TouchAssociationMap& touch_associations = |
| touch_device_manager->touch_associations(); |
| |
| for (const auto& association : touch_associations) { |
| base::DictionaryValue association_info_map_value; |
| for (const auto& association_info : association.second) { |
| // Iteration for each pair of <Display ID, TouchAssociationInfo>. |
| std::unique_ptr<base::DictionaryValue> association_info_value( |
| new base::DictionaryValue()); |
| |
| // Parsing each member of TouchAssociationInfo and storing them in |
| // |association_info_value|. |
| |
| // Serialie timestamp. |
| association_info_value->SetKey( |
| kTouchAssociationTimestamp, |
| base::Value(association_info.second.timestamp.ToDoubleT())); |
| |
| // Serialize TouchCalibrationData. |
| base::DictionaryValue calibration_data_value; |
| TouchDataToValue(association_info.second.calibration_data, |
| &calibration_data_value); |
| association_info_value->SetKey(kTouchAssociationCalibrationData, |
| calibration_data_value.Clone()); |
| |
| // Move the searialzed TouchAssociationInfo stored in |
| // |association_info_value| to |association_info_map_value| against the |
| // display id as key. This is a 1 to 1 mapping of a single entry from |
| // AssociationInfoMap to its serialized form. |
| association_info_map_value.SetKey( |
| base::NumberToString(association_info.first), |
| association_info_value->Clone()); |
| } |
| if (association_info_map_value.DictEmpty()) |
| continue; |
| |
| // Move the already serialized entry of AssociationInfoMap from |
| // |association_info_map_value| to |pref_data| against the |
| // TouchDeviceIdentifier as key. This is a 1 to 1 mapping of a single entry |
| // from TouchAssociationMap to its serialized form. |
| pref_data->SetKey(association.first.ToString(), |
| association_info_map_value.Clone()); |
| } |
| |
| // Store the port mappings. What display a touch device connected to a |
| // particular port is associated with. |
| DictionaryPrefUpdate update_port(pref_service, |
| prefs::kDisplayTouchPortAssociations); |
| pref_data = update_port.Get(); |
| update_port->Clear(); |
| |
| const display::TouchDeviceManager::PortAssociationMap& port_associations = |
| touch_device_manager->port_associations(); |
| |
| // For each port identified by the secondary id of TouchDeviceIdentifier, |
| // we store the touch device and the display associated with it. |
| for (const auto& association : port_associations) { |
| std::unique_ptr<base::DictionaryValue> association_info_value( |
| new base::DictionaryValue()); |
| association_info_value->SetKey(kTouchDeviceIdentifier, |
| base::Value(association.first.ToString())); |
| association_info_value->SetKey( |
| kPortAssociationDisplayId, |
| base::Value(base::NumberToString(association.second))); |
| |
| pref_data->SetKey(association.first.SecondaryIdToString(), |
| association_info_value->Clone()); |
| } |
| } |
| |
| // Stores mirror info for each external display. |
| void StoreExternalDisplayMirrorInfo(PrefService* pref_service) { |
| ListPrefUpdate update(pref_service, prefs::kExternalDisplayMirrorInfo); |
| base::ListValue* pref_data = update.Get(); |
| pref_data->Clear(); |
| const std::set<int64_t>& external_display_mirror_info = |
| GetDisplayManager()->external_display_mirror_info(); |
| for (const auto& id : external_display_mirror_info) |
| pref_data->Append(base::Value(base::NumberToString(id))); |
| } |
| |
| // Stores mixed mirror mode parameters. Clear the preferences if |
| // |mixed_mirror_mode_params| is null. |
| void StoreDisplayMixedMirrorModeParams( |
| PrefService* pref_service, |
| const absl::optional<display::MixedMirrorModeParams>& mixed_params) { |
| DictionaryPrefUpdate update(pref_service, |
| prefs::kDisplayMixedMirrorModeParams); |
| base::DictionaryValue* pref_data = update.Get(); |
| pref_data->Clear(); |
| |
| if (!mixed_params) |
| return; |
| |
| pref_data->SetKey(kMirroringSourceId, |
| base::Value(base::NumberToString(mixed_params->source_id))); |
| |
| base::ListValue mirroring_destination_ids_value; |
| for (const auto& id : mixed_params->destination_ids) { |
| mirroring_destination_ids_value.Append( |
| base::Value(base::NumberToString(id))); |
| } |
| pref_data->SetKey(kMirroringDestinationIds, |
| std::move(mirroring_destination_ids_value)); |
| } |
| |
| void StoreCurrentDisplayMixedMirrorModeParams(PrefService* pref_service) { |
| StoreDisplayMixedMirrorModeParams( |
| pref_service, GetDisplayManager()->mixed_mirror_mode_params()); |
| } |
| |
| } // namespace |
| |
| // static |
| void DisplayPrefs::RegisterLocalStatePrefs(PrefRegistrySimple* registry) { |
| registry->RegisterDictionaryPref(prefs::kSecondaryDisplays); |
| registry->RegisterDictionaryPref(prefs::kDisplayProperties); |
| registry->RegisterStringPref(prefs::kDisplayPowerState, kDisplayPowerAllOn); |
| registry->RegisterDictionaryPref(prefs::kDisplayRotationLock); |
| registry->RegisterDictionaryPref(prefs::kDisplayTouchAssociations); |
| registry->RegisterDictionaryPref(prefs::kDisplayTouchPortAssociations); |
| registry->RegisterListPref(prefs::kExternalDisplayMirrorInfo); |
| registry->RegisterDictionaryPref(prefs::kDisplayMixedMirrorModeParams); |
| registry->RegisterBooleanPref(prefs::kAllowMGSToStoreDisplayProperties, |
| false); |
| } |
| |
| DisplayPrefs::DisplayPrefs(PrefService* local_state) |
| : local_state_(local_state) { |
| Shell::Get()->session_controller()->AddObserver(this); |
| |
| // |local_state_| could be null in tests. |
| if (local_state_) |
| LoadDisplayPreferences(); |
| } |
| |
| DisplayPrefs::~DisplayPrefs() { |
| Shell::Get()->session_controller()->RemoveObserver(this); |
| } |
| |
| void DisplayPrefs::OnFirstSessionStarted() { |
| if (store_requested_) |
| MaybeStoreDisplayPrefs(); |
| } |
| |
| void DisplayPrefs::MaybeStoreDisplayPrefs() { |
| DCHECK(local_state_); |
| |
| // Stores the power state regardless of the login status, because the power |
| // state respects to the current status (close/open) of the lid which can be |
| // changed in any situation. See http://crbug.com/285360 |
| StoreCurrentDisplayPowerState(local_state_); |
| StoreCurrentDisplayRotationLockPrefs(local_state_); |
| |
| // We cannot really decide whether to store display prefs until there is an |
| // active user session. |OnFirstSessionStarted()| should eventually attempt to |
| // do a store in this case. |
| if (!Shell::Get()->session_controller()->GetUserType()) { |
| store_requested_ = true; |
| return; |
| } |
| |
| // There are multiple scenarios where we don't want to save display prefs. |
| // Some user types are not allowed, we don't want to change them while a |
| // display change confirmation dialog is still visible, etc. |
| if (!UserCanSaveDisplayPreference() || |
| !Shell::Get()->ShouldSaveDisplaySettings()) { |
| return; |
| } |
| |
| store_requested_ = false; |
| StoreCurrentDisplayLayoutPrefs(local_state_); |
| StoreCurrentDisplayProperties(local_state_); |
| StoreDisplayTouchAssociations(local_state_); |
| StoreExternalDisplayMirrorInfo(local_state_); |
| StoreCurrentDisplayMixedMirrorModeParams(local_state_); |
| |
| // The display prefs need to be committed immediately to guarantee they're not |
| // lost, and are restored properly on reboot. https://crbug.com/936884. |
| // This sends a request via mojo to commit the prefs to disk. |
| local_state_->CommitPendingWrite(); |
| } |
| |
| void DisplayPrefs::LoadDisplayPreferences() { |
| LoadDisplayLayouts(local_state_); |
| LoadDisplayProperties(local_state_); |
| LoadExternalDisplayMirrorInfo(local_state_); |
| LoadDisplayMixedMirrorModeParams(local_state_); |
| LoadDisplayRotationState(local_state_); |
| LoadDisplayTouchAssociations(local_state_); |
| |
| // Now that the display prefs have been loaded, request to reconfigure the |
| // displays, but signal the display manager to restore the mirror state of |
| // external displays from the loaded prefs (if any). |
| Shell::Get() |
| ->display_manager() |
| ->set_should_restore_mirror_mode_from_display_prefs(true); |
| Shell::Get()->display_configurator()->OnConfigurationChanged(); |
| |
| // Ensure that we have a reasonable initial display power state if |
| // powerd fails to send us one over D-Bus. Otherwise, we won't restore |
| // displays correctly after retaking control when changing virtual terminals. |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| chromeos::switches::kFirstExecAfterBoot)) { |
| Shell::Get()->display_configurator()->InitializeDisplayPowerState(); |
| return; |
| } |
| |
| // Restore DisplayPowerState: |
| const std::string value = |
| local_state_->Get(prefs::kDisplayPowerState)->GetString(); |
| chromeos::DisplayPowerState power_state; |
| if (GetDisplayPowerStateFromString(value, &power_state)) |
| Shell::Get()->display_configurator()->SetInitialDisplayPower(power_state); |
| } |
| |
| void DisplayPrefs::StoreDisplayRotationPrefsForTest( |
| display::Display::Rotation rotation, |
| bool rotation_lock) { |
| StoreDisplayRotationPrefs(local_state_, rotation, rotation_lock); |
| } |
| |
| void DisplayPrefs::StoreDisplayLayoutPrefForTest( |
| const display::DisplayIdList& list, |
| const display::DisplayLayout& layout) { |
| StoreDisplayLayoutPref(local_state_, list, layout); |
| } |
| |
| void DisplayPrefs::StoreDisplayPowerStateForTest( |
| DisplayPowerState power_state) { |
| StoreDisplayPowerState(local_state_, power_state); |
| } |
| |
| void DisplayPrefs::LoadTouchAssociationPreferenceForTest() { |
| LoadDisplayTouchAssociations(local_state_); |
| } |
| |
| void DisplayPrefs::StoreLegacyTouchDataForTest( |
| int64_t display_id, |
| const display::TouchCalibrationData& data) { |
| DictionaryPrefUpdate update(local_state_, prefs::kDisplayProperties); |
| base::DictionaryValue* pref_data = update.Get(); |
| base::DictionaryValue property_value; |
| TouchDataToValue(data, &property_value); |
| pref_data->SetKey(base::NumberToString(display_id), |
| std::move(property_value)); |
| } |
| |
| bool DisplayPrefs::ParseTouchCalibrationStringForTest( |
| const std::string& str, |
| display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad) { |
| return ParseTouchCalibrationStringValue(str, point_pair_quad); |
| } |
| |
| void DisplayPrefs::StoreDisplayMixedMirrorModeParamsForTest( |
| const absl::optional<display::MixedMirrorModeParams>& mixed_params) { |
| StoreDisplayMixedMirrorModeParams(local_state_, mixed_params); |
| } |
| |
| } // namespace ash |