147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org/*
247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org *  Copyright 2010 The WebRTC Project Authors. All rights reserved.
347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org *
447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org *  Use of this source code is governed by a BSD-style license
547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org *  that can be found in the LICENSE file in the root of the source
647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org *  tree. An additional intellectual property rights grant can be found
747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org *  in the file PATENTS.  All contributing project authors may
847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org *  be found in the AUTHORS file in the root of the source tree.
947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org */
1047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
11cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.org#include "webrtc/base/x11windowpicker.h"
1247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
1347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <math.h>
1447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <string.h>
1547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
1647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <algorithm>
1747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <string>
1847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
1947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <X11/Xatom.h>
2047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <X11/extensions/Xcomposite.h>
2147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <X11/extensions/Xrender.h>
2247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include <X11/Xutil.h>
2347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
2447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#include "webrtc/base/logging.h"
2547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
2647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgnamespace rtc {
2747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
2847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// Convenience wrapper for XGetWindowProperty results.
2947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgtemplate <class PropertyType>
3047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgclass XWindowProperty {
3147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org public:
3247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  XWindowProperty(Display* display, Window window, Atom property)
3347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      : data_(NULL) {
3447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    const int kBitsPerByte = 8;
3547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Atom actual_type;
3647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int actual_format;
3747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned long bytes_after;  // NOLINT: type required by XGetWindowProperty
3847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
3947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                    AnyPropertyType, &actual_type,
4047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                    &actual_format, &size_,
4147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                    &bytes_after, &data_);
4247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    succeeded_ = (status == Success);
4347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!succeeded_) {
4447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      data_ = NULL;  // Ensure nothing is freed.
4547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    } else if (sizeof(PropertyType) * kBitsPerByte != actual_format) {
4647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_WARNING) << "Returned type size differs from "
4747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          "requested type size.";
4847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      succeeded_ = false;
4947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // We still need to call XFree in this case, so leave data_ alone.
5047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
5147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!succeeded_) {
5247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      size_ = 0;
5347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
5447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
5547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
5647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  ~XWindowProperty() {
5747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (data_) {
5847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFree(data_);
5947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
6047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
6147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
6247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool succeeded() const { return succeeded_; }
6347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  size_t size() const { return size_; }
6447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  const PropertyType* data() const {
6547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return reinterpret_cast<PropertyType*>(data_);
6647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
6747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  PropertyType* data() {
6847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return reinterpret_cast<PropertyType*>(data_);
6947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
7047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
7147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org private:
7247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool succeeded_;
7347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  unsigned long size_;  // NOLINT: type required by XGetWindowProperty
7447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  unsigned char* data_;
7547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
7647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
7747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org};
7847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
7947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// Stupid X11.  It seems none of the synchronous returns codes from X11 calls
8047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// are meaningful unless an asynchronous error handler is configured.  This
8147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// RAII class registers and unregisters an X11 error handler.
8247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgclass XErrorSuppressor {
8347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org public:
8447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  explicit XErrorSuppressor(Display* display)
8547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      : display_(display), original_error_handler_(NULL) {
8647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    SuppressX11Errors();
8747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
8847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  ~XErrorSuppressor() {
8947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    UnsuppressX11Errors();
9047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
9147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
9247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org private:
9347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  static int ErrorHandler(Display* display, XErrorEvent* e) {
9447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    char buf[256];
9547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XGetErrorText(display, e->error_code, buf, sizeof buf);
9647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    LOG(LS_WARNING) << "Received X11 error \"" << buf << "\" for request code "
9747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                    << static_cast<unsigned int>(e->request_code);
9847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return 0;
9947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
10047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
10147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  void SuppressX11Errors() {
10247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XFlush(display_);
10347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XSync(display_, False);
10447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    original_error_handler_ = XSetErrorHandler(&ErrorHandler);
10547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
10647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
10747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  void UnsuppressX11Errors() {
10847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XFlush(display_);
10947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XSync(display_, False);
11047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorHandler handler = XSetErrorHandler(original_error_handler_);
11147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (handler != &ErrorHandler) {
11247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_WARNING) << "Unbalanced XSetErrorHandler() calls detected. "
11347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                      << "Final error handler may not be what you expect!";
11447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
11547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    original_error_handler_ = NULL;
11647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
11747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
11847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  Display* display_;
11947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  XErrorHandler original_error_handler_;
12047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
12147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  DISALLOW_COPY_AND_ASSIGN(XErrorSuppressor);
12247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org};
12347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
12447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// Hiding all X11 specifics inside its own class. This to avoid
12547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org// conflicts between talk and X11 header declarations.
12647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.orgclass XWindowEnumerator {
12747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org public:
12847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  XWindowEnumerator()
12947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      : display_(NULL),
13047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        has_composite_extension_(false),
13147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        has_render_extension_(false) {
13247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
13347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
13447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  ~XWindowEnumerator() {
13547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (display_ != NULL) {
13647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XCloseDisplay(display_);
13747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
13847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
13947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
14047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool Init() {
14147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (display_ != NULL) {
14247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // Already initialized.
14347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return true;
14447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
14547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    display_ = XOpenDisplay(NULL);
14647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (display_ == NULL) {
14747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "Failed to open display.";
14847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
14947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
15047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
15147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
15247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
15347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    wm_state_ = XInternAtom(display_, "WM_STATE", True);
15447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    net_wm_icon_ = XInternAtom(display_, "_NET_WM_ICON", False);
15547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
15647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int event_base, error_base, major_version, minor_version;
15747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (XCompositeQueryExtension(display_, &event_base, &error_base) &&
15847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        XCompositeQueryVersion(display_, &major_version, &minor_version) &&
15947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        // XCompositeNameWindowPixmap() requires version 0.2
16047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        (major_version > 0 || minor_version >= 2)) {
16147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      has_composite_extension_ = true;
16247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    } else {
16347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_INFO) << "Xcomposite extension not available or too old.";
16447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
16547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
16647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (XRenderQueryExtension(display_, &event_base, &error_base) &&
16747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        XRenderQueryVersion(display_, &major_version, &minor_version) &&
16847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        // XRenderSetPictureTransform() requires version 0.6
16947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        (major_version > 0 || minor_version >= 6)) {
17047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      has_render_extension_ = true;
17147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    } else {
17247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_INFO) << "Xrender extension not available or too old.";
17347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
17447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return true;
17547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
17647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
17747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool EnumerateWindows(WindowDescriptionList* descriptions) {
17847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
17947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
18047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
18147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
18247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int num_screens = XScreenCount(display_);
18347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    bool result = false;
18447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    for (int i = 0; i < num_screens; ++i) {
18547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      if (EnumerateScreenWindows(descriptions, i)) {
18647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        // We know we succeded on at least one screen.
18747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        result = true;
18847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      }
18947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
19047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return result;
19147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
19247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
19347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool EnumerateDesktops(DesktopDescriptionList* descriptions) {
19447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
19547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
19647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
19747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
19847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window default_root_window = XDefaultRootWindow(display_);
19947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int num_screens = XScreenCount(display_);
20047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    for (int i = 0; i < num_screens; ++i) {
20147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      Window root_window = XRootWindow(display_, i);
20247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      DesktopId id(DesktopId(root_window, i));
20347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // TODO: Figure out an appropriate desktop title.
20447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      DesktopDescription desc(id, "");
20547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      desc.set_primary(root_window == default_root_window);
20647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      descriptions->push_back(desc);
20747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
20847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return num_screens > 0;
20947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
21047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
21147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool IsVisible(const WindowId& id) {
21247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
21347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
21447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
21547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
21647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XWindowAttributes attr;
21747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!XGetWindowAttributes(display_, id.id(), &attr)) {
21847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
21947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
22047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
22147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return attr.map_state == IsViewable;
22247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
22347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
22447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool MoveToFront(const WindowId& id) {
22547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
22647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
22747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
22847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
22947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned int num_children;
23047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window* children;
23147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window parent;
23247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window root;
23347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
23447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Find root window to pass event to.
23547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int status = XQueryTree(display_, id.id(), &root, &parent, &children,
23647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                            &num_children);
23747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (status == 0) {
23847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_WARNING) << "Failed to query for child windows.";
23947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
24047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
24147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (children != NULL) {
24247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFree(children);
24347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
24447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
24547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Move the window to front.
24647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRaiseWindow(display_, id.id());
24747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
24847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Some window managers (e.g., metacity in GNOME) consider it illegal to
24947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // raise a window without also giving it input focus with
25047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
25147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Atom atom = XInternAtom(display_, "_NET_ACTIVE_WINDOW", True);
25247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (atom != None) {
25347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XEvent xev;
25447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      long event_mask;
25547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
25647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.type = ClientMessage;
25747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.serial = 0;
25847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.send_event = True;
25947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.window = id.id();
26047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.message_type = atom;
26147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
26247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // The format member is set to 8, 16, or 32 and specifies whether the
26347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // data should be viewed as a list of bytes, shorts, or longs.
26447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.format = 32;
26547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
26647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.data.l[0] = 0;
26747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.data.l[1] = 0;
26847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.data.l[2] = 0;
26947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.data.l[3] = 0;
27047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      xev.xclient.data.l[4] = 0;
27147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
27247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      event_mask = SubstructureRedirectMask | SubstructureNotifyMask;
27347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
27447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XSendEvent(display_, root, False, event_mask, &xev);
27547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
27647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XFlush(display_);
27747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return true;
27847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
27947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
28047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  uint8* GetWindowIcon(const WindowId& id, int* width, int* height) {
28147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
28247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
28347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
28447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
28547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Atom ret_type;
28647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int format;
28747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned long length, bytes_after, size;
28847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned char* data = NULL;
28947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
29047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Find out the size of the icon data.
29147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (XGetWindowProperty(
29247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org            display_, id.id(), net_wm_icon_, 0, 0, False, XA_CARDINAL,
29347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org            &ret_type, &format, &length, &size, &data) == Success &&
29447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        data) {
29547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFree(data);
29647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    } else {
29747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "Failed to get size of the icon.";
29847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
29947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
30047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Get the icon data, the format is one uint32 each for width and height,
30147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // followed by the actual pixel data.
30247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (size >= 2 &&
30347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        XGetWindowProperty(
30447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org            display_, id.id(), net_wm_icon_, 0, size, False, XA_CARDINAL,
30547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org            &ret_type, &format, &length, &bytes_after, &data) == Success &&
30647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        data) {
30747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      uint32* data_ptr = reinterpret_cast<uint32*>(data);
30847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      int w, h;
30947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      w = data_ptr[0];
31047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      h = data_ptr[1];
31147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      if (size < static_cast<unsigned long>(w * h + 2)) {
31247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        XFree(data);
31347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        LOG(LS_ERROR) << "Not a vaild icon.";
31447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        return NULL;
31547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      }
31647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      uint8* rgba =
31747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true);
31847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFree(data);
31947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      *width = w;
32047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      *height = h;
32147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return rgba;
32247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    } else {
32347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "Failed to get window icon data.";
32447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
32547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
32647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
32747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
32847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  uint8* GetWindowThumbnail(const WindowId& id, int width, int height) {
32947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
33047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
33147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
33247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
33347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!has_composite_extension_) {
33447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // Without the Xcomposite extension we would only get a good thumbnail if
33547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // the whole window is visible on screen and not covered by any
33647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // other window. This is not something we want so instead, just
33747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // bail out.
33847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_INFO) << "No Xcomposite extension detected.";
33947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
34047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
34147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
34247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
34347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window root;
34447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int x;
34547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int y;
34647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned int src_width;
34747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned int src_height;
34847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned int border_width;
34947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned int depth;
35047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
35147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // In addition to needing X11 server-side support for Xcomposite, it
35247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // actually needs to be turned on for this window in order to get a good
35347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // thumbnail. If the user has modern hardware/drivers but isn't using a
35447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // compositing window manager, that won't be the case. Here we
35547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // automatically turn it on for shareable windows so that we can get
35647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // thumbnails. We used to avoid it because the transition is visually ugly,
35747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // but recent window managers don't always redirect windows which led to
35847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // no thumbnails at all, which is a worse experience.
35947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
36047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Redirect drawing to an offscreen buffer (ie, turn on compositing).
36147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // X11 remembers what has requested this and will turn it off for us when
36247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // we exit.
36347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic);
36447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id());
36547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!src_pixmap) {
36647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // Even if the backing pixmap doesn't exist, this still should have
36747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // succeeded and returned a valid handle (it just wouldn't be a handle to
36847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // anything). So this is a real error path.
36947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed";
37047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
37147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
37247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!XGetGeometry(display_, src_pixmap, &root, &x, &y,
37347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                      &src_width, &src_height, &border_width,
37447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                      &depth)) {
37547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // If the window does not actually have a backing pixmap, this is the path
37647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // that will "fail", so it's a warning rather than an error.
37747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in "
37847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                      << "use)";
37947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFreePixmap(display_, src_pixmap);
38047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
38147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
38247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
38347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // If we get to here, then composite is in use for this window and it has a
38447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // valid backing pixmap.
38547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
38647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XWindowAttributes attr;
38747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!XGetWindowAttributes(display_, id.id(), &attr)) {
38847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
38947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFreePixmap(display_, src_pixmap);
39047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
39147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
39247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
39347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    uint8* data = GetDrawableThumbnail(src_pixmap,
39447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       attr.visual,
39547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       src_width,
39647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       src_height,
39747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       width,
39847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       height);
39947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XFreePixmap(display_, src_pixmap);
40047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return data;
40147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
40247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
40347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  int GetNumDesktops() {
40447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
40547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return -1;
40647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
40747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
40847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return XScreenCount(display_);
40947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
41047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
41147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height) {
41247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
41347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
41447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
41547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
41647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
41747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window root_window = id.id();
41847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XWindowAttributes attr;
41947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!XGetWindowAttributes(display_, root_window, &attr)) {
42047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
42147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
42247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
42347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
42447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return GetDrawableThumbnail(root_window,
42547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                attr.visual,
42647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                attr.width,
42747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                attr.height,
42847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                width,
42947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                height);
43047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
43147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
43247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) {
43347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!Init()) {
43447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
43547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
43647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XErrorSuppressor error_suppressor(display_);
43747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XWindowAttributes attr;
43847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!XGetWindowAttributes(display_, id.id(), &attr)) {
43947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
44047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
44147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
44247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    *width = attr.width;
44347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    *height = attr.height;
44447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return true;
44547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
44647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
44747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org private:
44847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  uint8* GetDrawableThumbnail(Drawable src_drawable,
44947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              Visual* visual,
45047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              int src_width,
45147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              int src_height,
45247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              int dst_width,
45347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              int dst_height) {
45447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!has_render_extension_) {
45547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // Without the Xrender extension we would have to read the full window and
45647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // scale it down in our process. Xrender is over a decade old so we aren't
45747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // going to expend effort to support that situation. We still need to
45847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // check though because probably some virtual VNC displays are in this
45947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // category.
46047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_INFO) << "No Xrender extension detected.";
46147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
46247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
46347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
46447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderPictFormat* format = XRenderFindVisualFormat(display_,
46547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                                        visual);
46647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!format) {
46747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XRenderFindVisualFormat() failed";
46847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
46947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
47047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
47147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Create a picture to reference the window pixmap.
47247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderPictureAttributes pa;
47347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    pa.subwindow_mode = IncludeInferiors;  // Don't clip child widgets
47447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Picture src = XRenderCreatePicture(display_,
47547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       src_drawable,
47647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       format,
47747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       CPSubwindowMode,
47847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                       &pa);
47947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!src) {
48047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XRenderCreatePicture() failed";
48147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
48247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
48347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
48447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Create a picture to reference the destination pixmap.
48547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Pixmap dst_pixmap = XCreatePixmap(display_,
48647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                      src_drawable,
48747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                      dst_width,
48847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                      dst_height,
48947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                      format->depth);
49047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!dst_pixmap) {
49147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XCreatePixmap() failed";
49247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XRenderFreePicture(display_, src);
49347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
49447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
49547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
49647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL);
49747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!dst) {
49847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XRenderCreatePicture() failed";
49947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFreePixmap(display_, dst_pixmap);
50047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XRenderFreePicture(display_, src);
50147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return NULL;
50247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
50347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
50447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Clear the background.
50547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderColor transparent = {0};
50647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderFillRectangle(display_,
50747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         PictOpSrc,
50847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         dst,
50947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         &transparent,
51047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         0,
51147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         0,
51247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         dst_width,
51347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         dst_height);
51447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
51547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Calculate how much we need to scale the image.
51647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    double scale_x = static_cast<double>(dst_width) /
51747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        static_cast<double>(src_width);
51847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    double scale_y = static_cast<double>(dst_height) /
51947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        static_cast<double>(src_height);
52047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    double scale = rtc::_min(scale_y, scale_x);
52147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
52247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int scaled_width = round(src_width * scale);
52347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int scaled_height = round(src_height * scale);
52447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
52547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Render the thumbnail centered on both axis.
52647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int centered_x = (dst_width - scaled_width) / 2;
52747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int centered_y = (dst_height - scaled_height) / 2;
52847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
52947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Scaling matrix
53047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XTransform xform = { {
53147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) },
53247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) },
53347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) }
53447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        } };
53547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderSetPictureTransform(display_, src, &xform);
53647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
53747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Apply filter to smooth out the image.
53847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0);
53947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
54047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Render the image to the destination picture.
54147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderComposite(display_,
54247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     PictOpSrc,
54347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     src,
54447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     None,
54547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     dst,
54647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     0,
54747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     0,
54847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     0,
54947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     0,
55047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     centered_x,
55147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     centered_y,
55247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     scaled_width,
55347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                     scaled_height);
55447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
55547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // Get the pixel data from the X server. TODO: XGetImage
55647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // might be slow here, compare with ShmGetImage.
55747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XImage* image = XGetImage(display_,
55847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              dst_pixmap,
55947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              0,
56047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              0,
56147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              dst_width,
56247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              dst_height,
56347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                              AllPlanes, ZPixmap);
56447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    uint8* data = ArgbToRgba(reinterpret_cast<uint32*>(image->data),
56547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                             centered_x,
56647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                             centered_y,
56747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                             scaled_width,
56847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                             scaled_height,
56947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                             dst_width,
57047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                             dst_height,
57147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                             false);
57247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XDestroyImage(image);
57347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderFreePicture(display_, dst);
57447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XFreePixmap(display_, dst_pixmap);
57547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XRenderFreePicture(display_, src);
57647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return data;
57747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
57847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
57947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  uint8* ArgbToRgba(uint32* argb_data, int x, int y, int w, int h,
58047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                    int stride_x, int stride_y, bool has_alpha) {
58147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    uint8* p;
58247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int len = stride_x * stride_y * 4;
58347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    uint8* data = new uint8[len];
58447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    memset(data, 0, len);
58547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    p = data + 4 * (y * stride_x + x);
58647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    for (int i = 0; i < h; ++i) {
58747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      for (int j = 0; j < w; ++j) {
58847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        uint32 argb;
58947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        uint32 rgba;
59047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        argb = argb_data[stride_x * (y + i) + x + j];
59147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        rgba = (argb << 8) | (argb >> 24);
59247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        *p = rgba >> 24;
59347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        ++p;
59447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        *p = (rgba >> 16) & 0xff;
59547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        ++p;
59647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        *p = (rgba >> 8) & 0xff;
59747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        ++p;
59847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        *p = has_alpha ? rgba & 0xFF : 0xFF;
59947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        ++p;
60047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      }
60147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      p += (stride_x - w) * 4;
60247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
60347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return data;
60447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
60547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
60647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) {
60747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window parent;
60847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window *children;
60947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int status;
61047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned int num_children;
61147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window root_window = XRootWindow(display_, screen);
61247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    status = XQueryTree(display_, root_window, &root_window, &parent, &children,
61347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                        &num_children);
61447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (status == 0) {
61547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "Failed to query for child windows.";
61647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return false;
61747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
61847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    for (unsigned int i = 0; i < num_children; ++i) {
61947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // Iterate in reverse order to display windows from front to back.
62047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#ifdef CHROMEOS
62147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to
62247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      // filter, just return all windows and let the picker scan through them.
62347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      Window app_window = children[num_children - 1 - i];
62447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#else
62547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      Window app_window = GetApplicationWindow(children[num_children - 1 - i]);
62647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org#endif
62747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      if (app_window &&
628cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.org          !X11WindowPicker::IsDesktopElement(display_, app_window)) {
62947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        std::string title;
63047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        if (GetWindowTitle(app_window, &title)) {
63147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          WindowId id(app_window);
63247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          WindowDescription desc(id, title);
63347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          descriptions->push_back(desc);
63447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        }
63547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      }
63647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
63747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (children != NULL) {
63847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFree(children);
63947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
64047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return true;
64147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
64247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
64347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool GetWindowTitle(Window window, std::string* title) {
64447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int status;
64547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    bool result = false;
64647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XTextProperty window_name;
64747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    window_name.value = NULL;
64847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (window) {
64947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      status = XGetWMName(display_, window, &window_name);
65047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      if (status && window_name.value && window_name.nitems) {
65147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        int cnt;
65247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        char **list = NULL;
65347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        status = Xutf8TextPropertyToTextList(display_, &window_name, &list,
65447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                             &cnt);
65547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        if (status >= Success && cnt && *list) {
65647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          if (cnt > 1) {
65747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org            LOG(LS_INFO) << "Window has " << cnt
65847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                         << " text properties, only using the first one.";
65947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          }
66047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          *title = *list;
66147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          result = true;
66247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        }
66347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        if (list != NULL) {
66447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org          XFreeStringList(list);
66547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        }
66647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      }
66747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      if (window_name.value != NULL) {
66847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        XFree(window_name.value);
66947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      }
67047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
67147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return result;
67247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
67347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
67447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  Window GetApplicationWindow(Window window) {
67547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window root, parent;
67647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window app_window = 0;
67747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Window *children;
67847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned int num_children;
67947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Atom type = None;
68047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int format;
68147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned long nitems, after;
68247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    unsigned char *data;
68347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
68447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    int ret = XGetWindowProperty(display_, window,
68547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                 wm_state_, 0L, 2,
68647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                 False, wm_state_, &type, &format,
68747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                 &nitems, &after, &data);
68847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (ret != Success) {
68947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret
69047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                    << " for window " << window << ".";
69147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return 0;
69247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
69347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (type != None) {
69447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      int64 state = static_cast<int64>(*data);
69547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFree(data);
69647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return state == NormalState ? window : 0;
69747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
69847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    XFree(data);
69947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (!XQueryTree(display_, window, &root, &parent, &children,
70047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                    &num_children)) {
70147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      LOG(LS_ERROR) << "Failed to query for child windows although window"
70247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                    << "does not have a valid WM_STATE.";
70347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      return 0;
70447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
70547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    for (unsigned int i = 0; i < num_children; ++i) {
70647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      app_window = GetApplicationWindow(children[i]);
70747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      if (app_window) {
70847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        break;
70947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      }
71047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
71147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    if (children != NULL) {
71247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      XFree(children);
71347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    }
71447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return app_window;
71547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
71647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
71747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  Atom wm_state_;
71847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  Atom net_wm_icon_;
71947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  Display* display_;
72047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool has_composite_extension_;
72147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool has_render_extension_;
72247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org};
72347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
724cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgX11WindowPicker::X11WindowPicker() : enumerator_(new XWindowEnumerator()) {
72547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
72647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
727cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgX11WindowPicker::~X11WindowPicker() {
72847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
72947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
730cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgbool X11WindowPicker::IsDesktopElement(_XDisplay* display, Window window) {
73147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  if (window == 0) {
73247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    LOG(LS_WARNING) << "Zero is never a valid window.";
73347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return false;
73447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
73547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
73647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  // First look for _NET_WM_WINDOW_TYPE. The standard
73747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
73847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  // says this hint *should* be present on all windows, and we use the existence
73947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
74047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  // a desktop element (that is, only "normal" windows should be shareable).
74147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True);
74247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  XWindowProperty<uint32_t> window_type(display, window, window_type_atom);
74347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  if (window_type.succeeded() && window_type.size() > 0) {
74447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    Atom normal_window_type_atom = XInternAtom(
74547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        display, "_NET_WM_WINDOW_TYPE_NORMAL", True);
74647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    uint32_t* end = window_type.data() + window_type.size();
74747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    bool is_normal = (end != std::find(
74847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org        window_type.data(), end, normal_window_type_atom));
74947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return !is_normal;
75047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
75147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
75247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  // Fall back on using the hint.
75347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  XClassHint class_hint;
75447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  Status s = XGetClassHint(display, window, &class_hint);
75547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  bool result = false;
75647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  if (s == 0) {
75747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    // No hints, assume this is a normal application window.
75847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    return result;
75947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
76047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  static const std::string gnome_panel("gnome-panel");
76147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  static const std::string desktop_window("desktop_window");
76247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
76347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  if (gnome_panel.compare(class_hint.res_name) == 0 ||
76447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org      desktop_window.compare(class_hint.res_name) == 0) {
76547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org    result = true;
76647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  }
76747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  XFree(class_hint.res_name);
76847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  XFree(class_hint.res_class);
76947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return result;
77047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
77147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
772cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgbool X11WindowPicker::Init() {
77347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->Init();
77447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
77547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
776cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgbool X11WindowPicker::GetWindowList(WindowDescriptionList* descriptions) {
77747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->EnumerateWindows(descriptions);
77847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
77947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
780cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgbool X11WindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) {
78147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->EnumerateDesktops(descriptions);
78247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
78347be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
784cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgbool X11WindowPicker::IsVisible(const WindowId& id) {
78547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->IsVisible(id);
78647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
78747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
788cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgbool X11WindowPicker::MoveToFront(const WindowId& id) {
78947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->MoveToFront(id);
79047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
79147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
79247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
793cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orguint8* X11WindowPicker::GetWindowIcon(const WindowId& id, int* width,
79447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                        int* height) {
79547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->GetWindowIcon(id, width, height);
79647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
79747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
798cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orguint8* X11WindowPicker::GetWindowThumbnail(const WindowId& id, int width,
79947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                             int height) {
80047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->GetWindowThumbnail(id, width, height);
80147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
80247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
803cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgint X11WindowPicker::GetNumDesktops() {
80447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->GetNumDesktops();
80547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
80647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
807cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orguint8* X11WindowPicker::GetDesktopThumbnail(const DesktopId& id,
80847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                              int width,
80947be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                              int height) {
81047be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->GetDesktopThumbnail(id, width, height);
81147be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
81247be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
813cdc312345fcdfc586a7c8cd720407449cc0bdcd2henrike@webrtc.orgbool X11WindowPicker::GetDesktopDimensions(const DesktopId& id, int* width,
81447be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org                                             int* height) {
81547be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org  return enumerator_->GetDesktopDimensions(id, width, height);
81647be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}
81747be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org
81847be73b8629244d6bb63a28198f97f040ce53d21henrike@webrtc.org}  // namespace rtc
819