summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-02 22:16:41 +0000
committergarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-02 22:16:41 +0000
commitd65e15bdcd7ffe01ec2054177a7e78fa69683a38 (patch)
treefb05d40a5b8bf2dfd943999b19fcb304e088d08e
parent051e71e1b702970eb39302abbe29836239036a3d (diff)
downloadchromium_src-d65e15bdcd7ffe01ec2054177a7e78fa69683a38.zip
chromium_src-d65e15bdcd7ffe01ec2054177a7e78fa69683a38.tar.gz
chromium_src-d65e15bdcd7ffe01ec2054177a7e78fa69683a38.tar.bz2
[Chromoting] Initial plumbing for cursor shape.
This cl contains: * protocol for sending cursor shape on control channel from host to client * cross-platform (Pepper) client code for rendering host cursor * Linux host support for reading current cursor shape Separate CLs will follow with Mac and Windows host support. BUG=116229 TEST=none Review URL: https://chromiumcodereview.appspot.com/10382184 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140205 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--remoting/client/chromoting_client.cc5
-rw-r--r--remoting/client/chromoting_client.h6
-rw-r--r--remoting/client/chromoting_view.h4
-rw-r--r--remoting/client/plugin/chromoting_instance.cc50
-rw-r--r--remoting/client/plugin/chromoting_instance.h6
-rw-r--r--remoting/client/plugin/pepper_view.cc4
-rw-r--r--remoting/client/plugin/pepper_view.h1
-rw-r--r--remoting/host/capturer.h15
-rw-r--r--remoting/host/capturer_fake.cc5
-rw-r--r--remoting/host/capturer_fake.h3
-rw-r--r--remoting/host/capturer_linux.cc98
-rw-r--r--remoting/host/capturer_mac.cc13
-rw-r--r--remoting/host/capturer_mac_unittest.cc20
-rw-r--r--remoting/host/capturer_win.cc12
-rw-r--r--remoting/host/host_mock_objects.h2
-rw-r--r--remoting/host/screen_recorder.cc29
-rw-r--r--remoting/host/screen_recorder.h6
-rw-r--r--remoting/host/screen_recorder_unittest.cc2
-rw-r--r--remoting/proto/control.proto13
-rw-r--r--remoting/proto/internal.proto1
-rw-r--r--remoting/protocol/client_control_dispatcher.cc2
-rw-r--r--remoting/protocol/client_control_dispatcher.h5
-rw-r--r--remoting/protocol/client_stub.h4
-rw-r--r--remoting/protocol/connection_to_client.h3
-rw-r--r--remoting/protocol/connection_to_host.h1
-rw-r--r--remoting/protocol/cursor_shape_stub.h31
-rw-r--r--remoting/protocol/host_control_dispatcher.cc7
-rw-r--r--remoting/protocol/host_control_dispatcher.h13
-rw-r--r--remoting/protocol/protocol_mock_objects.h4
29 files changed, 332 insertions, 33 deletions
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc
index 10a15c9..a28b887 100644
--- a/remoting/client/chromoting_client.cc
+++ b/remoting/client/chromoting_client.cc
@@ -106,6 +106,11 @@ void ChromotingClient::InjectClipboardEvent(
view_->GetClipboardStub()->InjectClipboardEvent(event);
}
+void ChromotingClient::SetCursorShape(
+ const protocol::CursorShapeInfo& cursor_shape) {
+ view_->GetCursorShapeStub()->SetCursorShape(cursor_shape);
+}
+
void ChromotingClient::ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
const base::Closure& done) {
DCHECK(message_loop()->BelongsToCurrentThread());
diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h
index d784b5a..bbb8a48 100644
--- a/remoting/client/chromoting_client.h
+++ b/remoting/client/chromoting_client.h
@@ -55,10 +55,14 @@ class ChromotingClient : public protocol::ConnectionToHost::HostEventCallback,
// Return the stats recorded by this client.
ChromotingStats* GetStats();
- // ClipboardStub implementation.
+ // ClipboardStub implementation for receiving clipboard data from host.
virtual void InjectClipboardEvent(const protocol::ClipboardEvent& event)
OVERRIDE;
+ // CursorShapeStub implementation for receiving cursor shape updates.
+ virtual void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape)
+ OVERRIDE;
+
// ConnectionToHost::HostEventCallback implementation.
virtual void OnConnectionState(
protocol::ConnectionToHost::State state,
diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h
index af9a5ac..1035d3c 100644
--- a/remoting/client/chromoting_view.h
+++ b/remoting/client/chromoting_view.h
@@ -12,6 +12,7 @@ namespace remoting {
namespace protocol {
class ClipboardStub;
+class CursorShapeStub;
} // namespace protocol
// ChromotingView defines the behavior of an object that draws a view of the
@@ -33,6 +34,9 @@ class ChromotingView {
// Get the view's ClipboardStub implementation.
virtual protocol::ClipboardStub* GetClipboardStub() = 0;
+
+ // Get the view's CursorShapeStub implementation.
+ virtual protocol::CursorShapeStub* GetCursorShapeStub() = 0;
};
} // namespace remoting
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index d6c8b5c..808dd84 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -23,6 +23,7 @@
#include "media/base/media.h"
#include "ppapi/cpp/completion_callback.h"
#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/mouse_cursor.h"
#include "ppapi/cpp/rect.h"
#include "remoting/base/constants.h"
#include "remoting/base/util.h"
@@ -49,6 +50,9 @@ namespace remoting {
namespace {
+// 32-bit BGRA is 4 bytes per pixel.
+const int kBytesPerPixel = 4;
+
const int kPerfStatsIntervalMs = 1000;
std::string ConnectionStateToString(ChromotingInstance::ConnectionState state) {
@@ -554,6 +558,52 @@ void ChromotingInstance::InjectClipboardEvent(
PostChromotingMessage("injectClipboardItem", data.Pass());
}
+void ChromotingInstance::SetCursorShape(
+ const protocol::CursorShapeInfo& cursor_shape) {
+ if (!cursor_shape.has_data() ||
+ !cursor_shape.has_width() ||
+ !cursor_shape.has_height() ||
+ !cursor_shape.has_hotspot_x() ||
+ !cursor_shape.has_hotspot_y()) {
+ return;
+ }
+
+ if (pp::ImageData::GetNativeImageDataFormat() !=
+ PP_IMAGEDATAFORMAT_BGRA_PREMUL) {
+ VLOG(2) << "Unable to set cursor shape - non-native image format";
+ return;
+ }
+
+ int width = cursor_shape.width();
+ int height = cursor_shape.height();
+
+ if (width > 32 || height > 32) {
+ VLOG(2) << "Cursor too large for SetCursor: "
+ << width << "x" << height << " > 32x32";
+ return;
+ }
+
+ int hotspot_x = cursor_shape.hotspot_x();
+ int hotspot_y = cursor_shape.hotspot_y();
+
+ pp::ImageData cursor_image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
+ pp::Size(width, height), false);
+
+ int bytes_per_row = width * kBytesPerPixel;
+ const uint8* src_row_data = reinterpret_cast<const uint8*>(
+ cursor_shape.data().data());
+ uint8* dst_row_data = reinterpret_cast<uint8*>(cursor_image.data());
+ for (int row = 0; row < height; row++) {
+ memcpy(dst_row_data, src_row_data, bytes_per_row);
+ src_row_data += bytes_per_row;
+ dst_row_data += cursor_image.stride();
+ }
+
+ pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM,
+ cursor_image,
+ pp::Point(hotspot_x, hotspot_y));
+}
+
// static
void ChromotingInstance::RegisterLogMessageHandler() {
base::AutoLock lock(g_logging_lock.Get());
diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h
index 1bbaf7d..dc8f549 100644
--- a/remoting/client/plugin/chromoting_instance.h
+++ b/remoting/client/plugin/chromoting_instance.h
@@ -32,6 +32,7 @@
#include "remoting/client/plugin/pepper_plugin_thread_delegate.h"
#include "remoting/proto/event.pb.h"
#include "remoting/protocol/clipboard_stub.h"
+#include "remoting/protocol/cursor_shape_stub.h"
#include "remoting/protocol/connection_to_host.h"
namespace base {
@@ -64,6 +65,7 @@ struct ClientConfig;
class ChromotingInstance :
public protocol::ClipboardStub,
+ public protocol::CursorShapeStub,
public pp::Instance,
public base::SupportsWeakPtr<ChromotingInstance> {
public:
@@ -125,6 +127,10 @@ class ChromotingInstance :
virtual void InjectClipboardEvent(const protocol::ClipboardEvent& event)
OVERRIDE;
+ // CursorShapeStub implementation.
+ virtual void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape)
+ OVERRIDE;
+
// Called by PepperView.
void SetDesktopSize(int width, int height);
void SetConnectionState(ConnectionState state, ConnectionError error);
diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc
index 8aa9f11..531ff6c 100644
--- a/remoting/client/plugin/pepper_view.cc
+++ b/remoting/client/plugin/pepper_view.cc
@@ -150,6 +150,10 @@ protocol::ClipboardStub* PepperView::GetClipboardStub() {
return instance_;
}
+protocol::CursorShapeStub* PepperView::GetCursorShapeStub() {
+ return instance_;
+}
+
void PepperView::SetView(const SkISize& view_size, const SkIRect& clip_area) {
bool view_changed = false;
diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h
index 4aa1857..949703a 100644
--- a/remoting/client/plugin/pepper_view.h
+++ b/remoting/client/plugin/pepper_view.h
@@ -40,6 +40,7 @@ class PepperView : public ChromotingView,
protocol::ConnectionToHost::State state,
protocol::ErrorCode error) OVERRIDE;
virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE;
+ virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE;
// FrameConsumer implementation.
virtual void ApplyBuffer(const SkISize& view_size,
diff --git a/remoting/host/capturer.h b/remoting/host/capturer.h
index 5379e7d..0eaf1b4 100644
--- a/remoting/host/capturer.h
+++ b/remoting/host/capturer.h
@@ -7,11 +7,17 @@
#include "base/basictypes.h"
#include "base/callback.h"
-#include "remoting/base/capture_data.h"
+#include "media/base/video_frame.h"
#include "third_party/skia/include/core/SkRegion.h"
namespace remoting {
+namespace protocol {
+class CursorShapeInfo;
+}
+
+class CaptureData;
+
// A class to perform the task of capturing the image of a window.
// The capture action is asynchronous to allow maximum throughput.
//
@@ -49,6 +55,10 @@ class Capturer {
typedef base::Callback<void(scoped_refptr<CaptureData>)>
CaptureCompletedCallback;
+ // CursorShapeChangedCallback is called when the cursor shape has changed.
+ typedef base::Callback<void(scoped_ptr<protocol::CursorShapeInfo>)>
+ CursorShapeChangedCallback;
+
virtual ~Capturer() {};
// Create platform-specific capturer.
@@ -69,7 +79,8 @@ class Capturer {
#endif // defined(OS_LINUX)
// Called at the beginning of a capturing session.
- virtual void Start() = 0;
+ virtual void Start(
+ const CursorShapeChangedCallback& callback) = 0;
// Called at the end of a capturing session.
virtual void Stop() = 0;
diff --git a/remoting/host/capturer_fake.cc b/remoting/host/capturer_fake.cc
index 3aafb1b..cf7ddec 100644
--- a/remoting/host/capturer_fake.cc
+++ b/remoting/host/capturer_fake.cc
@@ -4,6 +4,8 @@
#include "remoting/host/capturer_fake.h"
+#include "remoting/base/capture_data.h"
+
namespace remoting {
// CapturerFake generates a white picture of size kWidth x kHeight with a
@@ -36,7 +38,8 @@ CapturerFake::CapturerFake()
CapturerFake::~CapturerFake() {
}
-void CapturerFake::Start() {
+void CapturerFake::Start(
+ const CursorShapeChangedCallback& callback) {
}
void CapturerFake::Stop() {
diff --git a/remoting/host/capturer_fake.h b/remoting/host/capturer_fake.h
index 3efbb78..3654b98 100644
--- a/remoting/host/capturer_fake.h
+++ b/remoting/host/capturer_fake.h
@@ -21,7 +21,8 @@ class CapturerFake : public Capturer {
virtual ~CapturerFake();
// Capturer interface.
- virtual void Start() OVERRIDE;
+ virtual void Start(
+ const CursorShapeChangedCallback& callback) OVERRIDE;
virtual void Stop() OVERRIDE;
virtual void ScreenConfigurationChanged() OVERRIDE;
virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
diff --git a/remoting/host/capturer_linux.cc b/remoting/host/capturer_linux.cc
index e513bb7..0035b74 100644
--- a/remoting/host/capturer_linux.cc
+++ b/remoting/host/capturer_linux.cc
@@ -7,15 +7,18 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
#include <set>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "remoting/base/capture_data.h"
#include "remoting/host/capturer_helper.h"
#include "remoting/host/differ.h"
#include "remoting/host/x_server_pixel_buffer.h"
+#include "remoting/proto/control.pb.h"
namespace remoting {
@@ -75,7 +78,7 @@ class CapturerLinux : public Capturer {
bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
// Capturer interface.
- virtual void Start() OVERRIDE;
+ virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE;
virtual void Stop() OVERRIDE;
virtual void ScreenConfigurationChanged() OVERRIDE;
virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
@@ -106,6 +109,10 @@ class CapturerLinux : public Capturer {
// previous capture.
CaptureData* CaptureFrame();
+ // Capture the cursor image and call the CursorShapeChangedCallback if it
+ // has been set (using SetCursorShapeChangedCallback).
+ void CaptureCursor();
+
// Synchronize the current buffer with |last_buffer_|, by copying pixels from
// the area of |last_invalid_rects|.
// Note this only works on the assumption that kNumBuffers == 2, as
@@ -129,6 +136,11 @@ class CapturerLinux : public Capturer {
GC gc_;
Window root_window_;
+ // XFixes.
+ bool has_xfixes_;
+ int xfixes_event_base_;
+ int xfixes_error_base_;
+
// XDamage information.
bool use_damage_;
Damage damage_handle_;
@@ -143,6 +155,9 @@ class CapturerLinux : public Capturer {
// recently captured screen.
CapturerHelper helper_;
+ // Callback notified whenever the cursor shape is changed.
+ CursorShapeChangedCallback cursor_shape_changed_callback_;
+
// Capture state.
static const int kNumBuffers = 2;
VideoFrameBuffer buffers_[kNumBuffers];
@@ -168,6 +183,9 @@ CapturerLinux::CapturerLinux()
: display_(NULL),
gc_(NULL),
root_window_(BadValue),
+ has_xfixes_(false),
+ xfixes_event_base_(-1),
+ xfixes_error_base_(-1),
use_damage_(false),
damage_handle_(0),
damage_event_base_(-1),
@@ -208,6 +226,15 @@ bool CapturerLinux::Init() {
return false;
}
+ // Check for XFixes extension. This is required for cursor shape
+ // notifications, and for our use of XDamage.
+ if (XFixesQueryExtension(display_, &xfixes_event_base_,
+ &xfixes_error_base_)) {
+ has_xfixes_ = true;
+ } else {
+ LOG(INFO) << "X server does not support XFixes.";
+ }
+
if (ShouldUseXDamage()) {
InitXDamage();
}
@@ -215,18 +242,22 @@ bool CapturerLinux::Init() {
// Register for changes to the dimensions of the root window.
XSelectInput(display_, root_window_, StructureNotifyMask);
+ if (has_xfixes_) {
+ // Register for changes to the cursor shape.
+ XFixesSelectCursorInput(display_, root_window_,
+ XFixesDisplayCursorNotifyMask);
+ }
+
return true;
}
void CapturerLinux::InitXDamage() {
- // Check for XFixes and XDamage extensions. If both are found then use
- // XDamage to get explicit notifications of on-screen changes.
- int xfixes_event_base;
- int xfixes_error_base;
- if (!XFixesQueryExtension(display_, &xfixes_event_base, &xfixes_error_base)) {
- LOG(INFO) << "X server does not support XFixes.";
+ // Our use of XDamage requires XFixes.
+ if (!has_xfixes_) {
return;
}
+
+ // Check for XDamage extension.
if (!XDamageQueryExtension(display_, &damage_event_base_,
&damage_error_base_)) {
LOG(INFO) << "X server does not support XDamage.";
@@ -258,7 +289,9 @@ void CapturerLinux::InitXDamage() {
LOG(INFO) << "Using XDamage extension.";
}
-void CapturerLinux::Start() {
+void CapturerLinux::Start(
+ const CursorShapeChangedCallback& callback) {
+ cursor_shape_changed_callback_ = callback;
}
void CapturerLinux::Stop() {
@@ -296,8 +329,7 @@ void CapturerLinux::InvalidateFullScreen() {
void CapturerLinux::CaptureInvalidRegion(
const CaptureCompletedCallback& callback) {
- // TODO(lambroslambrou): In the non-DAMAGE case, there should be no need
- // for any X event processing in this class.
+ // Process XEvents for XDamage and cursor shape tracking.
ProcessPendingXEvents();
// Resize the current buffer if there was a recent change of
@@ -326,16 +358,60 @@ void CapturerLinux::ProcessPendingXEvents() {
for (int i = 0; i < events_to_process; i++) {
XNextEvent(display_, &e);
if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) {
- XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e);
+ XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
DCHECK(event->level == XDamageReportNonEmpty);
} else if (e.type == ConfigureNotify) {
ScreenConfigurationChanged();
+ } else if (has_xfixes_ &&
+ e.type == xfixes_event_base_ + XFixesCursorNotify) {
+ XFixesCursorNotifyEvent* cne;
+ cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e);
+ if (cne->subtype == XFixesDisplayCursorNotify) {
+ CaptureCursor();
+ }
} else {
LOG(WARNING) << "Got unknown event type: " << e.type;
}
}
}
+void CapturerLinux::CaptureCursor() {
+ DCHECK(has_xfixes_);
+ if (cursor_shape_changed_callback_.is_null())
+ return;
+
+ XFixesCursorImage* img = XFixesGetCursorImage(display_);
+ if (!img) {
+ return;
+ }
+
+ int width = img->width;
+ int height = img->height;
+ int total_bytes = width * height * kBytesPerPixel;
+
+ scoped_ptr<protocol::CursorShapeInfo> cursor_proto(
+ new protocol::CursorShapeInfo());
+ cursor_proto->set_width(width);
+ cursor_proto->set_height(height);
+ cursor_proto->set_hotspot_x(img->xhot);
+ cursor_proto->set_hotspot_y(img->yhot);
+
+ cursor_proto->mutable_data()->resize(total_bytes);
+ uint8* proto_data = const_cast<uint8*>(reinterpret_cast<const uint8*>(
+ cursor_proto->mutable_data()->data()));
+
+ // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
+ unsigned long* src = img->pixels;
+ uint32* dst = reinterpret_cast<uint32*>(proto_data);
+ uint32* dst_end = dst + (width * height);
+ while (dst < dst_end) {
+ *dst++ = static_cast<uint32>(*src++);
+ }
+ XFree(img);
+
+ cursor_shape_changed_callback_.Run(cursor_proto.Pass());
+}
+
CaptureData* CapturerLinux::CaptureFrame() {
VideoFrameBuffer& buffer = buffers_[current_buffer_];
DataPlanes planes;
diff --git a/remoting/host/capturer_mac.cc b/remoting/host/capturer_mac.cc
index 6c01622..6afbd09 100644
--- a/remoting/host/capturer_mac.cc
+++ b/remoting/host/capturer_mac.cc
@@ -17,9 +17,10 @@
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "base/time.h"
+#include "remoting/base/capture_data.h"
#include "remoting/base/util.h"
#include "remoting/host/capturer_helper.h"
-
+#include "remoting/proto/control.pb.h"
namespace remoting {
@@ -158,7 +159,7 @@ class CapturerMac : public Capturer {
bool Init();
// Capturer interface.
- virtual void Start() OVERRIDE;
+ virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE;
virtual void Stop() OVERRIDE;
virtual void ScreenConfigurationChanged() OVERRIDE;
virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
@@ -206,6 +207,9 @@ class CapturerMac : public Capturer {
// recently captured screen.
CapturerHelper helper_;
+ // Callback notified whenever the cursor shape is changed.
+ CursorShapeChangedCallback cursor_shape_changed_callback_;
+
// The current buffer with valid data for reading.
int current_buffer_;
@@ -306,7 +310,10 @@ void CapturerMac::ReleaseBuffers() {
}
}
-void CapturerMac::Start() {
+void CapturerMac::Start(
+ const CursorShapeChangedCallback& callback) {
+ cursor_shape_changed_callback_ = callback;
+
// Create power management assertions to wake the display and prevent it from
// going to sleep on user idle.
IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep,
diff --git a/remoting/host/capturer_mac_unittest.cc b/remoting/host/capturer_mac_unittest.cc
index 39cd994..0fbdae0 100644
--- a/remoting/host/capturer_mac_unittest.cc
+++ b/remoting/host/capturer_mac_unittest.cc
@@ -11,6 +11,8 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
+#include "remoting/base/capture_data.h"
+#include "remoting/proto/control.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
@@ -85,9 +87,25 @@ void CapturerCallback2::CaptureDoneCallback(
EXPECT_EQ(0, planes.strides[2]);
}
+class CursorCallback {
+ public:
+ CursorCallback() { }
+ void CursorShapeChangedCallback(
+ scoped_ptr<protocol::CursorShapeInfo> cursor_data);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CursorCallback);
+};
+
+void CursorCallback::CursorShapeChangedCallback(
+ scoped_ptr<protocol::CursorShapeInfo> cursor_data) {
+}
+
TEST_F(CapturerMacTest, Capture) {
SCOPED_TRACE("");
- capturer_->Start();
+ CursorCallback cursor_callback;
+ capturer_->Start(base::Bind(&CursorCallback::CursorShapeChangedCallback,
+ base::Unretained(&cursor_callback)));
// Check that we get an initial full-screen updated.
CapturerCallback1 callback1;
capturer_->CaptureInvalidRegion(base::Bind(
diff --git a/remoting/host/capturer_win.cc b/remoting/host/capturer_win.cc
index e9b6526..6ec2d60 100644
--- a/remoting/host/capturer_win.cc
+++ b/remoting/host/capturer_win.cc
@@ -12,10 +12,12 @@
#include "base/native_library.h"
#include "base/string16.h"
#include "base/stringize_macros.h"
+#include "remoting/base/capture_data.h"
#include "remoting/host/capturer_helper.h"
#include "remoting/host/desktop_win.h"
#include "remoting/host/differ.h"
#include "remoting/host/scoped_thread_desktop_win.h"
+#include "remoting/proto/control.pb.h"
namespace remoting {
@@ -39,7 +41,7 @@ class CapturerGdi : public Capturer {
virtual ~CapturerGdi();
// Capturer interface.
- virtual void Start() OVERRIDE;
+ virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE;
virtual void Stop() OVERRIDE;
virtual void ScreenConfigurationChanged() OVERRIDE;
virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
@@ -96,6 +98,9 @@ class CapturerGdi : public Capturer {
// recently captured screen.
CapturerHelper helper_;
+ // Callback notified whenever the cursor shape is changed.
+ CursorShapeChangedCallback cursor_shape_changed_callback_;
+
// There are two buffers for the screen images, as required by Capturer.
static const int kNumBuffers = 2;
VideoFrameBuffer buffers_[kNumBuffers];
@@ -214,7 +219,10 @@ void CapturerGdi::ReleaseBuffers() {
}
}
-void CapturerGdi::Start() {
+void CapturerGdi::Start(
+ const CursorShapeChangedCallback& callback) {
+ cursor_shape_changed_callback_ = callback;
+
// Load dwmapi.dll dynamically since it is not available on XP.
if (dwmapi_library_ == NULL) {
std::string error;
diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h
index 79e50a0..569b324 100644
--- a/remoting/host/host_mock_objects.h
+++ b/remoting/host/host_mock_objects.h
@@ -24,7 +24,7 @@ class MockCapturer : public Capturer {
MockCapturer();
virtual ~MockCapturer();
- MOCK_METHOD0(Start, void());
+ MOCK_METHOD1(Start, void(const CursorShapeChangedCallback& callback));
MOCK_METHOD0(Stop, void());
MOCK_METHOD0(ScreenConfigurationChanged, void());
MOCK_CONST_METHOD0(pixel_format, media::VideoFrame::Format());
diff --git a/remoting/host/screen_recorder.cc b/remoting/host/screen_recorder.cc
index ab96a53..e7b414c 100644
--- a/remoting/host/screen_recorder.cc
+++ b/remoting/host/screen_recorder.cc
@@ -16,6 +16,7 @@
#include "base/time.h"
#include "remoting/base/capture_data.h"
#include "remoting/proto/control.pb.h"
+#include "remoting/proto/internal.pb.h"
#include "remoting/proto/video.pb.h"
#include "remoting/protocol/client_stub.h"
#include "remoting/protocol/connection_to_client.h"
@@ -142,7 +143,9 @@ void ScreenRecorder::DoStart() {
return;
}
- capturer()->Start();
+ capturer()->Start(
+ base::Bind(&ScreenRecorder::CursorShapeChangedCallback, this));
+
capture_timer_.reset(new base::OneShotTimer<ScreenRecorder>());
// Capture first frame immedately.
@@ -214,6 +217,18 @@ void ScreenRecorder::CaptureDoneCallback(
FROM_HERE, base::Bind(&ScreenRecorder::DoEncode, this, capture_data));
}
+void ScreenRecorder::CursorShapeChangedCallback(
+ scoped_ptr<protocol::CursorShapeInfo> cursor_shape) {
+ DCHECK_EQ(capture_loop_, MessageLoop::current());
+
+ if (!is_recording())
+ return;
+
+ network_loop_->PostTask(
+ FROM_HERE, base::Bind(&ScreenRecorder::DoSendCursorShape, this,
+ base::Passed(cursor_shape.Pass())));
+}
+
void ScreenRecorder::DoFinishOneRecording() {
DCHECK_EQ(capture_loop_, MessageLoop::current());
@@ -281,6 +296,18 @@ void ScreenRecorder::DoStopOnNetworkThread(const base::Closure& done_task) {
this, done_task));
}
+void ScreenRecorder::DoSendCursorShape(
+ scoped_ptr<protocol::CursorShapeInfo> cursor_shape) {
+ DCHECK(network_loop_->BelongsToCurrentThread());
+
+ if (network_stopped_ || connections_.empty())
+ return;
+
+ // TODO(sergeyu): Currently we send the data only to the first
+ // connection. Send it to all connections if necessary.
+ connections_.front()->client_stub()->SetCursorShape(*cursor_shape);
+}
+
// Encoder thread --------------------------------------------------------------
void ScreenRecorder::DoEncode(
diff --git a/remoting/host/screen_recorder.h b/remoting/host/screen_recorder.h
index 64e079f..462ade7 100644
--- a/remoting/host/screen_recorder.h
+++ b/remoting/host/screen_recorder.h
@@ -27,6 +27,7 @@ namespace remoting {
namespace protocol {
class ConnectionToClient;
+class CursorShapeInfo;
} // namespace protocol
class CaptureData;
@@ -123,6 +124,8 @@ class ScreenRecorder : public base::RefCountedThreadSafe<ScreenRecorder> {
void DoCapture();
void CaptureDoneCallback(scoped_refptr<CaptureData> capture_data);
+ void CursorShapeChangedCallback(
+ scoped_ptr<protocol::CursorShapeInfo> cursor_data);
void DoFinishOneRecording();
void DoInvalidateFullScreen();
@@ -140,6 +143,9 @@ class ScreenRecorder : public base::RefCountedThreadSafe<ScreenRecorder> {
// each last packet in a frame.
void VideoFrameSentCallback();
+ // Send updated cursor shape to client.
+ void DoSendCursorShape(scoped_ptr<protocol::CursorShapeInfo> cursor_shape);
+
// Encoder thread -----------------------------------------------------------
void DoEncode(scoped_refptr<CaptureData> capture_data);
diff --git a/remoting/host/screen_recorder_unittest.cc b/remoting/host/screen_recorder_unittest.cc
index 3457444..b7d6b3b 100644
--- a/remoting/host/screen_recorder_unittest.cc
+++ b/remoting/host/screen_recorder_unittest.cc
@@ -128,7 +128,7 @@ TEST_F(ScreenRecorderTest, StartAndStop) {
planes.strides[i] = kWidth * 4;
}
- Expectation capturer_start = EXPECT_CALL(capturer_, Start());
+ Expectation capturer_start = EXPECT_CALL(capturer_, Start(_));
SkISize size(SkISize::Make(kWidth, kHeight));
scoped_refptr<CaptureData> data(new CaptureData(planes, size, kFormat));
diff --git a/remoting/proto/control.proto b/remoting/proto/control.proto
index 6c00389..ebe0358 100644
--- a/remoting/proto/control.proto
+++ b/remoting/proto/control.proto
@@ -20,3 +20,16 @@ message VideoControl {
// Enables the video channel if true, pauses if false.
optional bool enable = 1;
}
+
+message CursorShapeInfo {
+ // Width, height (in screen pixels) of the cursor.
+ optional int32 width = 1;
+ optional int32 height = 2;
+
+ // X,Y coordinates (relative to upper-left corner) of the cursor hotspot.
+ optional int32 hotspot_x = 3;
+ optional int32 hotspot_y = 4;
+
+ // Cursor pixmap data in 32-bit BGRA format.
+ optional bytes data = 5;
+}
diff --git a/remoting/proto/internal.proto b/remoting/proto/internal.proto
index 06f9585..4e68d7d 100644
--- a/remoting/proto/internal.proto
+++ b/remoting/proto/internal.proto
@@ -18,6 +18,7 @@ package remoting.protocol;
message ControlMessage {
optional ClipboardEvent clipboard_event = 1;
optional ClientDimensions client_dimensions = 2;
+ optional CursorShapeInfo cursor_shape = 4;
optional VideoControl video_control = 3;
}
diff --git a/remoting/protocol/client_control_dispatcher.cc b/remoting/protocol/client_control_dispatcher.cc
index 7abf3b6..7363de4 100644
--- a/remoting/protocol/client_control_dispatcher.cc
+++ b/remoting/protocol/client_control_dispatcher.cc
@@ -62,6 +62,8 @@ void ClientControlDispatcher::OnMessageReceived(
if (message->has_clipboard_event()) {
clipboard_stub_->InjectClipboardEvent(message->clipboard_event());
+ } else if (message->has_cursor_shape()) {
+ client_stub_->SetCursorShape(message->cursor_shape());
} else {
LOG(WARNING) << "Unknown control message received.";
}
diff --git a/remoting/protocol/client_control_dispatcher.h b/remoting/protocol/client_control_dispatcher.h
index e60a170..1cdc3f9 100644
--- a/remoting/protocol/client_control_dispatcher.h
+++ b/remoting/protocol/client_control_dispatcher.h
@@ -9,6 +9,7 @@
#include "remoting/protocol/buffered_socket_writer.h"
#include "remoting/protocol/channel_dispatcher_base.h"
#include "remoting/protocol/clipboard_stub.h"
+#include "remoting/protocol/cursor_shape_stub.h"
#include "remoting/protocol/host_stub.h"
#include "remoting/protocol/message_reader.h"
@@ -20,8 +21,8 @@ class ControlMessage;
class Session;
// ClientControlDispatcher dispatches incoming messages on the control
-// channel to ClientStub or ClipboardStub, and also implements ClipboardStub
-// and HostStub for outgoing messages.
+// channel to ClientStub, ClipboardStub or CursorShapeStub.
+// It also implements ClipboardStub and HostStub for outgoing messages.
class ClientControlDispatcher : public ChannelDispatcherBase,
public ClipboardStub,
public HostStub {
diff --git a/remoting/protocol/client_stub.h b/remoting/protocol/client_stub.h
index 119132b..ed4681d 100644
--- a/remoting/protocol/client_stub.h
+++ b/remoting/protocol/client_stub.h
@@ -12,11 +12,13 @@
#include "base/basictypes.h"
#include "remoting/protocol/clipboard_stub.h"
+#include "remoting/protocol/cursor_shape_stub.h"
namespace remoting {
namespace protocol {
-class ClientStub : public ClipboardStub {
+class ClientStub : public ClipboardStub,
+ public CursorShapeStub {
public:
ClientStub() {}
virtual ~ClientStub() {}
diff --git a/remoting/protocol/connection_to_client.h b/remoting/protocol/connection_to_client.h
index 7615d2d..f786e54 100644
--- a/remoting/protocol/connection_to_client.h
+++ b/remoting/protocol/connection_to_client.h
@@ -83,9 +83,10 @@ class ConnectionToClient : public base::NonThreadSafe {
// Send encoded update stream data to the viewer.
virtual VideoStub* video_stub();
- // Return pointer to ClientStub.
+ // Send control data to the viewer/client.
virtual ClientStub* client_stub();
+ // Stubs for receiving data from the client.
// These three setters should be called before Init().
virtual void set_clipboard_stub(ClipboardStub* clipboard_stub);
virtual void set_host_stub(HostStub* host_stub);
diff --git a/remoting/protocol/connection_to_host.h b/remoting/protocol/connection_to_host.h
index aa0bcda..11de8170 100644
--- a/remoting/protocol/connection_to_host.h
+++ b/remoting/protocol/connection_to_host.h
@@ -80,6 +80,7 @@ class ConnectionToHost : public SignalStrategy::Listener,
virtual const SessionConfig& config();
+ // Stubs for sending data to the host.
virtual ClipboardStub* clipboard_stub();
virtual HostStub* host_stub();
virtual InputStub* input_stub();
diff --git a/remoting/protocol/cursor_shape_stub.h b/remoting/protocol/cursor_shape_stub.h
new file mode 100644
index 0000000..3c96cbe
--- /dev/null
+++ b/remoting/protocol/cursor_shape_stub.h
@@ -0,0 +1,31 @@
+// 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.
+
+// Interface for an object that receives cursor shape events.
+
+#ifndef REMOTING_PROTOCOL_CURSOR_SHAPE_STUB_H_
+#define REMOTING_PROTOCOL_CURSOR_SHAPE_STUB_H_
+
+#include "base/basictypes.h"
+
+namespace remoting {
+namespace protocol {
+
+class CursorShapeInfo;
+
+class CursorShapeStub {
+ public:
+ CursorShapeStub() {}
+ virtual ~CursorShapeStub() {}
+
+ virtual void SetCursorShape(const CursorShapeInfo& cursor_shape) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CursorShapeStub);
+};
+
+} // namespace protocol
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_CURSOR_SHAPE_STUB_H_
diff --git a/remoting/protocol/host_control_dispatcher.cc b/remoting/protocol/host_control_dispatcher.cc
index 9e6b745..ac6d80f 100644
--- a/remoting/protocol/host_control_dispatcher.cc
+++ b/remoting/protocol/host_control_dispatcher.cc
@@ -38,6 +38,13 @@ void HostControlDispatcher::InjectClipboardEvent(const ClipboardEvent& event) {
writer_.Write(SerializeAndFrameMessage(message), base::Closure());
}
+void HostControlDispatcher::SetCursorShape(
+ const CursorShapeInfo& cursor_shape) {
+ ControlMessage message;
+ message.mutable_cursor_shape()->CopyFrom(cursor_shape);
+ writer_.Write(SerializeAndFrameMessage(message), base::Closure());
+}
+
void HostControlDispatcher::OnMessageReceived(
scoped_ptr<ControlMessage> message, const base::Closure& done_task) {
DCHECK(clipboard_stub_);
diff --git a/remoting/protocol/host_control_dispatcher.h b/remoting/protocol/host_control_dispatcher.h
index aff36c6..080d6dd 100644
--- a/remoting/protocol/host_control_dispatcher.h
+++ b/remoting/protocol/host_control_dispatcher.h
@@ -10,6 +10,7 @@
#include "remoting/protocol/channel_dispatcher_base.h"
#include "remoting/protocol/client_stub.h"
#include "remoting/protocol/clipboard_stub.h"
+#include "remoting/protocol/cursor_shape_stub.h"
#include "remoting/protocol/message_reader.h"
namespace net {
@@ -24,16 +25,20 @@ class HostStub;
class Session;
// HostControlDispatcher dispatches incoming messages on the control
-// channel to HostStub or ClipboardStub, and also implements ClientStub for
-// outgoing messages.
-class HostControlDispatcher : public ChannelDispatcherBase, public ClientStub {
+// channel to HostStub or ClipboardStub, and also implements ClientStub and
+// CursorShapeStub for outgoing messages.
+class HostControlDispatcher : public ChannelDispatcherBase,
+ public ClientStub {
public:
HostControlDispatcher();
virtual ~HostControlDispatcher();
- // ClipboardStub implementation.
+ // ClipboardStub implementation for sending clipboard data to client.
virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
+ // CursorShapeStub implementation for sending cursor shape to client.
+ virtual void SetCursorShape(const CursorShapeInfo& cursor_shape) OVERRIDE;
+
// Sets the ClipboardStub that will be called for each incoming clipboard
// message. |clipboard_stub| must outlive this object.
void set_clipboard_stub(ClipboardStub* clipboard_stub) {
diff --git a/remoting/protocol/protocol_mock_objects.h b/remoting/protocol/protocol_mock_objects.h
index c36fbe1..2ebbf58 100644
--- a/remoting/protocol/protocol_mock_objects.h
+++ b/remoting/protocol/protocol_mock_objects.h
@@ -117,8 +117,12 @@ class MockClientStub : public ClientStub {
MockClientStub();
virtual ~MockClientStub();
+ // ClipboardStub mock implementation.
MOCK_METHOD1(InjectClipboardEvent, void(const ClipboardEvent& event));
+ // CursorShapeStub mock implementation.
+ MOCK_METHOD1(SetCursorShape, void(const CursorShapeInfo& cursor_shape));
+
private:
DISALLOW_COPY_AND_ASSIGN(MockClientStub);
};