diff options
author | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-26 02:02:18 +0000 |
---|---|---|
committer | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-26 02:02:18 +0000 |
commit | 05a6071c95d34de491ad22836b1b450e79c0d416 (patch) | |
tree | 2ffb8a5bb80db4f2fc022a47c8f00ea8fb09db67 | |
parent | 6c2432f4a60bb8d6858e3db349097335482660ef (diff) | |
download | chromium_src-05a6071c95d34de491ad22836b1b450e79c0d416.zip chromium_src-05a6071c95d34de491ad22836b1b450e79c0d416.tar.gz chromium_src-05a6071c95d34de491ad22836b1b450e79c0d416.tar.bz2 |
Cross-platform plumbing for resize-to-client and Linux implementation.
BUG=145049
Review URL: https://chromiumcodereview.appspot.com/10918224
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158718 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | remoting/host/chromoting_host.cc | 7 | ||||
-rw-r--r-- | remoting/host/chromoting_host.h | 3 | ||||
-rw-r--r-- | remoting/host/client_session.cc | 3 | ||||
-rw-r--r-- | remoting/host/client_session.h | 6 | ||||
-rw-r--r-- | remoting/host/desktop_resizer.h | 52 | ||||
-rw-r--r-- | remoting/host/desktop_resizer_linux.cc | 45 | ||||
-rw-r--r-- | remoting/host/desktop_resizer_mac.cc | 45 | ||||
-rw-r--r-- | remoting/host/desktop_resizer_win.cc | 45 | ||||
-rw-r--r-- | remoting/host/host_mock_objects.h | 2 | ||||
-rw-r--r-- | remoting/host/host_status_observer.h | 6 | ||||
-rw-r--r-- | remoting/host/remoting_me2me_host.cc | 9 | ||||
-rw-r--r-- | remoting/host/resizing_host_observer.cc | 159 | ||||
-rw-r--r-- | remoting/host/resizing_host_observer.h | 48 | ||||
-rw-r--r-- | remoting/host/resizing_host_observer_unittest.cc | 194 | ||||
-rw-r--r-- | remoting/remoting.gyp | 7 |
15 files changed, 630 insertions, 1 deletions
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc index 487ac35..2054eb8 100644 --- a/remoting/host/chromoting_host.cc +++ b/remoting/host/chromoting_host.cc @@ -256,6 +256,13 @@ void ChromotingHost::OnSessionRouteChange( route)); } +void ChromotingHost::OnClientDimensionsChanged(ClientSession* session, + const SkISize& size) { + DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); + FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, + OnClientDimensionsChanged(session->client_jid(), size)); +} + void ChromotingHost::OnSessionManagerReady() { DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); // Don't need to do anything here, just wait for incoming diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h index a700c24..4742f94 100644 --- a/remoting/host/chromoting_host.h +++ b/remoting/host/chromoting_host.h @@ -20,6 +20,7 @@ #include "remoting/protocol/authenticator.h" #include "remoting/protocol/session_manager.h" #include "remoting/protocol/connection_to_client.h" +#include "third_party/skia/include/core/SkSize.h" namespace remoting { @@ -115,6 +116,8 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>, ClientSession* session, const std::string& channel_name, const protocol::TransportRoute& route) OVERRIDE; + virtual void OnClientDimensionsChanged(ClientSession* session, + const SkISize& size) OVERRIDE; // SessionManager::Listener implementation. virtual void OnSessionManagerReady() OVERRIDE; diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc index a7f89ea..8a9b842 100644 --- a/remoting/host/client_session.cc +++ b/remoting/host/client_session.cc @@ -70,10 +70,11 @@ ClientSession::ClientSession( void ClientSession::NotifyClientDimensions( const protocol::ClientDimensions& dimensions) { - // TODO(wez): Use the dimensions, e.g. to resize the host desktop to match. if (dimensions.has_width() && dimensions.has_height()) { VLOG(1) << "Received ClientDimensions (width=" << dimensions.width() << ", height=" << dimensions.height() << ")"; + event_handler_->OnClientDimensionsChanged( + this, SkISize::Make(dimensions.width(), dimensions.height())); } } diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h index af78877..f0ab673 100644 --- a/remoting/host/client_session.h +++ b/remoting/host/client_session.h @@ -23,6 +23,7 @@ #include "remoting/protocol/input_filter.h" #include "remoting/protocol/input_stub.h" #include "third_party/skia/include/core/SkPoint.h" +#include "third_party/skia/include/core/SkSize.h" namespace base { class SingleThreadTaskRunner; @@ -75,6 +76,11 @@ class ClientSession const std::string& channel_name, const protocol::TransportRoute& route) = 0; + // Called when the initial client dimensions are received, and when they + // change. + virtual void OnClientDimensionsChanged(ClientSession* client, + const SkISize& size) = 0; + protected: virtual ~EventHandler() {} }; diff --git a/remoting/host/desktop_resizer.h b/remoting/host/desktop_resizer.h new file mode 100644 index 0000000..6ab6f04 --- /dev/null +++ b/remoting/host/desktop_resizer.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef REMOTING_HOST_DESKTOP_RESIZER_H_ +#define REMOTING_HOST_DESKTOP_RESIZER_H_ + +#include <list> + +#include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkRect.h" + +namespace remoting { + +class DesktopResizer { + public: + virtual ~DesktopResizer() {} + + // Create a platform-specific DesktopResizer instance. + static scoped_ptr<DesktopResizer> Create(); + + // Return the current size of the desktop. If resize is not supported, the + // implementation should return 0x0, in which case none of the other API + // methods will be called. + virtual SkISize GetCurrentSize() = 0; + + // Get the list of supported sizes, which should ideally include |preferred|. + // Implementations will generally do one of the following: + // 1. Return the list of sizes supported by the underlying video driver, + // regardless of |preferred|. + // 2. Return a list containing just |preferred|, perhaps after imposing + // some minimum size constraint. This will typically be the case if + // there are no constraints imposed by the underlying video driver. + // 3. Return an empty list if resize is not supported. + virtual std::list<SkISize> GetSupportedSizes(const SkISize& preferred) = 0; + + // Set the size of the desktop. |size| must be one of the sizes previously + // returned by |GetSupportedSizes|. Note that implementations should fail + // gracefully if the specified size is no longer supported, since monitor + // configurations may change on the fly. + virtual void SetSize(const SkISize& size) = 0; + + // Restore the original desktop size. The caller must provide the original + // size of the desktop, as returned by |GetCurrentSize|, as a hint. However, + // implementaions are free to ignore this. For example, virtual hosts will + // typically ignore it to avoid unnecessary resize operations. + virtual void RestoreSize(const SkISize& original) = 0; +}; + +} // namespace remoting + +#endif // REMOTING_HOST_DESKTOP_RESIZER_H_ diff --git a/remoting/host/desktop_resizer_linux.cc b/remoting/host/desktop_resizer_linux.cc new file mode 100644 index 0000000..66fe413 --- /dev/null +++ b/remoting/host/desktop_resizer_linux.cc @@ -0,0 +1,45 @@ +// 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/desktop_resizer.h" + +#include "base/logging.h" + +namespace remoting { + +namespace { +class DesktopResizerLinux : public DesktopResizer { + public: + DesktopResizerLinux() { + } + + virtual SkISize GetCurrentSize() OVERRIDE { + NOTIMPLEMENTED(); + return SkISize::Make(0, 0); + } + + virtual std::list<SkISize> GetSupportedSizes( + const SkISize& preferred) OVERRIDE { + NOTIMPLEMENTED(); + return std::list<SkISize>(); + } + + virtual void SetSize(const SkISize& size) OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void RestoreSize(const SkISize& original) OVERRIDE { + NOTIMPLEMENTED(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DesktopResizerLinux); +}; +} // namespace + +scoped_ptr<DesktopResizer> DesktopResizer::Create() { + return scoped_ptr<DesktopResizer>(new DesktopResizerLinux); +} + +} // namespace remoting diff --git a/remoting/host/desktop_resizer_mac.cc b/remoting/host/desktop_resizer_mac.cc new file mode 100644 index 0000000..5d23ec6 --- /dev/null +++ b/remoting/host/desktop_resizer_mac.cc @@ -0,0 +1,45 @@ +// 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/desktop_resizer.h" + +#include "base/logging.h" + +namespace remoting { + +namespace { +class DesktopResizerMac : public DesktopResizer { + public: + DesktopResizerMac() { + } + + virtual SkISize GetCurrentSize() OVERRIDE { + NOTIMPLEMENTED(); + return SkISize::Make(0, 0); + } + + virtual std::list<SkISize> GetSupportedSizes( + const SkISize& preferred) OVERRIDE { + NOTIMPLEMENTED(); + return std::list<SkISize>(); + } + + virtual void SetSize(const SkISize& size) OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void RestoreSize(const SkISize& original) OVERRIDE { + NOTIMPLEMENTED(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DesktopResizerMac); +}; +} // namespace + +scoped_ptr<DesktopResizer> DesktopResizer::Create() { + return scoped_ptr<DesktopResizer>(new DesktopResizerMac); +} + +} // namespace remoting diff --git a/remoting/host/desktop_resizer_win.cc b/remoting/host/desktop_resizer_win.cc new file mode 100644 index 0000000..65ae55a --- /dev/null +++ b/remoting/host/desktop_resizer_win.cc @@ -0,0 +1,45 @@ +// 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/desktop_resizer.h" + +#include "base/logging.h" + +namespace remoting { + +namespace { +class DesktopResizerWin : public DesktopResizer { + public: + DesktopResizerWin() { + } + + virtual SkISize GetCurrentSize() OVERRIDE { + NOTIMPLEMENTED(); + return SkISize::Make(0, 0); + } + + virtual std::list<SkISize> GetSupportedSizes( + const SkISize& preferred) OVERRIDE { + NOTIMPLEMENTED(); + return std::list<SkISize>(); + } + + virtual void SetSize(const SkISize& size) OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void RestoreSize(const SkISize& original) OVERRIDE { + NOTIMPLEMENTED(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin); +}; +} // namespace + +scoped_ptr<DesktopResizer> DesktopResizer::Create() { + return scoped_ptr<DesktopResizer>(new DesktopResizerWin); +} + +} // namespace remoting diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h index 19c05bb..e87c94d 100644 --- a/remoting/host/host_mock_objects.h +++ b/remoting/host/host_mock_objects.h @@ -112,6 +112,8 @@ class MockClientSessionEventHandler : public ClientSession::EventHandler { ClientSession* client, const std::string& channel_name, const protocol::TransportRoute& route)); + MOCK_METHOD2(OnClientDimensionsChanged, void(ClientSession* client, + const SkISize& size)); private: DISALLOW_COPY_AND_ASSIGN(MockClientSessionEventHandler); diff --git a/remoting/host/host_status_observer.h b/remoting/host/host_status_observer.h index 200b7d8..a37e9e4 100644 --- a/remoting/host/host_status_observer.h +++ b/remoting/host/host_status_observer.h @@ -7,6 +7,8 @@ #include <string> +#include "third_party/skia/include/core/SkRect.h" + namespace net { class IPEndPoint; } // namespace net @@ -44,6 +46,10 @@ class HostStatusObserver { const std::string& channel_name, const protocol::TransportRoute& route) {} + // Called when the client view dimensions change. + virtual void OnClientDimensionsChanged(const std::string& jid, + const SkISize& size) {} + // Called when hosting is started for an account. virtual void OnStart(const std::string& xmpp_login) {} diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 911124d..6317b24 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -40,6 +40,7 @@ #include "remoting/host/constants.h" #include "remoting/host/config_file_watcher.h" #include "remoting/host/desktop_environment_factory.h" +#include "remoting/host/desktop_resizer.h" #include "remoting/host/dns_blackhole_checker.h" #include "remoting/host/event_executor.h" #include "remoting/host/heartbeat_sender.h" @@ -50,6 +51,7 @@ #include "remoting/host/log_to_server.h" #include "remoting/host/network_settings.h" #include "remoting/host/policy_hack/policy_watcher.h" +#include "remoting/host/resizing_host_observer.h" #include "remoting/host/session_manager_factory.h" #include "remoting/host/signaling_connector.h" #include "remoting/host/usage_stats_consent.h" @@ -130,6 +132,7 @@ class HostProcess #else // !defined(OS_WIN) desktop_environment_factory_(new DesktopEnvironmentFactory()), #endif // !defined(OS_WIN) + desktop_resizer_(DesktopResizer::Create()), exit_code_(kSuccessExitCode) #if defined(OS_MACOSX) , curtain_(base::Bind(&HostProcess::OnDisconnectRequested, @@ -583,6 +586,9 @@ class HostProcess new LogToServer(host_, ServerLogEntry::ME2ME, signal_strategy_.get())); host_event_logger_ = HostEventLogger::Create(host_, kApplicationName); + resizing_host_observer_.reset( + new ResizingHostObserver(desktop_resizer_.get(), host_)); + #if defined(OS_MACOSX) || defined(OS_WIN) if (host_user_interface_.get()) { host_user_interface_->Start( @@ -675,6 +681,7 @@ class HostProcess heartbeat_sender_.reset(); signaling_connector_.reset(); signal_strategy_.reset(); + resizing_host_observer_.reset(); } scoped_ptr<ChromotingHostContext> context_; @@ -705,6 +712,8 @@ class HostProcess bool shutting_down_; scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; + scoped_ptr<DesktopResizer> desktop_resizer_; + scoped_ptr<ResizingHostObserver> resizing_host_observer_; scoped_ptr<XmppSignalStrategy> signal_strategy_; scoped_ptr<SignalingConnector> signaling_connector_; scoped_ptr<HeartbeatSender> heartbeat_sender_; diff --git a/remoting/host/resizing_host_observer.cc b/remoting/host/resizing_host_observer.cc new file mode 100644 index 0000000..ba419512 --- /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 diff --git a/remoting/host/resizing_host_observer.h b/remoting/host/resizing_host_observer.h new file mode 100644 index 0000000..cdafa89 --- /dev/null +++ b/remoting/host/resizing_host_observer.h @@ -0,0 +1,48 @@ +// 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. + +#ifndef REMOTING_HOST_RESIZING_HOST_OBSERVER_H_ +#define REMOTING_HOST_RESIZING_HOST_OBSERVER_H_ + +#include <string> + +#include "remoting/host/host_status_observer.h" +#include "remoting/host/chromoting_host.h" + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace remoting { + +class DesktopResizer; + +// Use the specified DesktopResizer to match host desktop size to the client +// view size as closely as is possible. When the connection closes, restore +// the original desktop size. +class ResizingHostObserver : public HostStatusObserver { + public: + explicit ResizingHostObserver(DesktopResizer* desktop_resizer, + ChromotingHost* host); + virtual ~ResizingHostObserver(); + + // HostStatusObserver interface + virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE; + virtual void OnClientDisconnected(const std::string& jid) OVERRIDE; + virtual void OnClientDimensionsChanged(const std::string& jid, + const SkISize& size) OVERRIDE; + + private: + DesktopResizer* desktop_resizer_; + scoped_refptr<ChromotingHost> host_; + SkISize original_size_; + SkISize previous_size_; + std::string client_jid_; + + DISALLOW_COPY_AND_ASSIGN(ResizingHostObserver); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_RESIZING_HOST_OBSERVER_H_ diff --git a/remoting/host/resizing_host_observer_unittest.cc b/remoting/host/resizing_host_observer_unittest.cc new file mode 100644 index 0000000..4c5fc3b --- /dev/null +++ b/remoting/host/resizing_host_observer_unittest.cc @@ -0,0 +1,194 @@ +// 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 <list> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkSize.h" + +std::ostream& operator<<(std::ostream& os, const SkISize& size) { + return os << size.width() << "x" << size.height(); +} + +namespace remoting { + +class MockDesktopResizer : public DesktopResizer { + public: + MockDesktopResizer(const SkISize& initial_size, bool exact_size_supported, + const SkISize* supported_sizes, int num_supported_sizes) + : initial_size_(initial_size), + current_size_(initial_size), + exact_size_supported_(exact_size_supported) { + for (int i = 0; i < num_supported_sizes; ++i) { + supported_sizes_.push_back(supported_sizes[i]); + } + } + + const SkISize& initial_size() { return initial_size_; } + + // remoting::DesktopResizer interface + virtual SkISize GetCurrentSize() OVERRIDE { + return current_size_; + } + virtual std::list<SkISize> GetSupportedSizes( + const SkISize& preferred) OVERRIDE { + std::list<SkISize> result = supported_sizes_; + if (exact_size_supported_) { + result.push_back(preferred); + } + return result; + } + virtual void SetSize(const SkISize& size) OVERRIDE { + current_size_ = size; + } + virtual void RestoreSize(const SkISize& size) OVERRIDE { + current_size_ = size; + } + + private: + SkISize initial_size_; + SkISize current_size_; + bool exact_size_supported_; + std::list<SkISize> supported_sizes_; +}; + +class ResizingHostObserverTest : public testing::Test { + public: + void SetDesktopResizer(MockDesktopResizer* desktop_resizer) { + CHECK(!desktop_resizer_.get()) << "Call SetDeskopResizer once per test"; + resizing_host_observer_.reset(new ResizingHostObserver(desktop_resizer, + NULL)); + desktop_resizer_.reset(desktop_resizer); + resizing_host_observer_->OnClientAuthenticated(""); + } + + SkISize GetBestSize(const SkISize& client_size) { + resizing_host_observer_->OnClientDimensionsChanged("", client_size); + return desktop_resizer_->GetCurrentSize(); + } + + void VerifySizes(const SkISize* client_sizes, const SkISize* expected_sizes, + int number_of_sizes) { + for (int i = 0; i < number_of_sizes; ++i) { + SkISize best_size = GetBestSize(client_sizes[i]); + EXPECT_EQ(expected_sizes[i], best_size) + << "Input size = " << client_sizes[i]; + } + } + + void Reconnect() { + resizing_host_observer_->OnClientDisconnected(""); + resizing_host_observer_->OnClientAuthenticated(""); + } + + // testing::Test interface + virtual void TearDown() OVERRIDE { + resizing_host_observer_->OnClientDisconnected(""); + EXPECT_EQ(desktop_resizer_->initial_size(), + desktop_resizer_->GetCurrentSize()); + } + + private: + scoped_ptr<ResizingHostObserver> resizing_host_observer_; + scoped_ptr<MockDesktopResizer> desktop_resizer_; +}; + +// Check that the host is not resized if it reports an initial size of zero +// (even if it GetSupportedSizes does not return an empty list). +TEST_F(ResizingHostObserverTest, ZeroGetCurrentSize) { + SkISize zero = { 0, 0 }; + SetDesktopResizer( + new MockDesktopResizer(zero, true, NULL, 0)); + SkISize client_sizes[] = { { 200, 100 }, { 100, 200 } }; + SkISize expected_sizes[] = { zero, zero }; + VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes)); +} + +// Check that the host is not resized if GetSupportedSizes returns an empty +// list (even if GetCurrentSize is supported). +TEST_F(ResizingHostObserverTest, EmptyGetSupportedSizes) { + SkISize initial = { 640, 480 }; + SetDesktopResizer( + new MockDesktopResizer(initial, false, NULL, 0)); + SkISize client_sizes[] = { { 200, 100 }, { 100, 200 } }; + SkISize expected_sizes[] = { initial, initial }; + VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes)); +} + +// Check that if the implementation supports exact size matching, it is used. +TEST_F(ResizingHostObserverTest, SelectExactSize) { + SetDesktopResizer( + new MockDesktopResizer(SkISize::Make(640, 480), true, NULL, 0)); + SkISize client_sizes[] = { { 200, 100 }, { 100, 200 } , { 640, 480 }, + { 480, 640 }, { 1280, 1024 } }; + VerifySizes(client_sizes, client_sizes, arraysize(client_sizes)); +} + +// Check that if the implementation supports a size that is no larger than +// the requested size, then the largest such size is used. +TEST_F(ResizingHostObserverTest, SelectBestSmallerSize) { + SkISize supported_sizes[] = { + SkISize::Make(639, 479), SkISize::Make(640, 480) }; + SetDesktopResizer( + new MockDesktopResizer(SkISize::Make(640, 480), false, + supported_sizes, arraysize(supported_sizes))); + SkISize client_sizes[] = { { 639, 479 }, { 640, 480 }, { 641, 481 }, + { 999, 999 } }; + SkISize expected_sizes[] = { supported_sizes[0], supported_sizes[1], + supported_sizes[1], supported_sizes[1] }; + VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes)); +} + +// Check that if the implementation supports only sizes that are larger than +// the requested size, then the one that requires the least down-scaling. +TEST_F(ResizingHostObserverTest, SelectBestScaleFactor) { + SkISize supported_sizes[] = { + SkISize::Make(100, 100), SkISize::Make(200, 100) }; + SetDesktopResizer( + new MockDesktopResizer(SkISize::Make(200, 100), false, + supported_sizes, arraysize(supported_sizes))); + SkISize client_sizes[] = { { 1, 1 }, { 99, 99 }, { 199, 99 } }; + SkISize expected_sizes[] = { supported_sizes[0], supported_sizes[0], + supported_sizes[1] }; + VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes)); +} + +// Check that if the implementation supports two sizes that have the same +// resultant scale factor, then the widest one is selected. +TEST_F(ResizingHostObserverTest, SelectWidest) { + SkISize supported_sizes[] = { + SkISize::Make(640, 480), SkISize::Make(480, 640) }; + SetDesktopResizer( + new MockDesktopResizer(SkISize::Make(480, 640), false, + supported_sizes, arraysize(supported_sizes))); + SkISize client_sizes[] = { { 100, 100 }, { 480, 480 }, { 500, 500 }, + { 640, 640 }, { 1000, 1000 } }; + SkISize expected_sizes[] = { supported_sizes[0], supported_sizes[0], + supported_sizes[0], supported_sizes[0], + supported_sizes[0] }; + VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes)); +} + +// Check that resize-to-client is disabled if the size is changed explicitly. +TEST_F(ResizingHostObserverTest, ManualResize) { + MockDesktopResizer* desktop_resizer = + new MockDesktopResizer(SkISize::Make(640, 480), true, NULL, 0); + SetDesktopResizer(desktop_resizer); + SkISize client_sizes[] = { { 1, 1 }, { 2, 2 } , { 3, 3 } }; + VerifySizes(client_sizes, client_sizes, arraysize(client_sizes)); + SkISize explicit_size = SkISize::Make(640, 480); + desktop_resizer->SetSize(explicit_size); + SkISize expected_sizes[] = { explicit_size, explicit_size, explicit_size }; + VerifySizes(client_sizes, expected_sizes, arraysize(client_sizes)); + // Make sure this behaviour doesn't persist across reconnect. + Reconnect(); + VerifySizes(client_sizes, client_sizes, arraysize(client_sizes)); +} + +} // namespace remoting diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 9f01d58..3fc9245 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -1355,6 +1355,10 @@ 'host/desktop_environment.h', 'host/desktop_environment_factory.cc', 'host/desktop_environment_factory.h', + 'host/desktop_resizer.h', + 'host/desktop_resizer_linux.cc', + 'host/desktop_resizer_win.cc', + 'host/desktop_resizer_mac.cc', 'host/differ.cc', 'host/differ.h', 'host/disconnect_window.h', @@ -1416,6 +1420,8 @@ 'host/register_support_host_request.h', 'host/remote_input_filter.cc', 'host/remote_input_filter.h', + 'host/resizing_host_observer.cc', + 'host/resizing_host_observer.h', 'host/sas_injector.h', 'host/sas_injector_win.cc', 'host/screen_recorder.cc', @@ -1915,6 +1921,7 @@ 'host/policy_hack/policy_watcher_unittest.cc', 'host/register_support_host_request_unittest.cc', 'host/remote_input_filter_unittest.cc', + 'host/resizing_host_observer_unittest.cc', 'host/screen_recorder_unittest.cc', 'host/server_log_entry_unittest.cc', 'host/test_key_pair.h', |