chromium/chrome/browser/extensions/system_display/display_info_provider_utils.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/system_display/display_info_provider_utils.h"

#include "base/strings/string_number_conversions.h"
#include "chromeos/crosapi/mojom/cros_display_config.mojom.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/display/types/display_constants.h"

namespace extensions {

namespace {

namespace system_display = api::system_display;

system_display::LayoutPosition GetLayoutPositionFromMojo(
    crosapi::mojom::DisplayLayoutPosition position) {
  switch (position) {
    case crosapi::mojom::DisplayLayoutPosition::kTop:
      return system_display::LayoutPosition::kTop;
    case crosapi::mojom::DisplayLayoutPosition::kRight:
      return system_display::LayoutPosition::kRight;
    case crosapi::mojom::DisplayLayoutPosition::kBottom:
      return system_display::LayoutPosition::kBottom;
    case crosapi::mojom::DisplayLayoutPosition::kLeft:
      return system_display::LayoutPosition::kLeft;
  }
  NOTREACHED_IN_MIGRATION();
  return system_display::LayoutPosition::kLeft;
}
}  // namespace

void OnGetDisplayLayoutResult(
    base::OnceCallback<void(DisplayInfoProvider::DisplayLayoutList)> callback,
    crosapi::mojom::DisplayLayoutInfoPtr info) {
  DisplayInfoProvider::DisplayLayoutList result;
  if (info->layouts) {
    for (crosapi::mojom::DisplayLayoutPtr& layout : *info->layouts) {
      api::system_display::DisplayLayout display_layout;
      display_layout.id = layout->id;
      display_layout.parent_id = layout->parent_id;
      display_layout.position = GetLayoutPositionFromMojo(layout->position);
      display_layout.offset = layout->offset;
      result.emplace_back(std::move(display_layout));
    }
  }
  std::move(callback).Run(std::move(result));
}

int64_t GetDisplayId(const std::string& display_id_str) {
  int64_t display_id;
  if (!base::StringToInt64(display_id_str, &display_id)) {
    display_id = display::kInvalidDisplayId;
  }
  return display_id;
}

display::Display GetDisplayForId(const std::string& display_id_str) {
  int64_t id = GetDisplayId(display_id_str);
  display::Display display;
  display::Screen::GetScreen()->GetDisplayWithDisplayId(id, &display);
  return display;
}

crosapi::mojom::DisplayLayoutPosition GetDisplayLayoutPosition(
    system_display::LayoutPosition position) {
  switch (position) {
    case system_display::LayoutPosition::kTop:
      return crosapi::mojom::DisplayLayoutPosition::kTop;
    case system_display::LayoutPosition::kRight:
      return crosapi::mojom::DisplayLayoutPosition::kRight;
    case system_display::LayoutPosition::kBottom:
      return crosapi::mojom::DisplayLayoutPosition::kBottom;
    case system_display::LayoutPosition::kLeft:
    case system_display::LayoutPosition::kNone:
      return crosapi::mojom::DisplayLayoutPosition::kLeft;
  }
  NOTREACHED_IN_MIGRATION();
  return crosapi::mojom::DisplayLayoutPosition::kLeft;
}

gfx::Insets GetInsets(const system_display::Insets& insets) {
  return gfx::Insets::TLBR(insets.top, insets.left, insets.bottom,
                           insets.right);
}

bool IsValidRotation(int rotation) {
  return rotation == -1 || rotation == 0 || rotation == 90 || rotation == 180 ||
         rotation == 270;
}

crosapi::mojom::DisplayRotationOptions GetMojomDisplayRotationOptions(
    int rotation_value) {
  DCHECK(IsValidRotation(rotation_value));

  switch (rotation_value) {
    case -1:
      return crosapi::mojom::DisplayRotationOptions::kAutoRotate;
    case 0:
      return crosapi::mojom::DisplayRotationOptions::kZeroDegrees;
    case 90:
      return crosapi::mojom::DisplayRotationOptions::k90Degrees;
    case 180:
      return crosapi::mojom::DisplayRotationOptions::k180Degrees;
    case 270:
      return crosapi::mojom::DisplayRotationOptions::k270Degrees;
    default:
      NOTREACHED_IN_MIGRATION();
      return crosapi::mojom::DisplayRotationOptions::kZeroDegrees;
  }
}

int GetRotationFromMojomDisplayRotationInfo(
    crosapi::mojom::DisplayRotationOptions rotation_options) {
  switch (rotation_options) {
    case crosapi::mojom::DisplayRotationOptions::kAutoRotate:
      return -1;
    case crosapi::mojom::DisplayRotationOptions::kZeroDegrees:
      return 0;
    case crosapi::mojom::DisplayRotationOptions::k90Degrees:
      return 90;
    case crosapi::mojom::DisplayRotationOptions::k180Degrees:
      return 180;
    case crosapi::mojom::DisplayRotationOptions::k270Degrees:
      return 270;
  }
}

std::optional<std::string> ValidateDisplayPropertiesInput(
    const std::string& display_id_str,
    const system_display::DisplayProperties& info) {
  int64_t id = GetDisplayId(display_id_str);
  if (id == display::kInvalidDisplayId) {
    return "Invalid display id";
  }

  const display::Display& primary =
      display::Screen::GetScreen()->GetPrimaryDisplay();
  bool is_primary = id == primary.id() || (info.is_primary && *info.is_primary);

  if (info.is_unified) {
    if (!is_primary) {
      return "Unified desktop mode can only be set for the primary display.";
    }
    // Setting isUnfied may change the display layout so no other properties
    // should be set.
    if (info.mirroring_source_id) {
      return "Unified desktop mode can not be set with mirroringSourceId.";
    }
    if (info.bounds_origin_x || info.bounds_origin_y || info.rotation ||
        info.overscan || info.display_mode || info.display_zoom_factor) {
      LOG(WARNING)
          << "Unified mode set with other properties which will be ignored.";
    }
    return std::nullopt;
  }

  // If mirroring source parameter is specified, no other properties should be
  // set the display list may change when mirroring is applied.
  if (info.mirroring_source_id &&
      (info.is_primary || info.bounds_origin_x || info.bounds_origin_y ||
       info.rotation || info.overscan || info.display_mode ||
       info.display_zoom_factor)) {
    return "No other parameter should be set with mirroringSourceId.";
  }

  // Verify the rotation value is valid.
  if (info.rotation && !IsValidRotation(*info.rotation)) {
    return "Invalid rotation.";
  }

  return std::nullopt;
}

system_display::DisplayMode GetDisplayModeFromMojo(
    const crosapi::mojom::DisplayMode mode) {
  system_display::DisplayMode result;
  result.width = mode.size.width();
  result.height = mode.size.height();
  result.width_in_native_pixels = mode.size_in_native_pixels.width();
  result.height_in_native_pixels = mode.size_in_native_pixels.height();
  result.device_scale_factor = mode.device_scale_factor;
  result.refresh_rate = mode.refresh_rate;
  result.is_native = mode.is_native;
  result.is_interlaced = mode.is_interlaced;
  return result;
}

system_display::DisplayUnitInfo GetDisplayUnitInfoFromMojo(
    const crosapi::mojom::DisplayUnitInfo& mojo_info) {
  system_display::DisplayUnitInfo info;
  info.id = mojo_info.id;
  info.name = mojo_info.name;
  if (mojo_info.edid) {
    info.edid.emplace();
    info.edid->manufacturer_id = mojo_info.edid->manufacturer_id;
    info.edid->product_id = mojo_info.edid->product_id;
    info.edid->year_of_manufacture = mojo_info.edid->year_of_manufacture;
  }
  info.is_primary = mojo_info.is_primary;
  info.is_internal = mojo_info.is_internal;
  info.active_state = mojo_info.is_detected
                          ? system_display::ActiveState::kActive
                          : system_display::ActiveState::kInactive;
  info.is_enabled = mojo_info.is_enabled;
  info.is_auto_rotation_allowed = mojo_info.is_auto_rotation_allowed;
  info.dpi_x = mojo_info.dpi_x;
  info.dpi_y = mojo_info.dpi_y;
  info.rotation =
      GetRotationFromMojomDisplayRotationInfo(mojo_info.rotation_options);
  const gfx::Rect& bounds = mojo_info.bounds;
  info.bounds.left = bounds.x();
  info.bounds.top = bounds.y();
  info.bounds.width = bounds.width();
  info.bounds.height = bounds.height();
  const gfx::Insets& overscan = mojo_info.overscan;
  info.overscan.left = overscan.left();
  info.overscan.top = overscan.top();
  info.overscan.right = overscan.right();
  info.overscan.bottom = overscan.bottom();
  const gfx::Rect& work_area = mojo_info.work_area;
  info.work_area.left = work_area.x();
  info.work_area.top = work_area.y();
  info.work_area.width = work_area.width();
  info.work_area.height = work_area.height();
  for (const crosapi::mojom::DisplayModePtr& mode :
       mojo_info.available_display_modes) {
    info.modes.emplace_back(GetDisplayModeFromMojo(*mode));
  }
  if (!info.modes.empty()) {
    int index = mojo_info.selected_display_mode_index;
    if (index < 0 || index >= static_cast<int>(info.modes.size())) {
      index = 0;
    }
    info.modes[index].is_selected = true;
  }
  info.has_touch_support = mojo_info.has_touch_support;
  info.has_accelerometer_support = mojo_info.has_accelerometer_support;
  info.available_display_zoom_factors =
      mojo_info.available_display_zoom_factors;
  info.display_zoom_factor = mojo_info.display_zoom_factor;
  return info;
}

crosapi::mojom::TouchCalibrationPairPtr GetTouchCalibrationPair(
    const system_display::TouchCalibrationPair& pair) {
  auto result = crosapi::mojom::TouchCalibrationPair::New();
  result->display_point =
      gfx::Point(pair.display_point.x, pair.display_point.y);
  result->touch_point = gfx::Point(pair.touch_point.x, pair.touch_point.y);
  return result;
}

void SetDisplayUnitInfoLayoutProperties(
    const crosapi::mojom::DisplayLayoutInfo& layout,
    system_display::DisplayUnitInfo* display) {
  display->is_unified =
      layout.layout_mode == crosapi::mojom::DisplayLayoutMode::kUnified;
  if (layout.mirror_source_id) {
    display->mirroring_source_id = *layout.mirror_source_id;
    if (layout.mirror_destination_ids) {
      for (const std::string& id : *layout.mirror_destination_ids) {
        display->mirroring_destination_ids.push_back(id);
      }
    }
  }
}

}  // namespace extensions