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; |