diff options
-rw-r--r-- | remoting/host/capturer_linux.cc | 134 | ||||
-rw-r--r-- | remoting/host/x_server_pixel_buffer.cc | 206 | ||||
-rw-r--r-- | remoting/host/x_server_pixel_buffer.h | 63 | ||||
-rw-r--r-- | remoting/remoting.gyp | 8 |
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' ], }, }], |