Index: remoting/host/capturer_linux.cc |
diff --git a/remoting/host/capturer_linux.cc b/remoting/host/capturer_linux.cc |
index e513bb7c8932492f9dcf14c8fb061f885bebc253..0035b74ca17383dabfc3f5b009960fe746f2c8a1 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; |