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
This commit is contained in:
@@ -53,9 +53,11 @@
|
|||||||
"win/scoped_thread_desktop.cc",
|
"win/scoped_thread_desktop.cc",
|
||||||
"win/scoped_thread_desktop.h",
|
"win/scoped_thread_desktop.h",
|
||||||
"window_capturer.h",
|
"window_capturer.h",
|
||||||
"window_capturer_linux.cc",
|
|
||||||
"window_capturer_mac.cc",
|
"window_capturer_mac.cc",
|
||||||
"window_capturer_win.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.cc",
|
||||||
"x11/x_server_pixel_buffer.h",
|
"x11/x_server_pixel_buffer.h",
|
||||||
],
|
],
|
||||||
@@ -69,15 +71,18 @@
|
|||||||
'link_settings': {
|
'link_settings': {
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'-lX11',
|
'-lX11',
|
||||||
|
'-lXcomposite',
|
||||||
'-lXdamage',
|
'-lXdamage',
|
||||||
'-lXext',
|
'-lXext',
|
||||||
'-lXfixes',
|
'-lXfixes',
|
||||||
|
'-lXrender',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
['OS!="win" and OS!="mac" and use_x11==0', {
|
['OS!="win" and OS!="mac" and use_x11==0', {
|
||||||
'sources': [
|
'sources': [
|
||||||
"screen_capturer_null.cc",
|
"screen_capturer_null.cc",
|
||||||
|
"window_capturer_null.cc",
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
['OS=="mac"', {
|
['OS=="mac"', {
|
||||||
|
@@ -76,9 +76,8 @@ class ScreenCapturerLinux : public ScreenCapturer {
|
|||||||
// differences between this and the previous capture.
|
// differences between this and the previous capture.
|
||||||
DesktopFrame* CaptureScreen();
|
DesktopFrame* CaptureScreen();
|
||||||
|
|
||||||
// Called when the screen configuration is changed. |root_window_size|
|
// Called when the screen configuration is changed.
|
||||||
// specifies the most recent size of the root window.
|
void ScreenConfigurationChanged();
|
||||||
void ScreenConfigurationChanged(const DesktopSize& root_window_size);
|
|
||||||
|
|
||||||
// Synchronize the current buffer with |last_buffer_|, by copying pixels from
|
// Synchronize the current buffer with |last_buffer_|, by copying pixels from
|
||||||
// the area of |last_invalid_rects|.
|
// the area of |last_invalid_rects|.
|
||||||
@@ -89,25 +88,6 @@ class ScreenCapturerLinux : public ScreenCapturer {
|
|||||||
|
|
||||||
void DeinitXlib();
|
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_;
|
Callback* callback_;
|
||||||
MouseShapeObserver* mouse_shape_observer_;
|
MouseShapeObserver* mouse_shape_observer_;
|
||||||
|
|
||||||
@@ -116,9 +96,6 @@ class ScreenCapturerLinux : public ScreenCapturer {
|
|||||||
GC gc_;
|
GC gc_;
|
||||||
Window root_window_;
|
Window root_window_;
|
||||||
|
|
||||||
// Last known dimensions of the root window.
|
|
||||||
DesktopSize root_window_size_;
|
|
||||||
|
|
||||||
// XFixes.
|
// XFixes.
|
||||||
bool has_xfixes_;
|
bool has_xfixes_;
|
||||||
int xfixes_event_base_;
|
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.
|
// Register for changes to the dimensions of the root window.
|
||||||
XSelectInput(display_, root_window_, StructureNotifyMask);
|
XSelectInput(display_, root_window_, StructureNotifyMask);
|
||||||
|
|
||||||
root_window_size_ = XServerPixelBuffer::GetRootWindowSize(display_);
|
if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) {
|
||||||
x_server_pixel_buffer_.Init(display_, root_window_size_);
|
LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (has_xfixes_) {
|
if (has_xfixes_) {
|
||||||
// Register for changes to the cursor shape.
|
// 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.
|
// Process XEvents for XDamage and cursor shape tracking.
|
||||||
ProcessPendingXEvents();
|
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.
|
// 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
|
// Note that we can't reallocate other buffers at this point, since the caller
|
||||||
// may still be reading from them.
|
// may still be reading from them.
|
||||||
if (!queue_.current_frame()) {
|
if (!queue_.current_frame()) {
|
||||||
scoped_ptr<DesktopFrame> frame(
|
scoped_ptr<DesktopFrame> frame(
|
||||||
new BasicDesktopFrame(root_window_size_));
|
new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
|
||||||
queue_.ReplaceCurrentFrame(frame.release());
|
queue_.ReplaceCurrentFrame(frame.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,9 +312,7 @@ void ScreenCapturerLinux::ProcessPendingXEvents() {
|
|||||||
XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
|
XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
|
||||||
DCHECK(event->level == XDamageReportNonEmpty);
|
DCHECK(event->level == XDamageReportNonEmpty);
|
||||||
} else if (e.type == ConfigureNotify) {
|
} else if (e.type == ConfigureNotify) {
|
||||||
const XConfigureEvent& event = e.xconfigure;
|
ScreenConfigurationChanged();
|
||||||
ScreenConfigurationChanged(
|
|
||||||
DesktopSize(event.width, event.height));
|
|
||||||
} else if (has_xfixes_ &&
|
} else if (has_xfixes_ &&
|
||||||
e.type == xfixes_event_base_ + XFixesCursorNotify) {
|
e.type == xfixes_event_base_ + XFixesCursorNotify) {
|
||||||
XFixesCursorNotifyEvent* cne;
|
XFixesCursorNotifyEvent* cne;
|
||||||
@@ -371,6 +357,7 @@ void ScreenCapturerLinux::CaptureCursor() {
|
|||||||
|
|
||||||
DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
|
DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
|
||||||
DesktopFrame* frame = queue_.current_frame()->Share();
|
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
|
// Pass the screen size to the helper, so it can clip the invalid region if it
|
||||||
// expands that region to a grid.
|
// expands that region to a grid.
|
||||||
@@ -407,18 +394,17 @@ DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
|
|||||||
// spurious XDamage notifications were received for a previous (larger)
|
// spurious XDamage notifications were received for a previous (larger)
|
||||||
// screen size.
|
// screen size.
|
||||||
updated_region->IntersectWith(
|
updated_region->IntersectWith(
|
||||||
DesktopRect::MakeSize(root_window_size_));
|
DesktopRect::MakeSize(x_server_pixel_buffer_.window_size()));
|
||||||
|
|
||||||
for (DesktopRegion::Iterator it(*updated_region);
|
for (DesktopRegion::Iterator it(*updated_region);
|
||||||
!it.IsAtEnd(); it.Advance()) {
|
!it.IsAtEnd(); it.Advance()) {
|
||||||
CaptureRect(it.rect(), frame);
|
x_server_pixel_buffer_.CaptureRect(it.rect(), frame);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Doing full-screen polling, or this is the first capture after a
|
// Doing full-screen polling, or this is the first capture after a
|
||||||
// screen-resolution change. In either case, need a full-screen capture.
|
// screen-resolution change. In either case, need a full-screen capture.
|
||||||
DesktopRect screen_rect =
|
DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
|
||||||
DesktopRect::MakeSize(frame->size());
|
x_server_pixel_buffer_.CaptureRect(screen_rect, frame);
|
||||||
CaptureRect(screen_rect, frame);
|
|
||||||
|
|
||||||
if (queue_.previous_frame()) {
|
if (queue_.previous_frame()) {
|
||||||
// Full-screen polling, so calculate the invalid rects here, based on the
|
// Full-screen polling, so calculate the invalid rects here, based on the
|
||||||
@@ -439,15 +425,15 @@ DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
|
|||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenCapturerLinux::ScreenConfigurationChanged(
|
void ScreenCapturerLinux::ScreenConfigurationChanged() {
|
||||||
const DesktopSize& root_window_size) {
|
|
||||||
root_window_size_ = root_window_size;
|
|
||||||
|
|
||||||
// Make sure the frame buffers will be reallocated.
|
// Make sure the frame buffers will be reallocated.
|
||||||
queue_.Reset();
|
queue_.Reset();
|
||||||
|
|
||||||
helper_.ClearInvalidRegion();
|
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() {
|
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
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@@ -18,10 +18,10 @@ namespace webrtc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class WindowCapturerLinux : public WindowCapturer {
|
class WindowCapturerNull : public WindowCapturer {
|
||||||
public:
|
public:
|
||||||
WindowCapturerLinux();
|
WindowCapturerNull();
|
||||||
virtual ~WindowCapturerLinux();
|
virtual ~WindowCapturerNull();
|
||||||
|
|
||||||
// WindowCapturer interface.
|
// WindowCapturer interface.
|
||||||
virtual bool GetWindowList(WindowList* windows) OVERRIDE;
|
virtual bool GetWindowList(WindowList* windows) OVERRIDE;
|
||||||
@@ -34,34 +34,34 @@ class WindowCapturerLinux : public WindowCapturer {
|
|||||||
private:
|
private:
|
||||||
Callback* callback_;
|
Callback* callback_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
|
DISALLOW_COPY_AND_ASSIGN(WindowCapturerNull);
|
||||||
};
|
};
|
||||||
|
|
||||||
WindowCapturerLinux::WindowCapturerLinux()
|
WindowCapturerNull::WindowCapturerNull()
|
||||||
: callback_(NULL) {
|
: callback_(NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowCapturerLinux::~WindowCapturerLinux() {
|
WindowCapturerNull::~WindowCapturerNull() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowCapturerLinux::GetWindowList(WindowList* windows) {
|
bool WindowCapturerNull::GetWindowList(WindowList* windows) {
|
||||||
// Not implemented yet.
|
// Not implemented yet.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowCapturerLinux::SelectWindow(WindowId id) {
|
bool WindowCapturerNull::SelectWindow(WindowId id) {
|
||||||
// Not implemented yet.
|
// Not implemented yet.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowCapturerLinux::Start(Callback* callback) {
|
void WindowCapturerNull::Start(Callback* callback) {
|
||||||
assert(!callback_);
|
assert(!callback_);
|
||||||
assert(callback);
|
assert(callback);
|
||||||
|
|
||||||
callback_ = callback;
|
callback_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowCapturerLinux::Capture(const DesktopRegion& region) {
|
void WindowCapturerNull::Capture(const DesktopRegion& region) {
|
||||||
// Not implemented yet.
|
// Not implemented yet.
|
||||||
callback_->OnCaptureCompleted(NULL);
|
callback_->OnCaptureCompleted(NULL);
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ void WindowCapturerLinux::Capture(const DesktopRegion& region) {
|
|||||||
|
|
||||||
// static
|
// static
|
||||||
WindowCapturer* WindowCapturer::Create() {
|
WindowCapturer* WindowCapturer::Create() {
|
||||||
return new WindowCapturerLinux();
|
return new WindowCapturerNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/window_capturer.h"
|
#include "webrtc/modules/desktop_capture/window_capturer.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
@@ -42,8 +44,6 @@ class WindowCapturerTest : public testing::Test,
|
|||||||
scoped_ptr<DesktopFrame> frame_;
|
scoped_ptr<DesktopFrame> frame_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
|
|
||||||
|
|
||||||
// Verify that we can enumerate windows.
|
// Verify that we can enumerate windows.
|
||||||
TEST_F(WindowCapturerTest, Enumerate) {
|
TEST_F(WindowCapturerTest, Enumerate) {
|
||||||
WindowCapturer::WindowList windows;
|
WindowCapturer::WindowList windows;
|
||||||
@@ -92,6 +92,4 @@ TEST_F(WindowCapturerTest, Capture) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
|
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
354
webrtc/modules/desktop_capture/window_capturer_x11.cc
Executable file
354
webrtc/modules/desktop_capture/window_capturer_x11.cc
Executable file
@@ -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
|
69
webrtc/modules/desktop_capture/x11/x_error_trap.cc
Normal file
69
webrtc/modules/desktop_capture/x11/x_error_trap.cc
Normal file
@@ -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
|
39
webrtc/modules/desktop_capture/x11/x_error_trap.h
Normal file
39
webrtc/modules/desktop_capture/x11/x_error_trap.h
Normal file
@@ -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_
|
@@ -11,62 +11,56 @@
|
|||||||
#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
|
#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/shm.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"
|
#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 {
|
namespace {
|
||||||
|
|
||||||
#if defined(TOOLKIT_GTK)
|
// Returns the number of bits |mask| has to be shifted left so its last
|
||||||
// GDK sets error handler for Xlib errors, so we need to use it to
|
// (most-significant) bit set becomes the most-significant bit of the word.
|
||||||
// trap X errors when this code is compiled with GTK.
|
// When |mask| is 0 the function returns 31.
|
||||||
void EnableXServerErrorTrap() {
|
uint32_t MaskToShift(uint32_t mask) {
|
||||||
gdk_error_trap_push();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetLastXServerError() {
|
// Returns true if |image| is in RGB format.
|
||||||
return gdk_error_trap_pop();
|
bool IsXImageRGBFormat(XImage* image) {
|
||||||
|
return image->bits_per_pixel == 32 &&
|
||||||
|
image->red_mask == 0xff0000 &&
|
||||||
|
image->green_mask == 0xff00 &&
|
||||||
|
image->blue_mask == 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnableXServerErrorTrap() {
|
|
||||||
assert(!g_xserver_error_trap_enabled);
|
|
||||||
XSetErrorHandler(&XServerErrorHandler);
|
|
||||||
g_xserver_error_trap_enabled = true;
|
|
||||||
g_last_xserver_error_code = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetLastXServerError() {
|
|
||||||
assert(g_xserver_error_trap_enabled);
|
|
||||||
XSetErrorHandler(NULL);
|
|
||||||
g_xserver_error_trap_enabled = false;
|
|
||||||
return g_last_xserver_error_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !defined(TOOLKIT_GTK)
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
XServerPixelBuffer::XServerPixelBuffer()
|
XServerPixelBuffer::XServerPixelBuffer()
|
||||||
: display_(NULL), root_window_(0),
|
: display_(NULL), window_(0),
|
||||||
x_image_(NULL),
|
x_image_(NULL),
|
||||||
shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) {
|
shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) {
|
||||||
}
|
}
|
||||||
@@ -96,34 +90,39 @@ void XServerPixelBuffer::Release() {
|
|||||||
delete shm_segment_info_;
|
delete shm_segment_info_;
|
||||||
shm_segment_info_ = NULL;
|
shm_segment_info_ = NULL;
|
||||||
}
|
}
|
||||||
|
window_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XServerPixelBuffer::Init(Display* display,
|
bool XServerPixelBuffer::Init(Display* display, Window window) {
|
||||||
const DesktopSize& screen_size) {
|
|
||||||
Release();
|
Release();
|
||||||
display_ = display;
|
display_ = display;
|
||||||
root_window_size_ = screen_size;
|
|
||||||
int default_screen = DefaultScreen(display_);
|
XWindowAttributes attributes;
|
||||||
root_window_ = RootWindow(display_, default_screen);
|
{
|
||||||
InitShm(default_screen);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
void XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) {
|
||||||
DesktopSize XServerPixelBuffer::GetRootWindowSize(Display* display) {
|
Visual* default_visual = attributes.visual;
|
||||||
XWindowAttributes root_attr;
|
int default_depth = attributes.depth;
|
||||||
XGetWindowAttributes(display, DefaultRootWindow(display), &root_attr);
|
|
||||||
return DesktopSize(root_attr.width, root_attr.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XServerPixelBuffer::InitShm(int screen) {
|
|
||||||
Visual* default_visual = DefaultVisual(display_, screen);
|
|
||||||
int default_depth = DefaultDepth(display_, screen);
|
|
||||||
|
|
||||||
int major, minor;
|
int major, minor;
|
||||||
Bool havePixmaps;
|
Bool have_pixmaps;
|
||||||
if (!XShmQueryVersion(display_, &major, &minor, &havePixmaps))
|
if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) {
|
||||||
// Shared memory not supported. CaptureRect will use the XImage API instead.
|
// Shared memory not supported. CaptureRect will use the XImage API instead.
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool using_shm = false;
|
bool using_shm = false;
|
||||||
shm_segment_info_ = new XShmSegmentInfo;
|
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_->shmaddr = reinterpret_cast<char*>(-1);
|
||||||
shm_segment_info_->readOnly = False;
|
shm_segment_info_->readOnly = False;
|
||||||
x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap,
|
x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap,
|
||||||
0, shm_segment_info_, root_window_size_.width(),
|
0, shm_segment_info_, window_size_.width(),
|
||||||
root_window_size_.height());
|
window_size_.height());
|
||||||
if (x_image_) {
|
if (x_image_) {
|
||||||
shm_segment_info_->shmid = shmget(
|
shm_segment_info_->shmid = shmget(
|
||||||
IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height,
|
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 =
|
shm_segment_info_->shmaddr = x_image_->data =
|
||||||
reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0));
|
reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0));
|
||||||
if (x_image_->data != reinterpret_cast<char*>(-1)) {
|
if (x_image_->data != reinterpret_cast<char*>(-1)) {
|
||||||
EnableXServerErrorTrap();
|
XErrorTrap error_trap(display_);
|
||||||
using_shm = XShmAttach(display_, shm_segment_info_);
|
using_shm = XShmAttach(display_, shm_segment_info_);
|
||||||
XSync(display_, False);
|
XSync(display_, False);
|
||||||
if (GetLastXServerError() != 0)
|
if (error_trap.GetLastErrorAndDisable() != 0)
|
||||||
using_shm = false;
|
using_shm = false;
|
||||||
if (using_shm) {
|
if (using_shm) {
|
||||||
LOG(LS_VERBOSE) << "Using X shared memory segment "
|
LOG(LS_VERBOSE) << "Using X shared memory segment "
|
||||||
@@ -163,48 +162,52 @@ void XServerPixelBuffer::InitShm(int screen) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (havePixmaps)
|
if (have_pixmaps)
|
||||||
havePixmaps = InitPixmaps(default_depth);
|
have_pixmaps = InitPixmaps(default_depth);
|
||||||
|
|
||||||
shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
|
shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
|
||||||
shm_segment_info_->shmid = -1;
|
shm_segment_info_->shmid = -1;
|
||||||
|
|
||||||
LOG(LS_VERBOSE) << "Using X shared memory extension v"
|
LOG(LS_VERBOSE) << "Using X shared memory extension v"
|
||||||
<< major << "." << minor
|
<< major << "." << minor
|
||||||
<< " with" << (havePixmaps ? "" : "out") << " pixmaps.";
|
<< " with" << (have_pixmaps ? "" : "out") << " pixmaps.";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XServerPixelBuffer::InitPixmaps(int depth) {
|
bool XServerPixelBuffer::InitPixmaps(int depth) {
|
||||||
if (XShmPixmapFormat(display_) != ZPixmap)
|
if (XShmPixmapFormat(display_) != ZPixmap)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EnableXServerErrorTrap();
|
{
|
||||||
shm_pixmap_ = XShmCreatePixmap(display_, root_window_,
|
XErrorTrap error_trap(display_);
|
||||||
shm_segment_info_->shmaddr,
|
shm_pixmap_ = XShmCreatePixmap(display_, window_,
|
||||||
shm_segment_info_,
|
shm_segment_info_->shmaddr,
|
||||||
root_window_size_.width(),
|
shm_segment_info_,
|
||||||
root_window_size_.height(), depth);
|
window_size_.width(),
|
||||||
XSync(display_, False);
|
window_size_.height(), depth);
|
||||||
if (GetLastXServerError() != 0) {
|
XSync(display_, False);
|
||||||
// |shm_pixmap_| is not not valid because the request was not processed
|
if (error_trap.GetLastErrorAndDisable() != 0) {
|
||||||
// by the X Server, so zero it.
|
// |shm_pixmap_| is not not valid because the request was not processed
|
||||||
shm_pixmap_ = 0;
|
// by the X Server, so zero it.
|
||||||
return false;
|
shm_pixmap_ = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EnableXServerErrorTrap();
|
{
|
||||||
XGCValues shm_gc_values;
|
XErrorTrap error_trap(display_);
|
||||||
shm_gc_values.subwindow_mode = IncludeInferiors;
|
XGCValues shm_gc_values;
|
||||||
shm_gc_values.graphics_exposures = False;
|
shm_gc_values.subwindow_mode = IncludeInferiors;
|
||||||
shm_gc_ = XCreateGC(display_, root_window_,
|
shm_gc_values.graphics_exposures = False;
|
||||||
GCSubwindowMode | GCGraphicsExposures,
|
shm_gc_ = XCreateGC(display_, window_,
|
||||||
&shm_gc_values);
|
GCSubwindowMode | GCGraphicsExposures,
|
||||||
XSync(display_, False);
|
&shm_gc_values);
|
||||||
if (GetLastXServerError() != 0) {
|
XSync(display_, False);
|
||||||
XFreePixmap(display_, shm_pixmap_);
|
if (error_trap.GetLastErrorAndDisable() != 0) {
|
||||||
shm_pixmap_ = 0;
|
XFreePixmap(display_, shm_pixmap_);
|
||||||
shm_gc_ = 0; // See shm_pixmap_ comment above.
|
shm_pixmap_ = 0;
|
||||||
return false;
|
shm_gc_ = 0; // See shm_pixmap_ comment above.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -213,57 +216,110 @@ bool XServerPixelBuffer::InitPixmaps(int depth) {
|
|||||||
void XServerPixelBuffer::Synchronize() {
|
void XServerPixelBuffer::Synchronize() {
|
||||||
if (shm_segment_info_ && !shm_pixmap_) {
|
if (shm_segment_info_ && !shm_pixmap_) {
|
||||||
// XShmGetImage can fail if the display is being reconfigured.
|
// XShmGetImage can fail if the display is being reconfigured.
|
||||||
EnableXServerErrorTrap();
|
XErrorTrap error_trap(display_);
|
||||||
XShmGetImage(display_, root_window_, x_image_, 0, 0, AllPlanes);
|
XShmGetImage(display_, window_, x_image_, 0, 0, AllPlanes);
|
||||||
GetLastXServerError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* XServerPixelBuffer::CaptureRect(const DesktopRect& rect) {
|
void XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
|
||||||
assert(rect.right() <= root_window_size_.width());
|
DesktopFrame* frame) {
|
||||||
assert(rect.bottom() <= root_window_size_.height());
|
assert(rect.right() <= window_size_.width());
|
||||||
|
assert(rect.bottom() <= window_size_.height());
|
||||||
|
|
||||||
|
uint8_t* data;
|
||||||
|
|
||||||
if (shm_segment_info_) {
|
if (shm_segment_info_) {
|
||||||
if (shm_pixmap_) {
|
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(), rect.width(), rect.height(),
|
||||||
rect.left(), rect.top());
|
rect.left(), rect.top());
|
||||||
XSync(display_, False);
|
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.top() * x_image_->bytes_per_line +
|
||||||
rect.left() * x_image_->bits_per_pixel / 8;
|
rect.left() * x_image_->bits_per_pixel / 8;
|
||||||
} else {
|
} else {
|
||||||
if (x_image_)
|
if (x_image_)
|
||||||
XDestroyImage(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);
|
rect.width(), rect.height(), AllPlanes, ZPixmap);
|
||||||
return reinterpret_cast<uint8_t*>(x_image_->data);
|
data = reinterpret_cast<uint8_t*>(x_image_->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsXImageRGBFormat(x_image_)) {
|
||||||
|
FastBlit(data, rect, frame);
|
||||||
|
} else {
|
||||||
|
SlowBlit(data, rect, frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int XServerPixelBuffer::GetStride() const {
|
void XServerPixelBuffer::FastBlit(uint8_t* image,
|
||||||
return x_image_->bytes_per_line;
|
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::GetDepth() const {
|
void XServerPixelBuffer::SlowBlit(uint8_t* image,
|
||||||
return x_image_->depth;
|
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();
|
||||||
|
|
||||||
int XServerPixelBuffer::GetBitsPerPixel() const {
|
uint32_t red_mask = x_image_->red_mask;
|
||||||
return x_image_->bits_per_pixel;
|
uint32_t green_mask = x_image_->red_mask;
|
||||||
}
|
uint32_t blue_mask = x_image_->blue_mask;
|
||||||
|
|
||||||
int XServerPixelBuffer::GetRedMask() const {
|
uint32_t red_shift = MaskToShift(red_mask);
|
||||||
return x_image_->red_mask;
|
uint32_t green_shift = MaskToShift(green_mask);
|
||||||
}
|
uint32_t blue_shift = MaskToShift(blue_mask);
|
||||||
|
|
||||||
int XServerPixelBuffer::GetBlueMask() const {
|
int bits_per_pixel = x_image_->bits_per_pixel;
|
||||||
return x_image_->blue_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
int XServerPixelBuffer::GetGreenMask() const {
|
uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
|
||||||
return x_image_->green_mask;
|
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
|
} // namespace webrtc
|
||||||
|
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
class DesktopFrame;
|
||||||
|
|
||||||
// A class to allow the X server's pixel buffer to be accessed as efficiently
|
// A class to allow the X server's pixel buffer to be accessed as efficiently
|
||||||
// as possible.
|
// as possible.
|
||||||
class XServerPixelBuffer {
|
class XServerPixelBuffer {
|
||||||
@@ -29,14 +31,14 @@ class XServerPixelBuffer {
|
|||||||
|
|
||||||
void Release();
|
void Release();
|
||||||
|
|
||||||
// Allocate (or reallocate) the pixel buffer with the given size, which is
|
// Allocate (or reallocate) the pixel buffer for |window|. Returns false in
|
||||||
// assumed to be the current size of the root window.
|
// case of an error (e.g. window doesn't exist).
|
||||||
// |screen_size| should either come from GetRootWindowSize(), or
|
bool Init(Display* display, Window window);
|
||||||
// from a recent ConfigureNotify event on the root window.
|
|
||||||
void Init(Display* display, const DesktopSize& screen_size);
|
|
||||||
|
|
||||||
// Request the current size of the root window from the X Server.
|
bool is_initialized() { return window_ != 0; }
|
||||||
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
|
// If shared memory is being used without pixmaps, synchronize this pixel
|
||||||
// buffer with the root window contents (otherwise, this is a no-op).
|
// buffer with the root window contents (otherwise, this is a no-op).
|
||||||
@@ -45,31 +47,28 @@ class XServerPixelBuffer {
|
|||||||
// beginning.
|
// beginning.
|
||||||
void Synchronize();
|
void Synchronize();
|
||||||
|
|
||||||
// Capture the specified rectangle and return a pointer to its top-left pixel
|
// Capture the specified rectangle and stores it in the |frame|. In the case
|
||||||
// or NULL if capture fails. The returned pointer remains valid until the next
|
// where the full-screen data is captured by Synchronize(), this simply
|
||||||
// call to CaptureRect.
|
// returns the pointer without doing any more work. The caller must ensure
|
||||||
// In the case where the full-screen data is captured by Synchronize(), this
|
// that |rect| is not larger than window_size().
|
||||||
// simply returns the pointer without doing any more work.
|
void CaptureRect(const DesktopRect& rect, DesktopFrame* frame);
|
||||||
// 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;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitShm(int screen);
|
void InitShm(const XWindowAttributes& attributes);
|
||||||
bool InitPixmaps(int depth);
|
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_;
|
Display* display_;
|
||||||
Window root_window_;
|
Window window_;
|
||||||
DesktopSize root_window_size_;
|
DesktopSize window_size_;
|
||||||
XImage* x_image_;
|
XImage* x_image_;
|
||||||
XShmSegmentInfo* shm_segment_info_;
|
XShmSegmentInfo* shm_segment_info_;
|
||||||
Pixmap shm_pixmap_;
|
Pixmap shm_pixmap_;
|
||||||
|
Reference in New Issue
Block a user