Chromium Code Reviews| Index: remoting/host/capturer_linux.cc |
| diff --git a/remoting/host/capturer_linux.cc b/remoting/host/capturer_linux.cc |
| index e513bb7c8932492f9dcf14c8fb061f885bebc253..904cd40b3598ff01388ac93e1ded105e28038651 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,62 @@ 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; |
| + SkISize size = SkISize::Make(width, height); |
| + SkISize hotspot = SkISize::Make(img->xhot, img->yhot); |
| + 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++ = *src++; |
|
Wez
2012/05/30 23:42:42
Do you need an explicit down-cast to avoid compile
garykac
2012/05/31 00:29:41
There weren't any warnings, but I added a static_c
|
| + } |
| + XFree(img); |
| + |
| + cursor_shape_changed_callback_.Run(cursor_proto.Pass()); |
| +} |
| + |
| CaptureData* CapturerLinux::CaptureFrame() { |
| VideoFrameBuffer& buffer = buffers_[current_buffer_]; |
| DataPlanes planes; |