diff options
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); }; |