diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@4adac7df-926f-26a2-2b94-8c16560cd09d> | 2013-08-23 18:22:12 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@4adac7df-926f-26a2-2b94-8c16560cd09d> | 2013-08-23 18:22:12 +0000 |
commit | 9f282403f258162ca53eb2f16b8e9a26e7970096 (patch) | |
tree | 0b4736d97d68c3401a38cd0a7e19236f24c1558f | |
parent | 563910bde3efd612a6080dba4ceaa122d9f6e9f9 (diff) | |
download | webrtc-9f282403f258162ca53eb2f16b8e9a26e7970096.tar.gz |
WindowCapturer implementation for Linux.
Window enumeration is based on the code used by hangouts plugin
(see libjingle/talk/base/linuxwindowpicker.cc). XServerPixelBuffer
is used to capture windows. It had to be refactored to support window
capturing (previously it worked only for the whole screen).
R=wez@chromium.org
Review URL: https://webrtc-codereview.appspot.com/1741004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4605 4adac7df-926f-26a2-2b94-8c16560cd09d
-rw-r--r-- | webrtc/modules/desktop_capture/desktop_capture.gypi | 7 | ||||
-rw-r--r-- | webrtc/modules/desktop_capture/screen_capturer_x11.cc | 176 | ||||
-rwxr-xr-x | webrtc/modules/desktop_capture/window_capturer_null.cc (renamed from webrtc/modules/desktop_capture/window_capturer_linux.cc) | 22 | ||||
-rw-r--r-- | webrtc/modules/desktop_capture/window_capturer_unittest.cc | 6 | ||||
-rwxr-xr-x | webrtc/modules/desktop_capture/window_capturer_x11.cc | 354 | ||||
-rw-r--r-- | webrtc/modules/desktop_capture/x11/x_error_trap.cc | 69 | ||||
-rw-r--r-- | webrtc/modules/desktop_capture/x11/x_error_trap.h | 39 | ||||
-rw-r--r-- | webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc | 292 | ||||
-rw-r--r-- | webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h | 53 |
9 files changed, 708 insertions, 310 deletions
diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi index 269c583780..0bc3839ef4 100644 --- a/webrtc/modules/desktop_capture/desktop_capture.gypi +++ b/webrtc/modules/desktop_capture/desktop_capture.gypi @@ -53,9 +53,11 @@ "win/scoped_thread_desktop.cc", "win/scoped_thread_desktop.h", "window_capturer.h", - "window_capturer_linux.cc", "window_capturer_mac.cc", "window_capturer_win.cc", + "window_capturer_x11.cc", + "x11/x_error_trap.cc", + "x11/x_error_trap.h", "x11/x_server_pixel_buffer.cc", "x11/x_server_pixel_buffer.h", ], @@ -69,15 +71,18 @@ 'link_settings': { 'libraries': [ '-lX11', + '-lXcomposite', '-lXdamage', '-lXext', '-lXfixes', + '-lXrender', ], }, }], ['OS!="win" and OS!="mac" and use_x11==0', { 'sources': [ "screen_capturer_null.cc", + "window_capturer_null.cc", ], }], ['OS=="mac"', { diff --git a/webrtc/modules/desktop_capture/screen_capturer_x11.cc b/webrtc/modules/desktop_capture/screen_capturer_x11.cc index e730b71a84..5cca65b133 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_x11.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_x11.cc @@ -76,9 +76,8 @@ class ScreenCapturerLinux : public ScreenCapturer { // differences between this and the previous capture. DesktopFrame* CaptureScreen(); - // Called when the screen configuration is changed. |root_window_size| - // specifies the most recent size of the root window. - void ScreenConfigurationChanged(const DesktopSize& root_window_size); + // Called when the screen configuration is changed. + void ScreenConfigurationChanged(); // Synchronize the current buffer with |last_buffer_|, by copying pixels from // the area of |last_invalid_rects|. @@ -89,25 +88,6 @@ class ScreenCapturerLinux : public ScreenCapturer { void DeinitXlib(); - // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into - // |frame|. - void CaptureRect(const DesktopRect& rect, - DesktopFrame* frame); - - // We expose two forms of blitting to handle variations in the pixel format. - // In FastBlit, the operation is effectively a memcpy. - void FastBlit(uint8_t* image, - const DesktopRect& rect, - DesktopFrame* frame); - void SlowBlit(uint8_t* image, - const DesktopRect& rect, - DesktopFrame* frame); - - // Returns the number of bits |mask| has to be shifted left so its last - // (most-significant) bit set becomes the most-significant bit of the word. - // When |mask| is 0 the function returns 31. - static uint32_t GetRgbShift(uint32_t mask); - Callback* callback_; MouseShapeObserver* mouse_shape_observer_; @@ -116,9 +96,6 @@ class ScreenCapturerLinux : public ScreenCapturer { GC gc_; Window root_window_; - // Last known dimensions of the root window. - DesktopSize root_window_size_; - // XFixes. bool has_xfixes_; int xfixes_event_base_; @@ -207,8 +184,10 @@ bool ScreenCapturerLinux::Init(bool use_x_damage) { // Register for changes to the dimensions of the root window. XSelectInput(display_, root_window_, StructureNotifyMask); - root_window_size_ = XServerPixelBuffer::GetRootWindowSize(display_); - x_server_pixel_buffer_.Init(display_, root_window_size_); + if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) { + LOG(LS_ERROR) << "Failed to initialize pixel buffer."; + return false; + } if (has_xfixes_) { // Register for changes to the cursor shape. @@ -276,12 +255,21 @@ void ScreenCapturerLinux::Capture(const DesktopRegion& region) { // Process XEvents for XDamage and cursor shape tracking. ProcessPendingXEvents(); + // ProcessPendingXEvents() may call ScreenConfigurationChanged() which + // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still + // in a good shape. + if (!x_server_pixel_buffer_.is_initialized()) { + // We failed to initialize pixel buffer. + callback_->OnCaptureCompleted(NULL); + return; + } + // If the current frame is from an older generation then allocate a new one. // Note that we can't reallocate other buffers at this point, since the caller // may still be reading from them. if (!queue_.current_frame()) { scoped_ptr<DesktopFrame> frame( - new BasicDesktopFrame(root_window_size_)); + new BasicDesktopFrame(x_server_pixel_buffer_.window_size())); queue_.ReplaceCurrentFrame(frame.release()); } @@ -324,9 +312,7 @@ void ScreenCapturerLinux::ProcessPendingXEvents() { XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e); DCHECK(event->level == XDamageReportNonEmpty); } else if (e.type == ConfigureNotify) { - const XConfigureEvent& event = e.xconfigure; - ScreenConfigurationChanged( - DesktopSize(event.width, event.height)); + ScreenConfigurationChanged(); } else if (has_xfixes_ && e.type == xfixes_event_base_ + XFixesCursorNotify) { XFixesCursorNotifyEvent* cne; @@ -371,6 +357,7 @@ void ScreenCapturerLinux::CaptureCursor() { DesktopFrame* ScreenCapturerLinux::CaptureScreen() { DesktopFrame* frame = queue_.current_frame()->Share(); + assert(x_server_pixel_buffer_.window_size().equals(frame->size())); // Pass the screen size to the helper, so it can clip the invalid region if it // expands that region to a grid. @@ -407,18 +394,17 @@ DesktopFrame* ScreenCapturerLinux::CaptureScreen() { // spurious XDamage notifications were received for a previous (larger) // screen size. updated_region->IntersectWith( - DesktopRect::MakeSize(root_window_size_)); + DesktopRect::MakeSize(x_server_pixel_buffer_.window_size())); for (DesktopRegion::Iterator it(*updated_region); !it.IsAtEnd(); it.Advance()) { - CaptureRect(it.rect(), frame); + x_server_pixel_buffer_.CaptureRect(it.rect(), frame); } } else { // Doing full-screen polling, or this is the first capture after a // screen-resolution change. In either case, need a full-screen capture. - DesktopRect screen_rect = - DesktopRect::MakeSize(frame->size()); - CaptureRect(screen_rect, frame); + DesktopRect screen_rect = DesktopRect::MakeSize(frame->size()); + x_server_pixel_buffer_.CaptureRect(screen_rect, frame); if (queue_.previous_frame()) { // Full-screen polling, so calculate the invalid rects here, based on the @@ -439,15 +425,15 @@ DesktopFrame* ScreenCapturerLinux::CaptureScreen() { return frame; } -void ScreenCapturerLinux::ScreenConfigurationChanged( - const DesktopSize& root_window_size) { - root_window_size_ = root_window_size; - +void ScreenCapturerLinux::ScreenConfigurationChanged() { // Make sure the frame buffers will be reallocated. queue_.Reset(); helper_.ClearInvalidRegion(); - x_server_pixel_buffer_.Init(display_, root_window_size_); + if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) { + LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen " + "configuration change."; + } } void ScreenCapturerLinux::SynchronizeFrame() { @@ -497,114 +483,6 @@ void ScreenCapturerLinux::DeinitXlib() { } } -void ScreenCapturerLinux::CaptureRect(const DesktopRect& rect, - DesktopFrame* frame) { - uint8_t* image = x_server_pixel_buffer_.CaptureRect(rect); - int depth = x_server_pixel_buffer_.GetDepth(); - if ((depth == 24 || depth == 32) && - x_server_pixel_buffer_.GetBitsPerPixel() == 32 && - x_server_pixel_buffer_.GetRedMask() == 0xff0000 && - x_server_pixel_buffer_.GetGreenMask() == 0xff00 && - x_server_pixel_buffer_.GetBlueMask() == 0xff) { - FastBlit(image, rect, frame); - } else { - SlowBlit(image, rect, frame); - } -} - -void ScreenCapturerLinux::FastBlit(uint8_t* image, - const DesktopRect& rect, - DesktopFrame* frame) { - uint8_t* src_pos = image; - int src_stride = x_server_pixel_buffer_.GetStride(); - int dst_x = rect.left(), dst_y = rect.top(); - - uint8_t* dst_pos = frame->data() + frame->stride() * dst_y; - dst_pos += dst_x * DesktopFrame::kBytesPerPixel; - - int height = rect.height(); - int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel; - for (int y = 0; y < height; ++y) { - memcpy(dst_pos, src_pos, row_bytes); - src_pos += src_stride; - dst_pos += frame->stride(); - } -} - -void ScreenCapturerLinux::SlowBlit(uint8_t* image, - const DesktopRect& rect, - DesktopFrame* frame) { - int src_stride = x_server_pixel_buffer_.GetStride(); - int dst_x = rect.left(), dst_y = rect.top(); - int width = rect.width(), height = rect.height(); - - uint32_t red_mask = x_server_pixel_buffer_.GetRedMask(); - uint32_t green_mask = x_server_pixel_buffer_.GetGreenMask(); - uint32_t blue_mask = x_server_pixel_buffer_.GetBlueMask(); - - uint32_t red_shift = GetRgbShift(red_mask); - uint32_t green_shift = GetRgbShift(green_mask); - uint32_t blue_shift = GetRgbShift(blue_mask); - - unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel(); - - uint8_t* dst_pos = frame->data() + frame->stride() * dst_y; - uint8_t* src_pos = image; - dst_pos += dst_x * DesktopFrame::kBytesPerPixel; - // TODO(hclam): Optimize, perhaps using MMX code or by converting to - // YUV directly - for (int y = 0; y < height; y++) { - uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos); - 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; - uint32_t g = (pixel & green_mask) << green_shift; - uint32_t b = (pixel & blue_mask) << blue_shift; - - // Write as 32-bit RGB. - dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) | - ((b >> 24) & 0xff); - } - dst_pos += frame->stride(); - src_pos += src_stride; - } -} - -// static -uint32_t ScreenCapturerLinux::GetRgbShift(uint32_t mask) { - int shift = 0; - if ((mask & 0xffff0000u) == 0) { - mask <<= 16; - shift += 16; - } - if ((mask & 0xff000000u) == 0) { - mask <<= 8; - shift += 8; - } - if ((mask & 0xf0000000u) == 0) { - mask <<= 4; - shift += 4; - } - if ((mask & 0xc0000000u) == 0) { - mask <<= 2; - shift += 2; - } - if ((mask & 0x80000000u) == 0) - shift += 1; - - return shift; -} - } // namespace // static diff --git a/webrtc/modules/desktop_capture/window_capturer_linux.cc b/webrtc/modules/desktop_capture/window_capturer_null.cc index e95cad8e61..8ea723aaa6 100755 --- a/webrtc/modules/desktop_capture/window_capturer_linux.cc +++ b/webrtc/modules/desktop_capture/window_capturer_null.cc @@ -18,10 +18,10 @@ namespace webrtc { namespace { -class WindowCapturerLinux : public WindowCapturer { +class WindowCapturerNull : public WindowCapturer { public: - WindowCapturerLinux(); - virtual ~WindowCapturerLinux(); + WindowCapturerNull(); + virtual ~WindowCapturerNull(); // WindowCapturer interface. virtual bool GetWindowList(WindowList* windows) OVERRIDE; @@ -34,34 +34,34 @@ class WindowCapturerLinux : public WindowCapturer { private: Callback* callback_; - DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux); + DISALLOW_COPY_AND_ASSIGN(WindowCapturerNull); }; -WindowCapturerLinux::WindowCapturerLinux() +WindowCapturerNull::WindowCapturerNull() : callback_(NULL) { } -WindowCapturerLinux::~WindowCapturerLinux() { +WindowCapturerNull::~WindowCapturerNull() { } -bool WindowCapturerLinux::GetWindowList(WindowList* windows) { +bool WindowCapturerNull::GetWindowList(WindowList* windows) { // Not implemented yet. return false; } -bool WindowCapturerLinux::SelectWindow(WindowId id) { +bool WindowCapturerNull::SelectWindow(WindowId id) { // Not implemented yet. return false; } -void WindowCapturerLinux::Start(Callback* callback) { +void WindowCapturerNull::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } -void WindowCapturerLinux::Capture(const DesktopRegion& region) { +void WindowCapturerNull::Capture(const DesktopRegion& region) { // Not implemented yet. callback_->OnCaptureCompleted(NULL); } @@ -70,7 +70,7 @@ void WindowCapturerLinux::Capture(const DesktopRegion& region) { // static WindowCapturer* WindowCapturer::Create() { - return new WindowCapturerLinux(); + return new WindowCapturerNull(); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/window_capturer_unittest.cc b/webrtc/modules/desktop_capture/window_capturer_unittest.cc index 2c11d849d9..c6de16ac42 100644 --- a/webrtc/modules/desktop_capture/window_capturer_unittest.cc +++ b/webrtc/modules/desktop_capture/window_capturer_unittest.cc @@ -10,6 +10,8 @@ #include "webrtc/modules/desktop_capture/window_capturer.h" +#include <iostream> + #include "gtest/gtest.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/desktop_region.h" @@ -42,8 +44,6 @@ class WindowCapturerTest : public testing::Test, scoped_ptr<DesktopFrame> frame_; }; -#if defined(WEBRTC_WIN) || defined(WEBRTC_MAC) - // Verify that we can enumerate windows. TEST_F(WindowCapturerTest, Enumerate) { WindowCapturer::WindowList windows; @@ -92,6 +92,4 @@ TEST_F(WindowCapturerTest, Capture) { } } -#endif // defined(WEBRTC_WIN) || defined(WEBRTC_MAC) - } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/window_capturer_x11.cc b/webrtc/modules/desktop_capture/window_capturer_x11.cc new file mode 100755 index 0000000000..adafa9450d --- /dev/null +++ b/webrtc/modules/desktop_capture/window_capturer_x11.cc @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/window_capturer.h" + +#include <string.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xrender.h> +#include <X11/Xutil.h> +#include <algorithm> +#include <cassert> + +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/x11/x_error_trap.h" +#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +namespace { + +// Convenience wrapper for XGetWindowProperty() results. +template <class PropertyType> +class XWindowProperty { + public: + XWindowProperty(Display* display, Window window, Atom property) + : is_valid_(false), + size_(0), + data_(NULL) { + const int kBitsPerByte = 8; + Atom actual_type; + int actual_format; + unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty + int status = XGetWindowProperty(display, window, property, 0L, ~0L, False, + AnyPropertyType, &actual_type, + &actual_format, &size_, + &bytes_after, &data_); + if (status != Success) { + data_ = NULL; + return; + } + if (sizeof(PropertyType) * kBitsPerByte != actual_format) { + size_ = 0; + return; + } + + is_valid_ = true; + } + + ~XWindowProperty() { + if (data_) + XFree(data_); + } + + // True if we got properly value successfully. + bool is_valid() const { return is_valid_; } + + // Size and value of the property. + size_t size() const { return size_; } + const PropertyType* data() const { + return reinterpret_cast<PropertyType*>(data_); + } + PropertyType* data() { + return reinterpret_cast<PropertyType*>(data_); + } + + private: + bool is_valid_; + unsigned long size_; // NOLINT: type required by XGetWindowProperty + unsigned char* data_; + + DISALLOW_COPY_AND_ASSIGN(XWindowProperty); +}; + +class WindowCapturerLinux : public WindowCapturer { + public: + WindowCapturerLinux(); + virtual ~WindowCapturerLinux(); + + // WindowCapturer interface. + virtual bool GetWindowList(WindowList* windows) OVERRIDE; + virtual bool SelectWindow(WindowId id) OVERRIDE; + + // DesktopCapturer interface. + virtual void Start(Callback* callback) OVERRIDE; + virtual void Capture(const DesktopRegion& region) OVERRIDE; + + private: + // Iterates through |window| hierarchy to find first visible window, i.e. one + // that has WM_STATE property set to NormalState. + // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 . + ::Window GetApplicationWindow(::Window window); + + // Returns true if the |window| is a desktop element. + bool IsDesktopElement(::Window window); + + // Returns window title for the specified X |window|. + bool GetWindowTitle(::Window window, std::string* title); + + Callback* callback_; + + Display* display_; + + Atom wm_state_atom_; + Atom window_type_atom_; + Atom normal_window_type_atom_; + bool has_composite_extension_; + + ::Window selected_window_; + XServerPixelBuffer x_server_pixel_buffer_; + + DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux); +}; + +WindowCapturerLinux::WindowCapturerLinux() + : callback_(NULL), + display_(NULL), + has_composite_extension_(false), + selected_window_(0) { + display_ = XOpenDisplay(NULL); + if (!display_) { + LOG(LS_ERROR) << "Failed to open display."; + return; + } + + // Create Atoms so we don't need to do it every time they are used. + wm_state_atom_ = XInternAtom(display_, "WM_STATE", True); + window_type_atom_ = XInternAtom(display_, "_NET_WM_WINDOW_TYPE", True); + normal_window_type_atom_ = XInternAtom( + display_, "_NET_WM_WINDOW_TYPE_NORMAL", True); + + int event_base, error_base, major_version, minor_version; + if (XCompositeQueryExtension(display_, &event_base, &error_base) && + XCompositeQueryVersion(display_, &major_version, &minor_version) && + // XCompositeNameWindowPixmap() requires version 0.2 + (major_version > 0 || minor_version >= 2)) { + has_composite_extension_ = true; + } else { + LOG(LS_INFO) << "Xcomposite extension not available or too old."; + } +} + +WindowCapturerLinux::~WindowCapturerLinux() { + if (display_) + XCloseDisplay(display_); +} + +bool WindowCapturerLinux::GetWindowList(WindowList* windows) { + if (!display_) + return false; + + WindowList result; + + XErrorTrap error_trap(display_); + + int num_screens = XScreenCount(display_); + for (int screen = 0; screen < num_screens; ++screen) { + ::Window root_window = XRootWindow(display_, screen); + ::Window parent; + ::Window *children; + unsigned int num_children; + int status = XQueryTree(display_, root_window, &root_window, &parent, + &children, &num_children); + if (status == 0) { + LOG(LS_ERROR) << "Failed to query for child windows for screen " + << screen; + continue; + } + + for (unsigned int i = 0; i < num_children; ++i) { + // Iterate in reverse order to return windows from front to back. + ::Window app_window = + GetApplicationWindow(children[num_children - 1 - i]); + if (app_window && !IsDesktopElement(app_window)) { + Window w; + w.id = app_window; + if (GetWindowTitle(app_window, &w.title)) + result.push_back(w); + } + } + + if (children) + XFree(children); + } + + windows->swap(result); + + return true; +} + +bool WindowCapturerLinux::SelectWindow(WindowId id) { + if (!x_server_pixel_buffer_.Init(display_, id)) + return false; + + selected_window_ = id; + + // In addition to needing X11 server-side support for Xcomposite, it actually + // needs to be turned on for the window. If the user has modern + // hardware/drivers but isn't using a compositing window manager, that won't + // be the case. Here we automatically turn it on. + + // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11 + // remembers who has requested this and will turn it off for us when we exit. + XCompositeRedirectWindow(display_, id, CompositeRedirectAutomatic); + + return true; +} + +void WindowCapturerLinux::Start(Callback* callback) { + assert(!callback_); + assert(callback); + + callback_ = callback; +} + +void WindowCapturerLinux::Capture(const DesktopRegion& region) { + if (!has_composite_extension_) { + // Without the Xcomposite extension we capture when the whole window is + // visible on screen and not covered by any other window. This is not + // something we want so instead, just bail out. + LOG(LS_INFO) << "No Xcomposite extension detected."; + callback_->OnCaptureCompleted(NULL); + return; + } + + DesktopFrame* frame = + new BasicDesktopFrame(x_server_pixel_buffer_.window_size()); + + x_server_pixel_buffer_.Synchronize(); + x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()), + frame); + + callback_->OnCaptureCompleted(frame); +} + +::Window WindowCapturerLinux::GetApplicationWindow(::Window window) { + // Get WM_STATE property of the window. + XWindowProperty<uint32_t> window_state(display_, window, wm_state_atom_); + + // WM_STATE is considered to be set to WithdrawnState when it missing. + int32_t state = window_state.is_valid() ? + *window_state.data() : WithdrawnState; + + if (state == NormalState) { + // Window has WM_STATE==NormalState. Return it. + return window; + } else if (state == IconicState) { + // Window is in minimized. Skip it. + return 0; + } + + // If the window is in WithdrawnState then look at all of its children. + ::Window root, parent; + ::Window *children; + unsigned int num_children; + if (!XQueryTree(display_, window, &root, &parent, &children, + &num_children)) { + LOG(LS_ERROR) << "Failed to query for child windows although window" + << "does not have a valid WM_STATE."; + return 0; + } + ::Window app_window = 0; + for (unsigned int i = 0; i < num_children; ++i) { + app_window = GetApplicationWindow(children[i]); + if (app_window) + break; + } + + if (children) + XFree(children); + return app_window; +} + +bool WindowCapturerLinux::IsDesktopElement(::Window window) { + if (window == 0) + return false; + + // First look for _NET_WM_WINDOW_TYPE. The standard + // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306) + // says this hint *should* be present on all windows, and we use the existence + // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not + // a desktop element (that is, only "normal" windows should be shareable). + XWindowProperty<uint32_t> window_type(display_, window, window_type_atom_); + if (window_type.is_valid() && window_type.size() > 0) { + uint32_t* end = window_type.data() + window_type.size(); + bool is_normal = (end != std::find( + window_type.data(), end, normal_window_type_atom_)); + return !is_normal; + } + + // Fall back on using the hint. + XClassHint class_hint; + Status status = XGetClassHint(display_, window, &class_hint); + bool result = false; + if (status == 0) { + // No hints, assume this is a normal application window. + return result; + } + + if (strcmp("gnome-panel", class_hint.res_name) == 0 || + strcmp("desktop_window", class_hint.res_name) == 0) { + result = true; + } + XFree(class_hint.res_name); + XFree(class_hint.res_class); + return result; +} + +bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) { + int status; + bool result = false; + XTextProperty window_name; + window_name.value = NULL; + if (window) { + status = XGetWMName(display_, window, &window_name); + if (status && window_name.value && window_name.nitems) { + int cnt; + char **list = NULL; + status = Xutf8TextPropertyToTextList(display_, &window_name, &list, + &cnt); + if (status >= Success && cnt && *list) { + if (cnt > 1) { + LOG(LS_INFO) << "Window has " << cnt + << " text properties, only using the first one."; + } + *title = *list; + result = true; + } + if (list) + XFreeStringList(list); + } + if (window_name.value) + XFree(window_name.value); + } + return result; +} + +} // namespace + +// static +WindowCapturer* WindowCapturer::Create() { + return new WindowCapturerLinux(); +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/x11/x_error_trap.cc b/webrtc/modules/desktop_capture/x11/x_error_trap.cc new file mode 100644 index 0000000000..458fbe61a0 --- /dev/null +++ b/webrtc/modules/desktop_capture/x11/x_error_trap.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/x11/x_error_trap.h" + +#include <assert.h> + +#if defined(TOOLKIT_GTK) +#include <gdk/gdk.h> +#endif // !defined(TOOLKIT_GTK) + +namespace webrtc { + +namespace { + +#if !defined(TOOLKIT_GTK) + +// TODO(sergeyu): This code is not thread safe. Fix it. Bug 2202. +static bool g_xserver_error_trap_enabled = false; +static int g_last_xserver_error_code = 0; + +int XServerErrorHandler(Display* display, XErrorEvent* error_event) { + assert(g_xserver_error_trap_enabled); + g_last_xserver_error_code = error_event->error_code; + return 0; +} + +#endif // !defined(TOOLKIT_GTK) + +} // namespace + +XErrorTrap::XErrorTrap(Display* display) + : original_error_handler_(NULL), + enabled_(true) { +#if defined(TOOLKIT_GTK) + gdk_error_trap_push(); +#else // !defined(TOOLKIT_GTK) + assert(!g_xserver_error_trap_enabled); + original_error_handler_ = XSetErrorHandler(&XServerErrorHandler); + g_xserver_error_trap_enabled = true; + g_last_xserver_error_code = 0; +#endif // !defined(TOOLKIT_GTK) +} + +int XErrorTrap::GetLastErrorAndDisable() { + enabled_ = false; +#if defined(TOOLKIT_GTK) + return gdk_error_trap_push(); +#else // !defined(TOOLKIT_GTK) + assert(g_xserver_error_trap_enabled); + XSetErrorHandler(original_error_handler_); + g_xserver_error_trap_enabled = false; + return g_last_xserver_error_code; +#endif // !defined(TOOLKIT_GTK) +} + +XErrorTrap::~XErrorTrap() { + if (enabled_) + GetLastErrorAndDisable(); +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/x11/x_error_trap.h b/webrtc/modules/desktop_capture/x11/x_error_trap.h new file mode 100644 index 0000000000..fd8346928c --- /dev/null +++ b/webrtc/modules/desktop_capture/x11/x_error_trap.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_ + +#include <X11/Xlib.h> + +#include "webrtc/system_wrappers/interface/constructor_magic.h" + +namespace webrtc { + +// Helper class that registers X Window error handler. Caller can use +// GetLastErrorAndDisable() to get the last error that was caught, if any. +class XErrorTrap { + public: + explicit XErrorTrap(Display* display); + ~XErrorTrap(); + + // Returns last error and removes unregisters the error handler. + int GetLastErrorAndDisable(); + + private: + XErrorHandler original_error_handler_; + bool enabled_; + + DISALLOW_COPY_AND_ASSIGN(XErrorTrap); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_ diff --git a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc index 88f7738dcf..6983a6dcce 100644 --- a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc +++ b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc @@ -11,62 +11,56 @@ #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" #include <assert.h> +#include <string.h> #include <sys/shm.h> +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/x11/x_error_trap.h" #include "webrtc/system_wrappers/interface/logging.h" -#if defined(TOOLKIT_GTK) -#include <gdk/gdk.h> -#else // !defined(TOOLKIT_GTK) -#include <X11/Xlib.h> -#endif // !defined(TOOLKIT_GTK) - namespace { -#if defined(TOOLKIT_GTK) -// GDK sets error handler for Xlib errors, so we need to use it to -// trap X errors when this code is compiled with GTK. -void EnableXServerErrorTrap() { - gdk_error_trap_push(); -} - -int GetLastXServerError() { - return gdk_error_trap_pop(); -} - -#else // !defined(TOOLKIT_GTK) - -static bool g_xserver_error_trap_enabled = false; -static int g_last_xserver_error_code = 0; - -int XServerErrorHandler(Display* display, XErrorEvent* error_event) { - assert(g_xserver_error_trap_enabled); - g_last_xserver_error_code = error_event->error_code; - return 0; -} +// Returns the number of bits |mask| has to be shifted left so its last +// (most-significant) bit set becomes the most-significant bit of the word. +// When |mask| is 0 the function returns 31. +uint32_t MaskToShift(uint32_t mask) { + int shift = 0; + if ((mask & 0xffff0000u) == 0) { + mask <<= 16; + shift += 16; + } + if ((mask & 0xff000000u) == 0) { + mask <<= 8; + shift += 8; + } + if ((mask & 0xf0000000u) == 0) { + mask <<= 4; + shift += 4; + } + if ((mask & 0xc0000000u) == 0) { + mask <<= 2; + shift += 2; + } + if ((mask & 0x80000000u) == 0) + shift += 1; -void EnableXServerErrorTrap() { - assert(!g_xserver_error_trap_enabled); - XSetErrorHandler(&XServerErrorHandler); - g_xserver_error_trap_enabled = true; - g_last_xserver_error_code = 0; + return shift; } -int GetLastXServerError() { - assert(g_xserver_error_trap_enabled); - XSetErrorHandler(NULL); - g_xserver_error_trap_enabled = false; - return g_last_xserver_error_code; +// Returns true if |image| is in RGB format. +bool IsXImageRGBFormat(XImage* image) { + return image->bits_per_pixel == 32 && + image->red_mask == 0xff0000 && + image->green_mask == 0xff00 && + image->blue_mask == 0xff; } -#endif // !defined(TOOLKIT_GTK) - } // namespace namespace webrtc { XServerPixelBuffer::XServerPixelBuffer() - : display_(NULL), root_window_(0), + : display_(NULL), window_(0), x_image_(NULL), shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) { } @@ -96,34 +90,39 @@ void XServerPixelBuffer::Release() { delete shm_segment_info_; shm_segment_info_ = NULL; } + window_ = 0; } -void XServerPixelBuffer::Init(Display* display, - const DesktopSize& screen_size) { +bool XServerPixelBuffer::Init(Display* display, Window window) { Release(); display_ = display; - root_window_size_ = screen_size; - int default_screen = DefaultScreen(display_); - root_window_ = RootWindow(display_, default_screen); - InitShm(default_screen); -} -// static -DesktopSize XServerPixelBuffer::GetRootWindowSize(Display* display) { - XWindowAttributes root_attr; - XGetWindowAttributes(display, DefaultRootWindow(display), &root_attr); - return DesktopSize(root_attr.width, root_attr.height); + XWindowAttributes attributes; + { + XErrorTrap error_trap(display_); + if (!XGetWindowAttributes(display_, window, &attributes) || + error_trap.GetLastErrorAndDisable() != 0) { + return false; + } + } + + window_size_ = DesktopSize(attributes.width, attributes.height); + window_ = window; + InitShm(attributes); + + return true; } -void XServerPixelBuffer::InitShm(int screen) { - Visual* default_visual = DefaultVisual(display_, screen); - int default_depth = DefaultDepth(display_, screen); +void XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) { + Visual* default_visual = attributes.visual; + int default_depth = attributes.depth; int major, minor; - Bool havePixmaps; - if (!XShmQueryVersion(display_, &major, &minor, &havePixmaps)) + Bool have_pixmaps; + if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) { // Shared memory not supported. CaptureRect will use the XImage API instead. return; + } bool using_shm = false; shm_segment_info_ = new XShmSegmentInfo; @@ -131,8 +130,8 @@ void XServerPixelBuffer::InitShm(int screen) { 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_, root_window_size_.width(), - root_window_size_.height()); + 0, shm_segment_info_, window_size_.width(), + window_size_.height()); if (x_image_) { shm_segment_info_->shmid = shmget( IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height, @@ -141,10 +140,10 @@ void XServerPixelBuffer::InitShm(int screen) { 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)) { - EnableXServerErrorTrap(); + XErrorTrap error_trap(display_); using_shm = XShmAttach(display_, shm_segment_info_); XSync(display_, False); - if (GetLastXServerError() != 0) + if (error_trap.GetLastErrorAndDisable() != 0) using_shm = false; if (using_shm) { LOG(LS_VERBOSE) << "Using X shared memory segment " @@ -163,48 +162,52 @@ void XServerPixelBuffer::InitShm(int screen) { return; } - if (havePixmaps) - havePixmaps = InitPixmaps(default_depth); + if (have_pixmaps) + have_pixmaps = InitPixmaps(default_depth); shmctl(shm_segment_info_->shmid, IPC_RMID, 0); shm_segment_info_->shmid = -1; LOG(LS_VERBOSE) << "Using X shared memory extension v" << major << "." << minor - << " with" << (havePixmaps ? "" : "out") << " pixmaps."; + << " with" << (have_pixmaps ? "" : "out") << " pixmaps."; } bool XServerPixelBuffer::InitPixmaps(int depth) { if (XShmPixmapFormat(display_) != ZPixmap) return false; - EnableXServerErrorTrap(); - shm_pixmap_ = XShmCreatePixmap(display_, root_window_, - shm_segment_info_->shmaddr, - shm_segment_info_, - root_window_size_.width(), - root_window_size_.height(), depth); - XSync(display_, False); - if (GetLastXServerError() != 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; + { + XErrorTrap error_trap(display_); + shm_pixmap_ = XShmCreatePixmap(display_, window_, + shm_segment_info_->shmaddr, + shm_segment_info_, + window_size_.width(), + window_size_.height(), depth); + XSync(display_, False); + if (error_trap.GetLastErrorAndDisable() != 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; + } } - EnableXServerErrorTrap(); - 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 (GetLastXServerError() != 0) { - XFreePixmap(display_, shm_pixmap_); - shm_pixmap_ = 0; - shm_gc_ = 0; // See shm_pixmap_ comment above. - return false; + { + XErrorTrap error_trap(display_); + XGCValues shm_gc_values; + shm_gc_values.subwindow_mode = IncludeInferiors; + shm_gc_values.graphics_exposures = False; + shm_gc_ = XCreateGC(display_, window_, + GCSubwindowMode | GCGraphicsExposures, + &shm_gc_values); + XSync(display_, False); + if (error_trap.GetLastErrorAndDisable() != 0) { + XFreePixmap(display_, shm_pixmap_); + shm_pixmap_ = 0; + shm_gc_ = 0; // See shm_pixmap_ comment above. + return false; + } } return true; @@ -213,57 +216,110 @@ bool XServerPixelBuffer::InitPixmaps(int depth) { void XServerPixelBuffer::Synchronize() { if (shm_segment_info_ && !shm_pixmap_) { // XShmGetImage can fail if the display is being reconfigured. - EnableXServerErrorTrap(); - XShmGetImage(display_, root_window_, x_image_, 0, 0, AllPlanes); - GetLastXServerError(); + XErrorTrap error_trap(display_); + XShmGetImage(display_, window_, x_image_, 0, 0, AllPlanes); } } -uint8_t* XServerPixelBuffer::CaptureRect(const DesktopRect& rect) { - assert(rect.right() <= root_window_size_.width()); - assert(rect.bottom() <= root_window_size_.height()); +void XServerPixelBuffer::CaptureRect(const DesktopRect& rect, + DesktopFrame* frame) { + assert(rect.right() <= window_size_.width()); + assert(rect.bottom() <= window_size_.height()); + + uint8_t* data; if (shm_segment_info_) { if (shm_pixmap_) { - XCopyArea(display_, root_window_, shm_pixmap_, shm_gc_, + XCopyArea(display_, window_, shm_pixmap_, shm_gc_, rect.left(), rect.top(), rect.width(), rect.height(), rect.left(), rect.top()); XSync(display_, False); } - return reinterpret_cast<uint8_t*>(x_image_->data) + + data = reinterpret_cast<uint8_t*>(x_image_->data) + rect.top() * x_image_->bytes_per_line + rect.left() * x_image_->bits_per_pixel / 8; } else { if (x_image_) XDestroyImage(x_image_); - x_image_ = XGetImage(display_, root_window_, rect.left(), rect.top(), + x_image_ = XGetImage(display_, window_, rect.left(), rect.top(), rect.width(), rect.height(), AllPlanes, ZPixmap); - return reinterpret_cast<uint8_t*>(x_image_->data); + data = reinterpret_cast<uint8_t*>(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; + if (IsXImageRGBFormat(x_image_)) { + FastBlit(data, rect, frame); + } else { + SlowBlit(data, rect, frame); + } } -int XServerPixelBuffer::GetBlueMask() const { - return x_image_->blue_mask; +void XServerPixelBuffer::FastBlit(uint8_t* image, + const DesktopRect& rect, + DesktopFrame* frame) { + uint8_t* src_pos = image; + int src_stride = x_image_->bytes_per_line; + int dst_x = rect.left(), dst_y = rect.top(); + + uint8_t* dst_pos = frame->data() + frame->stride() * dst_y; + dst_pos += dst_x * DesktopFrame::kBytesPerPixel; + + int height = rect.height(); + int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel; + for (int y = 0; y < height; ++y) { + memcpy(dst_pos, src_pos, row_bytes); + src_pos += src_stride; + dst_pos += frame->stride(); + } } -int XServerPixelBuffer::GetGreenMask() const { - return x_image_->green_mask; +void XServerPixelBuffer::SlowBlit(uint8_t* image, + const DesktopRect& rect, + DesktopFrame* frame) { + int src_stride = x_image_->bytes_per_line; + int dst_x = rect.left(), dst_y = rect.top(); + int width = rect.width(), height = rect.height(); + + uint32_t red_mask = x_image_->red_mask; + uint32_t green_mask = x_image_->red_mask; + uint32_t blue_mask = x_image_->blue_mask; + + uint32_t red_shift = MaskToShift(red_mask); + uint32_t green_shift = MaskToShift(green_mask); + uint32_t blue_shift = MaskToShift(blue_mask); + + int bits_per_pixel = x_image_->bits_per_pixel; + + uint8_t* dst_pos = frame->data() + frame->stride() * dst_y; + uint8_t* src_pos = image; + dst_pos += dst_x * DesktopFrame::kBytesPerPixel; + // TODO(hclam): Optimize, perhaps using MMX code or by converting to + // YUV directly. + // TODO(sergeyu): This code doesn't handle XImage byte order properly and + // won't work with 24bpp images. Fix it. + for (int y = 0; y < height; y++) { + uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos); + 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; + uint32_t g = (pixel & green_mask) << green_shift; + uint32_t b = (pixel & blue_mask) << blue_shift; + // Write as 32-bit RGB. + dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) | + ((b >> 24) & 0xff); + } + dst_pos += frame->stride(); + src_pos += src_stride; + } } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h index 8759be329b..b81096c811 100644 --- a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h +++ b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h @@ -20,6 +20,8 @@ namespace webrtc { +class DesktopFrame; + // A class to allow the X server's pixel buffer to be accessed as efficiently // as possible. class XServerPixelBuffer { @@ -29,14 +31,14 @@ class XServerPixelBuffer { void Release(); - // Allocate (or reallocate) the pixel buffer with the given size, which is - // assumed to be the current size of the root window. - // |screen_size| should either come from GetRootWindowSize(), or - // from a recent ConfigureNotify event on the root window. - void Init(Display* display, const DesktopSize& screen_size); + // Allocate (or reallocate) the pixel buffer for |window|. Returns false in + // case of an error (e.g. window doesn't exist). + bool Init(Display* display, Window window); + + bool is_initialized() { return window_ != 0; } - // Request the current size of the root window from the X Server. - static DesktopSize GetRootWindowSize(Display* display); + // Returns the size of the window the buffer was initialized for. + const DesktopSize& window_size() { return window_size_; } // If shared memory is being used without pixmaps, synchronize this pixel // buffer with the root window contents (otherwise, this is a no-op). @@ -45,31 +47,28 @@ class XServerPixelBuffer { // beginning. void Synchronize(); - // 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. - // In the case where the full-screen data is captured by Synchronize(), this - // simply returns the pointer without doing any more work. - // The caller must ensure that |rect| is no larger than the screen size - // supplied to Init(). - uint8_t* CaptureRect(const DesktopRect& 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; + // Capture the specified rectangle and stores it in the |frame|. In the case + // where the full-screen data is captured by Synchronize(), this simply + // returns the pointer without doing any more work. The caller must ensure + // that |rect| is not larger than window_size(). + void CaptureRect(const DesktopRect& rect, DesktopFrame* frame); private: - void InitShm(int screen); + void InitShm(const XWindowAttributes& attributes); bool InitPixmaps(int depth); + // We expose two forms of blitting to handle variations in the pixel format. + // In FastBlit(), the operation is effectively a memcpy. + void FastBlit(uint8_t* image, + const DesktopRect& rect, + DesktopFrame* frame); + void SlowBlit(uint8_t* image, + const DesktopRect& rect, + DesktopFrame* frame); + Display* display_; - Window root_window_; - DesktopSize root_window_size_; + Window window_; + DesktopSize window_size_; XImage* x_image_; XShmSegmentInfo* shm_segment_info_; Pixmap shm_pixmap_; |