16c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org/*
26c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
36c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org *
46c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org *  Use of this source code is governed by a BSD-style license
56c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org *  that can be found in the LICENSE file in the root of the source
66c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org *  tree. An additional intellectual property rights grant can be found
76c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org *  in the file PATENTS.  All contributing project authors may
86c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org *  be found in the AUTHORS file in the root of the source tree.
96c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org */
106c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
116c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
126c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
133f45c2e0ac4cb280f941efa3a3476895795e3dd6pbos@webrtc.org#include <assert.h>
14e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include <string.h>
156c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org#include <sys/shm.h>
166c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
17e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_frame.h"
18e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
196c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org#include "webrtc/system_wrappers/interface/logging.h"
206c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
216c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.orgnamespace {
226c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
23e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org// Returns the number of bits |mask| has to be shifted left so its last
24e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org// (most-significant) bit set becomes the most-significant bit of the word.
25e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org// When |mask| is 0 the function returns 31.
26e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orguint32_t MaskToShift(uint32_t mask) {
27e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int shift = 0;
28e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if ((mask & 0xffff0000u) == 0) {
29e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    mask <<= 16;
30e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shift += 16;
31e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
32e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if ((mask & 0xff000000u) == 0) {
33e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    mask <<= 8;
34e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shift += 8;
35e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
36e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if ((mask & 0xf0000000u) == 0) {
37e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    mask <<= 4;
38e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shift += 4;
39e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
40e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if ((mask & 0xc0000000u) == 0) {
41e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    mask <<= 2;
42e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shift += 2;
43e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
44e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if ((mask & 0x80000000u) == 0)
45e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shift += 1;
466c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
47e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return shift;
486c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
496c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
50e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org// Returns true if |image| is in RGB format.
51e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgbool IsXImageRGBFormat(XImage* image) {
52e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return image->bits_per_pixel == 32 &&
53e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      image->red_mask == 0xff0000 &&
54e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      image->green_mask == 0xff00 &&
55e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      image->blue_mask == 0xff;
566c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
576c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
586c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}  // namespace
596c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
606c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.orgnamespace webrtc {
616c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
626c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.orgXServerPixelBuffer::XServerPixelBuffer()
63e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    : display_(NULL), window_(0),
646c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      x_image_(NULL),
656c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) {
666c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
676c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
686c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.orgXServerPixelBuffer::~XServerPixelBuffer() {
696c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  Release();
706c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
716c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
726c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.orgvoid XServerPixelBuffer::Release() {
736c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (x_image_) {
746c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    XDestroyImage(x_image_);
756c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    x_image_ = NULL;
766c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
776c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (shm_pixmap_) {
786c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    XFreePixmap(display_, shm_pixmap_);
796c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    shm_pixmap_ = 0;
806c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
816c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (shm_gc_) {
826c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    XFreeGC(display_, shm_gc_);
836c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    shm_gc_ = NULL;
846c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
856c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (shm_segment_info_) {
866c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    if (shm_segment_info_->shmaddr != reinterpret_cast<char*>(-1))
876c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      shmdt(shm_segment_info_->shmaddr);
886c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    if (shm_segment_info_->shmid != -1)
896c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
906c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    delete shm_segment_info_;
916c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    shm_segment_info_ = NULL;
926c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
93e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  window_ = 0;
946c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
956c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
96e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgbool XServerPixelBuffer::Init(Display* display, Window window) {
976c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  Release();
986c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  display_ = display;
996c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
100e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  XWindowAttributes attributes;
101e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  {
102e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XErrorTrap error_trap(display_);
103e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (!XGetWindowAttributes(display_, window, &attributes) ||
104e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        error_trap.GetLastErrorAndDisable() != 0) {
105e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      return false;
106e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
107e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
108e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
109e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  window_size_ = DesktopSize(attributes.width, attributes.height);
110e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  window_ = window;
111e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  InitShm(attributes);
112e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
113e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return true;
1146c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
1156c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
116e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgvoid XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) {
117e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  Visual* default_visual = attributes.visual;
118e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int default_depth = attributes.depth;
1196c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
1206c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  int major, minor;
121e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  Bool have_pixmaps;
122e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) {
1236c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    // Shared memory not supported. CaptureRect will use the XImage API instead.
1246c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    return;
125e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
1266c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
1276c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  bool using_shm = false;
1286c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  shm_segment_info_ = new XShmSegmentInfo;
1296c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  shm_segment_info_->shmid = -1;
1306c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  shm_segment_info_->shmaddr = reinterpret_cast<char*>(-1);
1316c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  shm_segment_info_->readOnly = False;
1326c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap,
133e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                             0, shm_segment_info_, window_size_.width(),
134e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                             window_size_.height());
1356c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (x_image_) {
1366c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    shm_segment_info_->shmid = shmget(
1376c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height,
1386c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        IPC_CREAT | 0600);
1396c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    if (shm_segment_info_->shmid != -1) {
1406c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      shm_segment_info_->shmaddr = x_image_->data =
1416c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org          reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0));
1426c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      if (x_image_->data != reinterpret_cast<char*>(-1)) {
143e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        XErrorTrap error_trap(display_);
1446c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        using_shm = XShmAttach(display_, shm_segment_info_);
1456c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        XSync(display_, False);
146e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        if (error_trap.GetLastErrorAndDisable() != 0)
1476c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org          using_shm = false;
1486c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        if (using_shm) {
1496c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org          LOG(LS_VERBOSE) << "Using X shared memory segment "
1506c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org                          << shm_segment_info_->shmid;
1516c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        }
1526c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      }
1536c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    } else {
1546c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      LOG(LS_WARNING) << "Failed to get shared memory segment. "
1556c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org                      "Performance may be degraded.";
1566c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    }
1576c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
1586c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
1596c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (!using_shm) {
1606c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    LOG(LS_WARNING) << "Not using shared memory. Performance may be degraded.";
1616c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    Release();
1626c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    return;
1636c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
1646c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
165e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (have_pixmaps)
166e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    have_pixmaps = InitPixmaps(default_depth);
1676c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
1686c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
1696c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  shm_segment_info_->shmid = -1;
1706c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
1716c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  LOG(LS_VERBOSE) << "Using X shared memory extension v"
1726c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org                  << major << "." << minor
173e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                  << " with" << (have_pixmaps ? "" : "out") << " pixmaps.";
1746c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
1756c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
1766c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.orgbool XServerPixelBuffer::InitPixmaps(int depth) {
1776c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (XShmPixmapFormat(display_) != ZPixmap)
1786c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    return false;
1796c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
180e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  {
181e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XErrorTrap error_trap(display_);
182e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shm_pixmap_ = XShmCreatePixmap(display_, window_,
183e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                   shm_segment_info_->shmaddr,
184e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                   shm_segment_info_,
185e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                   window_size_.width(),
186e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                   window_size_.height(), depth);
187e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XSync(display_, False);
188e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (error_trap.GetLastErrorAndDisable() != 0) {
189e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      // |shm_pixmap_| is not not valid because the request was not processed
190e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      // by the X Server, so zero it.
191e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      shm_pixmap_ = 0;
192e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      return false;
193e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
1946c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
1956c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
196e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  {
197e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XErrorTrap error_trap(display_);
198e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XGCValues shm_gc_values;
199e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shm_gc_values.subwindow_mode = IncludeInferiors;
200e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shm_gc_values.graphics_exposures = False;
201e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    shm_gc_ = XCreateGC(display_, window_,
202e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                        GCSubwindowMode | GCGraphicsExposures,
203e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                        &shm_gc_values);
204e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XSync(display_, False);
205e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (error_trap.GetLastErrorAndDisable() != 0) {
206e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      XFreePixmap(display_, shm_pixmap_);
207e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      shm_pixmap_ = 0;
208e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      shm_gc_ = 0;  // See shm_pixmap_ comment above.
209e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      return false;
210e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
2116c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
2126c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
2136c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  return true;
2146c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
2156c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
216dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.orgbool XServerPixelBuffer::IsWindowValid() const {
217dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org  XWindowAttributes attributes;
218dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org  {
219dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org    XErrorTrap error_trap(display_);
220dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org    if (!XGetWindowAttributes(display_, window_, &attributes) ||
221dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org        error_trap.GetLastErrorAndDisable() != 0) {
222dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org      return false;
223dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org    }
224dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org  }
225dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org  return true;
226dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org}
227dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org
2286c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.orgvoid XServerPixelBuffer::Synchronize() {
2296c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (shm_segment_info_ && !shm_pixmap_) {
2306c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    // XShmGetImage can fail if the display is being reconfigured.
231e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XErrorTrap error_trap(display_);
232e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XShmGetImage(display_, window_, x_image_, 0, 0, AllPlanes);
2336c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
2346c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
2356c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
236e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgvoid XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
237e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                     DesktopFrame* frame) {
238e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  assert(rect.right() <= window_size_.width());
239e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  assert(rect.bottom() <= window_size_.height());
240e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
241e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint8_t* data;
2426c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
2436c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  if (shm_segment_info_) {
2446c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    if (shm_pixmap_) {
245e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      XCopyArea(display_, window_, shm_pixmap_, shm_gc_,
2466c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org                rect.left(), rect.top(), rect.width(), rect.height(),
2476c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org                rect.left(), rect.top());
2486c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      XSync(display_, False);
2496c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    }
250e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    data = reinterpret_cast<uint8_t*>(x_image_->data) +
2516c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        rect.top() * x_image_->bytes_per_line +
2526c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org        rect.left() * x_image_->bits_per_pixel / 8;
2536c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  } else {
2546c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org    if (x_image_)
2556c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org      XDestroyImage(x_image_);
256e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    x_image_ = XGetImage(display_, window_, rect.left(), rect.top(),
2576c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org                         rect.width(), rect.height(), AllPlanes, ZPixmap);
258e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    data = reinterpret_cast<uint8_t*>(x_image_->data);
2596c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org  }
2606c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
261e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (IsXImageRGBFormat(x_image_)) {
262e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    FastBlit(data, rect, frame);
263e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  } else {
264e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    SlowBlit(data, rect, frame);
265e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
2666c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
2676c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
268e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgvoid XServerPixelBuffer::FastBlit(uint8_t* image,
269e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                  const DesktopRect& rect,
270e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                  DesktopFrame* frame) {
271e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint8_t* src_pos = image;
272e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int src_stride = x_image_->bytes_per_line;
273e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int dst_x = rect.left(), dst_y = rect.top();
274e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
275e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
276e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
277e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
278e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int height = rect.height();
279e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel;
280e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  for (int y = 0; y < height; ++y) {
281e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    memcpy(dst_pos, src_pos, row_bytes);
282e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    src_pos += src_stride;
283e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    dst_pos += frame->stride();
284e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
2856c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
2866c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
287e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgvoid XServerPixelBuffer::SlowBlit(uint8_t* image,
288e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                  const DesktopRect& rect,
289e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                  DesktopFrame* frame) {
290e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int src_stride = x_image_->bytes_per_line;
291e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int dst_x = rect.left(), dst_y = rect.top();
292e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int width = rect.width(), height = rect.height();
293e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
294e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint32_t red_mask = x_image_->red_mask;
295e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint32_t green_mask = x_image_->red_mask;
296e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint32_t blue_mask = x_image_->blue_mask;
297e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
298e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint32_t red_shift = MaskToShift(red_mask);
299e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint32_t green_shift = MaskToShift(green_mask);
300e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint32_t blue_shift = MaskToShift(blue_mask);
301e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
302e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int bits_per_pixel = x_image_->bits_per_pixel;
303e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
304e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
305e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  uint8_t* src_pos = image;
306e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
307e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // TODO(hclam): Optimize, perhaps using MMX code or by converting to
308e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // YUV directly.
309e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // TODO(sergeyu): This code doesn't handle XImage byte order properly and
310e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // won't work with 24bpp images. Fix it.
311e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  for (int y = 0; y < height; y++) {
312e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
313e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
314e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
315e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    for (int x = 0; x < width; x++) {
316e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      // Dereference through an appropriately-aligned pointer.
317e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      uint32_t pixel;
318e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      if (bits_per_pixel == 32) {
319e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        pixel = src_pos_32[x];
320e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      } else if (bits_per_pixel == 16) {
321e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        pixel = src_pos_16[x];
322e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      } else {
323e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        pixel = src_pos[x];
324e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      }
325e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      uint32_t r = (pixel & red_mask) << red_shift;
326e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      uint32_t g = (pixel & green_mask) << green_shift;
327e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      uint32_t b = (pixel & blue_mask) << blue_shift;
328e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      // Write as 32-bit RGB.
329e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) |
330e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org          ((b >> 24) & 0xff);
331e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
332e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    dst_pos += frame->stride();
333e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    src_pos += src_stride;
334e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
3356c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}
3366c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org
3376c82a7ea6ea92e8c68b37112186fd928b11ddc49sergeyu@chromium.org}  // namespace webrtc
338