| Index: remoting/host/resizing_host_observer.cc
 | 
| diff --git a/remoting/host/resizing_host_observer.cc b/remoting/host/resizing_host_observer.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..ba4195122902a48edc9b52514fcdb0c7850784d4
 | 
| --- /dev/null
 | 
| +++ b/remoting/host/resizing_host_observer.cc
 | 
| @@ -0,0 +1,159 @@
 | 
| +// 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 "remoting/host/resizing_host_observer.h"
 | 
| +#include "remoting/host/desktop_resizer.h"
 | 
| +
 | 
| +#include <set>
 | 
| +
 | 
| +#include "base/logging.h"
 | 
| +
 | 
| +namespace remoting {
 | 
| +
 | 
| +ResizingHostObserver::ResizingHostObserver(
 | 
| +    DesktopResizer* desktop_resizer, ChromotingHost* host)
 | 
| +    : desktop_resizer_(desktop_resizer),
 | 
| +      host_(host),
 | 
| +      original_size_(SkISize::Make(0, 0)),
 | 
| +      previous_size_(SkISize::Make(0, 0)) {
 | 
| +  if (host_) {
 | 
| +    host_->AddStatusObserver(this);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +ResizingHostObserver::~ResizingHostObserver() {
 | 
| +  if (host_) {
 | 
| +    host_->RemoveStatusObserver(this);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void ResizingHostObserver::OnClientAuthenticated(const std::string& jid) {
 | 
| +  // This implementation assumes a single connected client, which is what the
 | 
| +  // host currently supports
 | 
| +  DCHECK(client_jid_.empty());
 | 
| +  original_size_ = desktop_resizer_->GetCurrentSize();
 | 
| +  previous_size_ = original_size_;
 | 
| +}
 | 
| +
 | 
| +void ResizingHostObserver::OnClientDisconnected(const std::string& jid) {
 | 
| +  if (!original_size_.isZero()) {
 | 
| +    desktop_resizer_->RestoreSize(original_size_);
 | 
| +    original_size_.set(0, 0);
 | 
| +  }
 | 
| +  client_jid_.clear();
 | 
| +}
 | 
| +
 | 
| +class CandidateSize {
 | 
| + public:
 | 
| +  CandidateSize(const SkISize& candidate, const SkISize& preferred)
 | 
| +      : size_(candidate) {
 | 
| +    // The client scale factor is the smaller of the candidate:preferred ratios
 | 
| +    // for width and height.
 | 
| +    if ((candidate.width() > preferred.width()) ||
 | 
| +        (candidate.height() > preferred.height())) {
 | 
| +      float ratio_width =
 | 
| +          static_cast<float>(preferred.width()) / candidate.width();
 | 
| +      float ratio_height =
 | 
| +          static_cast<float>(preferred.height()) / candidate.height();
 | 
| +      client_scale_factor_ = std::min(ratio_width, ratio_height);
 | 
| +    } else {
 | 
| +      // Since clients do not scale up, 1.0 is the maximum.
 | 
| +      client_scale_factor_ = 1.0;
 | 
| +    }
 | 
| +
 | 
| +    // The aspect ratio "goodness" is defined as being the ratio of the smaller
 | 
| +    // of the two aspect ratios (candidate and preferred) to the larger. The
 | 
| +    // best aspect ratio is the one that most closely matches the preferred
 | 
| +    // aspect ratio (in other words, the ideal aspect ratio "goodness" is 1.0).
 | 
| +    // By keeping the values < 1.0, it allows ratios the differ in opposite
 | 
| +    // directions to be compared numerically.
 | 
| +    float candidate_aspect_ratio =
 | 
| +        static_cast<float>(candidate.width()) / candidate.height();
 | 
| +    float preferred_aspect_ratio =
 | 
| +        static_cast<float>(preferred.width()) / preferred.height();
 | 
| +    if (candidate_aspect_ratio > preferred_aspect_ratio) {
 | 
| +      aspect_ratio_goodness_ = preferred_aspect_ratio / candidate_aspect_ratio;
 | 
| +    } else {
 | 
| +      aspect_ratio_goodness_ = candidate_aspect_ratio / preferred_aspect_ratio;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  const SkISize& size() const { return size_; }
 | 
| +  float client_scale_factor() const { return client_scale_factor_; }
 | 
| +  float aspect_ratio_goodness() const { return aspect_ratio_goodness_; }
 | 
| +  int64 area() const {
 | 
| +    return static_cast<int64>(size_.width()) * size_.height();
 | 
| +  }
 | 
| +
 | 
| +  bool IsBetterThan(const CandidateSize& other) const {
 | 
| +    // If either size would require down-scaling, prefer the one that down-
 | 
| +    // scales the least (since the client scale factor is at most 1.0, this
 | 
| +    // does not differentiate between sizes that don't require down-scaling).
 | 
| +    if (client_scale_factor() < other.client_scale_factor()) {
 | 
| +      return false;
 | 
| +    } else if (client_scale_factor() > other.client_scale_factor()) {
 | 
| +      return true;
 | 
| +    }
 | 
| +
 | 
| +    // If the scale factors are the same, pick the size with the largest area.
 | 
| +    if (area() < other.area()) {
 | 
| +      return false;
 | 
| +    } else if (area() > other.area()) {
 | 
| +      return true;
 | 
| +    }
 | 
| +
 | 
| +    // If the areas are equal, pick the size with the "best" aspect ratio.
 | 
| +    if (aspect_ratio_goodness() < other.aspect_ratio_goodness()) {
 | 
| +      return false;
 | 
| +    } else if (aspect_ratio_goodness() > other.aspect_ratio_goodness()) {
 | 
| +      return true;
 | 
| +    }
 | 
| +
 | 
| +    // If the aspect ratios are equally good (for example, comparing 640x480
 | 
| +    // to 480x640 w.r.t. 640x640), just pick the widest, since desktop UIs
 | 
| +    // are typically design for landscape aspect ratios.
 | 
| +    return size().width() > other.size().width();
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  float client_scale_factor_;
 | 
| +  float aspect_ratio_goodness_;
 | 
| +  SkISize size_;
 | 
| +};
 | 
| +
 | 
| +void ResizingHostObserver::OnClientDimensionsChanged(
 | 
| +    const std::string& jid, const SkISize& preferred_size) {
 | 
| +  if (previous_size_.isZero()) {
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  // If the host desktop size changes other than via the resize-to-client
 | 
| +  // mechanism, then set |previous_size_| to zero and give up. This is an
 | 
| +  // indication that the user doesn't want resize-to-client.
 | 
| +  SkISize new_size = desktop_resizer_->GetCurrentSize();
 | 
| +  if (new_size != previous_size_) {
 | 
| +    previous_size_ = SkISize::Make(0, 0);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  // If the implementation returns any sizes, pick the best one according to
 | 
| +  // the algorithm described in CandidateSize::IsBetterThen.
 | 
| +  std::list<SkISize> sizes =
 | 
| +      desktop_resizer_->GetSupportedSizes(preferred_size);
 | 
| +  if (sizes.empty()) {
 | 
| +    return;
 | 
| +  }
 | 
| +  CandidateSize best_size(sizes.front(), preferred_size);
 | 
| +  std::list<SkISize>::iterator i = sizes.begin();
 | 
| +  for (++i; i != sizes.end(); ++i) {
 | 
| +    CandidateSize candidate_size(*i, preferred_size);
 | 
| +    if (candidate_size.IsBetterThan(best_size)) {
 | 
| +      best_size = candidate_size;
 | 
| +    }
 | 
| +  }
 | 
| +  previous_size_ = best_size.size();
 | 
| +  desktop_resizer_->SetSize(previous_size_);
 | 
| +}
 | 
| +
 | 
| +}  // namespace remoting
 | 
| 
 |