910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/protocol/coordinate_converter.h"

#include <algorithm>
#include <optional>

#include "base/check.h"
#include "base/numerics/safe_conversions.h"
#include "remoting/base/logging.h"
#include "remoting/proto/control.pb.h"
#include "remoting/proto/coordinates.pb.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"

namespace remoting::protocol {

namespace {

// Scales `fraction` (between 0 and 1) to a value between `minimum` and
// (minimum + size - 1).
int ScaleAndClamp(float fraction, int minimum, int size) {
  int scaled = base::ClampRound(fraction * size);
  scaled = std::clamp(scaled, 0, size - 1);
  return scaled + minimum;
}

}  // namespace

CoordinateConverter::CoordinateConverter() = default;
CoordinateConverter::~CoordinateConverter() = default;

void CoordinateConverter::set_video_layout(const VideoLayout& layout) {
  video_layout_ = layout;
}

void CoordinateConverter::set_fallback_geometry(
    const webrtc::DesktopRect& geometry) {
  fallback_geometry_ = geometry;
}

std::optional<webrtc::DesktopVector>
CoordinateConverter::ToGlobalAbsoluteCoordinate(
    const FractionalCoordinate& fractional) const {
  if (!fractional.has_x() || !fractional.has_y()) {
    LOG(ERROR) << "Event has incomplete fractional coordinates.";
    return std::nullopt;
  }

  int bounds_x;
  int bounds_y;
  int bounds_width;
  int bounds_height;
  if (fractional.has_screen_id()) {
    auto screen_id = fractional.screen_id();
    VLOG(3) << "screen_id = " << screen_id;
    auto it = std::ranges::find_if(video_layout_.video_track(),
                                   [screen_id](const VideoTrackLayout& track) {
                                     return track.has_screen_id() &&
                                            track.screen_id() == screen_id;
                                   });
    if (it == video_layout_.video_track().end()) {
      LOG(ERROR) << "screen_id " << screen_id
                 << " not found in the video layout.";
      return std::nullopt;
    }

    const VideoTrackLayout& monitor = *it;

    DCHECK(monitor.has_position_x());
    DCHECK(monitor.has_position_y());
    DCHECK(monitor.has_width());
    DCHECK(monitor.has_height());
    bounds_x = monitor.position_x();
    bounds_y = monitor.position_y();
    bounds_width = monitor.width();
    bounds_height = monitor.height();
  } else {
    if (fallback_geometry_.is_empty()) {
      LOG(ERROR)
          << "Fractional coordinates have no screen_id and no fallback is set.";
      return std::nullopt;
    }

    bounds_x = fallback_geometry_.left();
    bounds_y = fallback_geometry_.top();
    bounds_width = fallback_geometry_.width();
    bounds_height = fallback_geometry_.height();
  }

  int new_x = ScaleAndClamp(fractional.x(), bounds_x, bounds_width);
  int new_y = ScaleAndClamp(fractional.y(), bounds_y, bounds_height);
  VLOG(3) << "(" << fractional.x() << ", " << fractional.y() << ") -> ("
          << new_x << ", " << new_y << ")";
  return webrtc::DesktopVector{new_x, new_y};
}

}  // namespace remoting::protocol