summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/host/capturer_linux.cc134
-rw-r--r--remoting/host/x_server_pixel_buffer.cc206
-rw-r--r--remoting/host/x_server_pixel_buffer.h63
-rw-r--r--remoting/remoting.gyp8
4 files changed, 340 insertions, 71 deletions
diff --git a/remoting/host/capturer_linux.cc b/remoting/host/capturer_linux.cc
index a168358..db0ffc4 100644
--- a/remoting/host/capturer_linux.cc
+++ b/remoting/host/capturer_linux.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -11,32 +11,10 @@
#include <set>
#include "remoting/base/types.h"
+#include "remoting/host/x_server_pixel_buffer.h"
namespace remoting {
-static int IndexOfLowestBit(unsigned int mask) {
- int i = 0;
-
- // Extra-special do-while premature optimization, just to make dmaclach@
- // happy.
- do {
- if (mask & 1) {
- return i;
- }
- mask >>= 1;
- ++i;
- } while (mask);
-
- NOTREACHED() << "mask should never be 0.";
- return 0;
-}
-
-static bool IsRgb32(XImage* image) {
- return (IndexOfLowestBit(image->red_mask) == 16) &&
- (IndexOfLowestBit(image->green_mask) == 8) &&
- (IndexOfLowestBit(image->blue_mask) == 0);
-}
-
// Private Implementation pattern to avoid leaking the X11 types into the header
// file.
class CapturerLinuxPimpl {
@@ -53,10 +31,8 @@ class CapturerLinuxPimpl {
void DeinitXlib();
// We expose two forms of blitting to handle variations in the pixel format.
// In FastBlit, the operation is effectively a memcpy.
- void FastBlit(XImage* image, int dest_x, int dest_y,
- CaptureData* capture_data);
- void SlowBlit(XImage* image, int dest_x, int dest_y,
- CaptureData* capture_data);
+ void FastBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
+ void SlowBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
static const int kBytesPerPixel = 4;
@@ -76,6 +52,9 @@ class CapturerLinuxPimpl {
int damage_event_base_;
int damage_error_base_;
+ // Access to the X Server's pixel buffer.
+ XServerPixelBuffer x_server_pixel_buffer_;
+
// Capture state.
uint8* buffers_[CapturerLinux::kNumBuffers];
int stride_;
@@ -150,6 +129,8 @@ bool CapturerLinuxPimpl::Init() {
return false;
}
+ x_server_pixel_buffer_.Init(display_);
+
root_window_ = RootWindow(display_, DefaultScreen(display_));
if (root_window_ == BadValue) {
LOG(ERROR) << "Unable to get the root window";
@@ -273,21 +254,18 @@ void CapturerLinuxPimpl::CaptureRects(
for (InvalidRects::const_iterator it = rects.begin();
it != rects.end();
++it) {
- XImage* image = XGetImage(display_, root_window_, it->x(), it->y(),
- it->width(), it->height(), AllPlanes, ZPixmap);
-
+ uint8* image = x_server_pixel_buffer_.CaptureRect(*it);
// Check if we can fastpath the blit.
- if ((image->depth == 24 || image->depth == 32) &&
- image->bits_per_pixel == 32 &&
- IsRgb32(image)) {
+ int depth = x_server_pixel_buffer_.GetDepth();
+ int bpp = x_server_pixel_buffer_.GetBitsPerPixel();
+ bool is_rgb = x_server_pixel_buffer_.IsRgb();
+ if ((depth == 24 || depth == 32) && bpp == 32 && is_rgb) {
VLOG(3) << "Fast blitting";
- FastBlit(image, it->x(), it->y(), capture_data);
+ FastBlit(image, *it, capture_data);
} else {
VLOG(3) << "Slow blitting";
- SlowBlit(image, it->x(), it->y(), capture_data);
+ SlowBlit(image, *it, capture_data);
}
-
- XDestroyImage(image);
}
// TODO(ajwong): We should only repair the rects that were copied!
@@ -314,60 +292,76 @@ void CapturerLinuxPimpl::DeinitXlib() {
}
}
-void CapturerLinuxPimpl::FastBlit(XImage* image, int dest_x, int dest_y,
+void CapturerLinuxPimpl::FastBlit(uint8* image, const gfx::Rect& rect,
CaptureData* capture_data) {
- uint8* src_pos = reinterpret_cast<uint8*>(image->data);
+ uint8* src_pos = image;
+ int src_stride = x_server_pixel_buffer_.GetStride();
+ int dst_x = rect.x(), dst_y = rect.y();
DataPlanes planes = capture_data->data_planes();
uint8* dst_buffer = planes.data[0];
const int dst_stride = planes.strides[0];
- const int src_stride = image->bytes_per_line;
-
- uint8* dst_pos = dst_buffer + dst_stride * dest_y;
- dst_pos += dest_x * kBytesPerPixel;
- for (int y = 0; y < image->height; ++y) {
- memcpy(dst_pos, src_pos, image->width * kBytesPerPixel);
+ uint8* dst_pos = dst_buffer + dst_stride * dst_y;
+ dst_pos += dst_x * kBytesPerPixel;
+ int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel;
+ for (int y = 0; y < height; ++y) {
+ memcpy(dst_pos, src_pos, row_bytes);
src_pos += src_stride;
dst_pos += dst_stride;
}
}
-void CapturerLinuxPimpl::SlowBlit(XImage* image, int dest_x, int dest_y,
+void CapturerLinuxPimpl::SlowBlit(uint8* image, const gfx::Rect& rect,
CaptureData* capture_data) {
DataPlanes planes = capture_data->data_planes();
uint8* dst_buffer = planes.data[0];
const int dst_stride = planes.strides[0];
-
- unsigned int red_shift = IndexOfLowestBit(image->red_mask);
- unsigned int blue_shift = IndexOfLowestBit(image->blue_mask);
- unsigned int green_shift = IndexOfLowestBit(image->green_mask);
-
- unsigned int max_red = image->red_mask >> red_shift;
- unsigned int max_blue = image->blue_mask >> blue_shift;
- unsigned int max_green = image->green_mask >> green_shift;
-
- // Produce an upside-down image.
- uint8* dst_pos = dst_buffer + dst_stride * (height_ - dest_y - 1);
- dst_pos += dest_x * kBytesPerPixel;
- // TODO(jamiewalch): Optimize, perhaps using MMX code or by converting to
+ int src_stride = x_server_pixel_buffer_.GetStride();
+ int dst_x = rect.x(), dst_y = rect.y();
+ int width = rect.width(), height = rect.height();
+
+ unsigned int red_mask = x_server_pixel_buffer_.GetRedMask();
+ unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask();
+ unsigned int green_mask = x_server_pixel_buffer_.GetGreenMask();
+ unsigned int red_shift = x_server_pixel_buffer_.GetRedShift();
+ unsigned int blue_shift = x_server_pixel_buffer_.GetBlueShift();
+ unsigned int green_shift = x_server_pixel_buffer_.GetGreenShift();
+
+ unsigned int max_red = red_mask >> red_shift;
+ unsigned int max_blue = blue_mask >> blue_shift;
+ unsigned int max_green = green_mask >> green_shift;
+
+ unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel();
+
+ uint8* dst_pos = dst_buffer + dst_stride * dst_y;
+ uint8* src_pos = image;
+ dst_pos += dst_x * kBytesPerPixel;
+ // TODO(hclam): Optimize, perhaps using MMX code or by converting to
// YUV directly
- for (int y = 0; y < image->height; y++) {
+ for (int y = 0; y < height; y++) {
uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
- for (int x = 0; x < image->width; x++) {
- unsigned long pixel = XGetPixel(image, x, y);
- uint32_t r = (((pixel & image->red_mask) >> red_shift) * max_red) / 255;
- uint32_t g =
- (((pixel & image->green_mask) >> green_shift) * max_blue) / 255;
- uint32_t b =
- (((pixel & image->blue_mask) >> blue_shift) * max_green) / 255;
-
+ uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
+ uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
+ for (int x = 0; x < width; x++) {
+ // Dereference through an appropriately-aligned pointer.
+ uint32_t pixel;
+ if (bits_per_pixel == 32)
+ pixel = src_pos_32[x];
+ else if (bits_per_pixel == 16)
+ pixel = src_pos_16[x];
+ else
+ pixel = src_pos[x];
+ uint32_t r = (((pixel & red_mask) >> red_shift) * 255) / max_red;
+ uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue;
+ uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green;
// Write as 32-bit RGB.
dst_pos_32[x] = r << 16 | g << 8 | b;
}
- dst_pos -= dst_stride;
+ dst_pos += dst_stride;
+ src_pos += src_stride;
}
}
diff --git a/remoting/host/x_server_pixel_buffer.cc b/remoting/host/x_server_pixel_buffer.cc
new file mode 100644
index 0000000..b93c40f
--- /dev/null
+++ b/remoting/host/x_server_pixel_buffer.cc
@@ -0,0 +1,206 @@
+// Copyright (c) 2011 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.
+
+#include "remoting/host/x_server_pixel_buffer.h"
+
+#include <gdk/gdk.h>
+#include <sys/shm.h>
+
+#include "base/logging.h"
+
+namespace remoting {
+
+XServerPixelBuffer::XServerPixelBuffer()
+ : display_(NULL), root_window_(0), x_image_(NULL),
+ shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) {
+}
+
+XServerPixelBuffer::~XServerPixelBuffer() {
+ if (x_image_)
+ XDestroyImage(x_image_);
+ if (shm_pixmap_)
+ XFreePixmap(display_, shm_pixmap_);
+ if (shm_gc_)
+ XFreeGC(display_, shm_gc_);
+ DestroyShmSegmentInfo();
+}
+
+void XServerPixelBuffer::Init(Display* display) {
+ display_ = display;
+ int default_screen = DefaultScreen(display_);
+ root_window_ = RootWindow(display_, default_screen);
+ InitShm(default_screen);
+}
+
+void XServerPixelBuffer::InitShm(int screen) {
+ XWindowAttributes root_attr;
+ XGetWindowAttributes(display_, root_window_, &root_attr);
+ int width = root_attr.width, height = root_attr.height;
+ Visual* default_visual = DefaultVisual(display_, screen);
+ int default_depth = DefaultDepth(display_, screen);
+
+ int major, minor;
+ Bool havePixmaps;
+ if (!XShmQueryVersion(display_, &major, &minor, &havePixmaps))
+ // Shared memory not supported. CaptureRect will use the XImage API instead.
+ return;
+
+ bool using_shm = false;
+ shm_segment_info_ = new XShmSegmentInfo;
+ shm_segment_info_->shmid = -1;
+ shm_segment_info_->shmaddr = reinterpret_cast<char*>(-1);
+ shm_segment_info_->readOnly = False;
+ x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap,
+ 0, shm_segment_info_, width, height);
+ if (x_image_) {
+ shm_segment_info_->shmid = shmget(
+ IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height,
+ IPC_CREAT | 0666);
+ if (shm_segment_info_->shmid != -1) {
+ shm_segment_info_->shmaddr = x_image_->data =
+ reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0));
+ if (x_image_->data != reinterpret_cast<char*>(-1)) {
+ gdk_error_trap_push();
+ using_shm = XShmAttach(display_, shm_segment_info_);
+ XSync(display_, False);
+ if (gdk_error_trap_pop() != 0)
+ using_shm = false;
+ }
+ }
+ }
+
+ if (!using_shm) {
+ VLOG(1) << "Not using shared memory.";
+ DestroyShmSegmentInfo();
+ if (x_image_) {
+ XDestroyImage(x_image_);
+ x_image_ = 0;
+ }
+ return;
+ }
+
+ if (havePixmaps)
+ havePixmaps = InitPixmaps(width, height, default_depth);
+
+ shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
+ shm_segment_info_->shmid = -1;
+
+ VLOG(1) << "Using X shared memory extension v" << major << "." << minor
+ << " with" << (havePixmaps?"":"out") << " pixmaps.";
+}
+
+bool XServerPixelBuffer::InitPixmaps(int width, int height, int depth) {
+ if (XShmPixmapFormat(display_) != ZPixmap)
+ return false;
+
+ gdk_error_trap_push();
+ shm_pixmap_ = XShmCreatePixmap(display_, root_window_,
+ shm_segment_info_->shmaddr,
+ shm_segment_info_,
+ width, height, depth);
+ XSync(display_, False);
+ if (gdk_error_trap_pop() != 0) {
+ // |shm_pixmap_| is not not valid because the request was not processed
+ // by the X Server, so zero it.
+ shm_pixmap_ = 0;
+ return false;
+ }
+
+ gdk_error_trap_push();
+ XGCValues shm_gc_values;
+ shm_gc_values.subwindow_mode = IncludeInferiors;
+ shm_gc_values.graphics_exposures = False;
+ shm_gc_ = XCreateGC(display_, root_window_,
+ GCSubwindowMode | GCGraphicsExposures,
+ &shm_gc_values);
+ XSync(display_, False);
+ if (gdk_error_trap_pop() != 0) {
+ XFreePixmap(display_, shm_pixmap_);
+ shm_pixmap_ = 0;
+ shm_gc_ = 0; // See shm_pixmap_ comment above.
+ return false;
+ }
+
+ return true;
+}
+
+void XServerPixelBuffer::DestroyShmSegmentInfo() {
+ if (shm_segment_info_) {
+ if (shm_segment_info_->shmaddr != reinterpret_cast<char*>(-1))
+ shmdt(shm_segment_info_->shmaddr);
+ if (shm_segment_info_->shmid != -1)
+ shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
+ delete shm_segment_info_;
+ shm_segment_info_ = NULL;
+ }
+}
+
+uint8* XServerPixelBuffer::CaptureRect(const gfx::Rect& rect) {
+ if (shm_segment_info_) {
+ if (shm_pixmap_) {
+ XCopyArea(display_, root_window_, shm_pixmap_, shm_gc_,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x(), rect.y());
+ XSync(display_, False);
+ } else {
+ // XShmGetImage can fail if the display is being reconfigured.
+ gdk_error_trap_push();
+ XShmGetImage(display_, root_window_, x_image_, 0, 0, AllPlanes);
+ gdk_error_trap_pop();
+ }
+ return reinterpret_cast<uint8*>(x_image_->data) +
+ rect.y() * x_image_->bytes_per_line +
+ rect.x() * x_image_->bits_per_pixel / 8;
+ } else {
+ // XGetSubImage would allow cleaner code here, (in particular,
+ // CaptureComplete could be removed) but XGetImage is faster.
+ if (x_image_)
+ XDestroyImage(x_image_);
+ x_image_ = XGetImage(display_, root_window_, rect.x(), rect.y(),
+ rect.width(), rect.height(), AllPlanes, ZPixmap);
+ return reinterpret_cast<uint8*>(x_image_->data);
+ }
+}
+
+int XServerPixelBuffer::GetStride() const {
+ return x_image_->bytes_per_line;
+}
+
+int XServerPixelBuffer::GetDepth() const {
+ return x_image_->depth;
+}
+
+int XServerPixelBuffer::GetBitsPerPixel() const {
+ return x_image_->bits_per_pixel;
+}
+
+int XServerPixelBuffer::GetRedMask() const {
+ return x_image_->red_mask;
+}
+
+int XServerPixelBuffer::GetBlueMask() const {
+ return x_image_->blue_mask;
+}
+
+int XServerPixelBuffer::GetGreenMask() const {
+ return x_image_->green_mask;
+}
+
+int XServerPixelBuffer::GetRedShift() const {
+ return ffs(x_image_->red_mask) - 1;
+}
+
+int XServerPixelBuffer::GetBlueShift() const {
+ return ffs(x_image_->blue_mask) - 1;
+}
+
+int XServerPixelBuffer::GetGreenShift() const {
+ return ffs(x_image_->green_mask) - 1;
+}
+
+bool XServerPixelBuffer::IsRgb() const {
+ return GetRedShift() == 16 && GetGreenShift() == 8 && GetBlueShift() == 0;
+}
+
+} // namespace remoting
diff --git a/remoting/host/x_server_pixel_buffer.h b/remoting/host/x_server_pixel_buffer.h
new file mode 100644
index 0000000..98bb8f6
--- /dev/null
+++ b/remoting/host/x_server_pixel_buffer.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 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.
+
+// Don't include this file in any .h files because it pulls in some X headers.
+
+#ifndef REMOTING_HOST_X_SERVER_PIXEL_BUFFER_H_
+#define REMOTING_HOST_X_SERVER_PIXEL_BUFFER_H_
+
+#include "base/scoped_ptr.h"
+#include "base/basictypes.h"
+#include "ui/gfx/rect.h"
+
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+
+namespace remoting {
+
+// A class to allow the X server's pixel buffer to be accessed as efficiently
+// as possible.
+class XServerPixelBuffer {
+ public:
+ XServerPixelBuffer();
+ ~XServerPixelBuffer();
+
+ void Init(Display* display);
+
+ // Capture the specified rectangle and return a pointer to its top-left pixel
+ // or NULL if capture fails. The returned pointer remains valid until the next
+ // call to CaptureRect.
+ uint8* CaptureRect(const gfx::Rect& rect);
+
+ // Return information about the most recent capture. This is only guaranteed
+ // to be valid between CaptureRect calls.
+ int GetStride() const;
+ int GetDepth() const;
+ int GetBitsPerPixel() const;
+ int GetRedMask() const;
+ int GetBlueMask() const;
+ int GetGreenMask() const;
+ int GetRedShift() const;
+ int GetBlueShift() const;
+ int GetGreenShift() const;
+ bool IsRgb() const;
+
+ private:
+ void InitShm(int screen);
+ bool InitPixmaps(int width, int height, int depth);
+ void DestroyShmSegmentInfo();
+
+ Display* display_;
+ Window root_window_;
+ XImage* x_image_;
+ XShmSegmentInfo* shm_segment_info_;
+ Pixmap shm_pixmap_;
+ GC shm_gc_;
+
+ DISALLOW_COPY_AND_ASSIGN(XServerPixelBuffer);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_X_SERVER_PIXEL_BUFFER_H_
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 88d8266..076763e 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -238,14 +238,19 @@
],
}],
['OS=="linux"', {
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ ],
'sources': [
'host/capturer_linux.cc',
'host/capturer_linux.h',
'host/event_executor_linux.cc',
'host/event_executor_linux.h',
+ 'host/user_authenticator_linux.cc',
'host/user_authenticator_pam.cc',
'host/user_authenticator_pam.h',
- 'host/user_authenticator_linux.cc'
+ 'host/x_server_pixel_buffer.cc',
+ 'host/x_server_pixel_buffer.h',
],
'link_settings': {
'libraries': [
@@ -253,6 +258,7 @@
'-lXdamage',
'-lXtst',
'-lpam',
+ '-lXext'
],
},
}],