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..33361a611802d35e6417edbc9e5650babc445ddf 100644 |
| --- a/remoting/host/capturer_linux.cc |
| +++ b/remoting/host/capturer_linux.cc |
| @@ -7,12 +7,15 @@ |
| #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/base/cursor_shape_data.h" |
| #include "remoting/host/capturer_helper.h" |
| #include "remoting/host/differ.h" |
| #include "remoting/host/x_server_pixel_buffer.h" |
| @@ -77,6 +80,8 @@ class CapturerLinux : public Capturer { |
| // Capturer interface. |
| virtual void Start() OVERRIDE; |
| virtual void Stop() OVERRIDE; |
| + virtual void SetCursorShapeChangedCallback( |
| + const CursorShapeChangedCallback& callback) OVERRIDE; |
| virtual void ScreenConfigurationChanged() OVERRIDE; |
| virtual media::VideoFrame::Format pixel_format() const OVERRIDE; |
| virtual void ClearInvalidRegion() OVERRIDE; |
| @@ -106,6 +111,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 +138,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 +157,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 +185,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 +228,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 +244,21 @@ 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_) |
|
Wez
2012/05/23 00:01:57
nit: Use {} consistently with the other if()s belo
garykac
2012/05/26 01:58:01
Done.
|
| return; |
| - } |
| + |
| + // Check for XDamage extension. |
| if (!XDamageQueryExtension(display_, &damage_event_base_, |
| &damage_error_base_)) { |
| LOG(INFO) << "X server does not support XDamage."; |
| @@ -264,6 +296,11 @@ void CapturerLinux::Start() { |
| void CapturerLinux::Stop() { |
| } |
| +void CapturerLinux::SetCursorShapeChangedCallback( |
| + const CursorShapeChangedCallback& callback) { |
| + cursor_shape_changed_callback_ = callback; |
| +} |
| + |
| void CapturerLinux::ScreenConfigurationChanged() { |
| last_buffer_ = NULL; |
| for (int i = 0; i < kNumBuffers; ++i) { |
| @@ -296,8 +333,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 +362,59 @@ 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) |
|
Wez
2012/05/23 00:01:57
nit: Here and above, use {} consistently with othe
garykac
2012/05/26 01:58:01
Done.
|
| + return; |
| + |
| + SkISize size, hotspot; |
| + size.set(img->width, img->height); |
| + hotspot.set(img->xhot, img->yhot); |
| + const int bytes_per_pixel = 4; |
| + int total_bytes = img->width * img->height * bytes_per_pixel; |
|
Wez
2012/05/23 00:01:57
nit: Rather than set these up here, why not use e.
garykac
2012/05/26 01:58:01
Done.
|
| + scoped_array<uint8> data(new uint8[total_bytes]); |
| + |
| + // Xlib stores 32-bit data in longs, even if longs are 64-bits long. |
|
Wez
2012/05/23 00:01:57
nit: I'd recommend simplifying this to use the slo
garykac
2012/05/26 01:58:01
Done.
|
| + if (sizeof(unsigned long) == sizeof(uint32)) { |
| + // Longs are 32-bits, do a simple memcpy. |
| + memcpy(data.get(), img->pixels, total_bytes); |
| + } else { |
| + // Longs are 64-bits, copy each long into a uint32. |
| + unsigned long* src = img->pixels; |
| + uint32* dst = reinterpret_cast<uint32*>(data.get()); |
| + uint32* dst_end = dst + (img->width * img->height); |
| + while (dst < dst_end) { |
| + *dst++ = *src++; |
| + } |
| + } |
| + XFree(img); |
| + |
| + scoped_refptr<CursorShapeData> cursor_data = new CursorShapeData(); |
| + cursor_data->Initialize(data.Pass(), size, hotspot, bytes_per_pixel); |
| + cursor_shape_changed_callback_.Run(cursor_data); |
| +} |
| + |
| CaptureData* CapturerLinux::CaptureFrame() { |
| VideoFrameBuffer& buffer = buffers_[current_buffer_]; |
| DataPlanes planes; |