x11windowpicker.cc revision f048872e915a3ee229044ec4bc541f6cbf9e4de1
1f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org/* 2f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org * Copyright 2010 The WebRTC Project Authors. All rights reserved. 3f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org * 4f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org * Use of this source code is governed by a BSD-style license 5f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org * that can be found in the LICENSE file in the root of the source 6f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org * tree. An additional intellectual property rights grant can be found 7f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org * in the file PATENTS. All contributing project authors may 8f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org * be found in the AUTHORS file in the root of the source tree. 9f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org */ 10f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 11f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include "webrtc/base/linuxwindowpicker.h" 12f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 13f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <math.h> 14f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <string.h> 15f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 16f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <algorithm> 17f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <string> 18f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 19f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <X11/Xatom.h> 20f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <X11/extensions/Xcomposite.h> 21f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <X11/extensions/Xrender.h> 22f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include <X11/Xutil.h> 23f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 24f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#include "webrtc/base/logging.h" 25f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 26f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgnamespace rtc { 27f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 28f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org// Convenience wrapper for XGetWindowProperty results. 29f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgtemplate <class PropertyType> 30f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgclass XWindowProperty { 31f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org public: 32f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XWindowProperty(Display* display, Window window, Atom property) 33f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org : data_(NULL) { 34f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org const int kBitsPerByte = 8; 35f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom actual_type; 36f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int actual_format; 37f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty 38f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int status = XGetWindowProperty(display, window, property, 0L, ~0L, False, 39f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org AnyPropertyType, &actual_type, 40f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &actual_format, &size_, 41f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &bytes_after, &data_); 42f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org succeeded_ = (status == Success); 43f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!succeeded_) { 44f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org data_ = NULL; // Ensure nothing is freed. 45f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } else if (sizeof(PropertyType) * kBitsPerByte != actual_format) { 46f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_WARNING) << "Returned type size differs from " 47f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org "requested type size."; 48f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org succeeded_ = false; 49f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // We still need to call XFree in this case, so leave data_ alone. 50f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 51f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!succeeded_) { 52f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org size_ = 0; 53f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 54f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 55f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 56f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ~XWindowProperty() { 57f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (data_) { 58f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(data_); 59f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 60f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 61f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 62f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool succeeded() const { return succeeded_; } 63f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org size_t size() const { return size_; } 64f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org const PropertyType* data() const { 65f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return reinterpret_cast<PropertyType*>(data_); 66f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 67f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org PropertyType* data() { 68f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return reinterpret_cast<PropertyType*>(data_); 69f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 70f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 71f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org private: 72f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool succeeded_; 73f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned long size_; // NOLINT: type required by XGetWindowProperty 74f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned char* data_; 75f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 76f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org DISALLOW_COPY_AND_ASSIGN(XWindowProperty); 77f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org}; 78f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 79f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org// Stupid X11. It seems none of the synchronous returns codes from X11 calls 80f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org// are meaningful unless an asynchronous error handler is configured. This 81f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org// RAII class registers and unregisters an X11 error handler. 82f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgclass XErrorSuppressor { 83f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org public: 84f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org explicit XErrorSuppressor(Display* display) 85f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org : display_(display), original_error_handler_(NULL) { 86f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org SuppressX11Errors(); 87f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 88f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ~XErrorSuppressor() { 89f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org UnsuppressX11Errors(); 90f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 91f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 92f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org private: 93f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org static int ErrorHandler(Display* display, XErrorEvent* e) { 94f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org char buf[256]; 95f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XGetErrorText(display, e->error_code, buf, sizeof buf); 96f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_WARNING) << "Received X11 error \"" << buf << "\" for request code " 97f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org << static_cast<unsigned int>(e->request_code); 98f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return 0; 99f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 100f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 101f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org void SuppressX11Errors() { 102f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFlush(display_); 103f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XSync(display_, False); 104f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org original_error_handler_ = XSetErrorHandler(&ErrorHandler); 105f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 106f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 107f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org void UnsuppressX11Errors() { 108f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFlush(display_); 109f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XSync(display_, False); 110f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorHandler handler = XSetErrorHandler(original_error_handler_); 111f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (handler != &ErrorHandler) { 112f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_WARNING) << "Unbalanced XSetErrorHandler() calls detected. " 113f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org << "Final error handler may not be what you expect!"; 114f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 115f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org original_error_handler_ = NULL; 116f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 117f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 118f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Display* display_; 119f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorHandler original_error_handler_; 120f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 121f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org DISALLOW_COPY_AND_ASSIGN(XErrorSuppressor); 122f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org}; 123f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 124f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org// Hiding all X11 specifics inside its own class. This to avoid 125f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org// conflicts between talk and X11 header declarations. 126f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgclass XWindowEnumerator { 127f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org public: 128f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XWindowEnumerator() 129f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org : display_(NULL), 130f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org has_composite_extension_(false), 131f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org has_render_extension_(false) { 132f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 133f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 134f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ~XWindowEnumerator() { 135f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (display_ != NULL) { 136f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XCloseDisplay(display_); 137f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 138f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 139f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 140f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool Init() { 141f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (display_ != NULL) { 142f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Already initialized. 143f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return true; 144f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 145f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org display_ = XOpenDisplay(NULL); 146f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (display_ == NULL) { 147f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "Failed to open display."; 148f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 149f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 150f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 151f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 152f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 153f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org wm_state_ = XInternAtom(display_, "WM_STATE", True); 154f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org net_wm_icon_ = XInternAtom(display_, "_NET_WM_ICON", False); 155f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 156f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int event_base, error_base, major_version, minor_version; 157f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (XCompositeQueryExtension(display_, &event_base, &error_base) && 158f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XCompositeQueryVersion(display_, &major_version, &minor_version) && 159f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // XCompositeNameWindowPixmap() requires version 0.2 160f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org (major_version > 0 || minor_version >= 2)) { 161f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org has_composite_extension_ = true; 162f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } else { 163f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_INFO) << "Xcomposite extension not available or too old."; 164f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 165f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 166f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (XRenderQueryExtension(display_, &event_base, &error_base) && 167f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderQueryVersion(display_, &major_version, &minor_version) && 168f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // XRenderSetPictureTransform() requires version 0.6 169f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org (major_version > 0 || minor_version >= 6)) { 170f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org has_render_extension_ = true; 171f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } else { 172f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_INFO) << "Xrender extension not available or too old."; 173f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 174f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return true; 175f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 176f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 177f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool EnumerateWindows(WindowDescriptionList* descriptions) { 178f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 179f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 180f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 181f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 182f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int num_screens = XScreenCount(display_); 183f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool result = false; 184f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org for (int i = 0; i < num_screens; ++i) { 185f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (EnumerateScreenWindows(descriptions, i)) { 186f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // We know we succeded on at least one screen. 187f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org result = true; 188f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 189f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 190f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return result; 191f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 192f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 193f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool EnumerateDesktops(DesktopDescriptionList* descriptions) { 194f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 195f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 196f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 197f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 198f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window default_root_window = XDefaultRootWindow(display_); 199f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int num_screens = XScreenCount(display_); 200f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org for (int i = 0; i < num_screens; ++i) { 201f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window root_window = XRootWindow(display_, i); 202f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org DesktopId id(DesktopId(root_window, i)); 203f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // TODO: Figure out an appropriate desktop title. 204f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org DesktopDescription desc(id, ""); 205f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org desc.set_primary(root_window == default_root_window); 206f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org descriptions->push_back(desc); 207f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 208f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return num_screens > 0; 209f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 210f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 211f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool IsVisible(const WindowId& id) { 212f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 213f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 214f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 215f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 216f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XWindowAttributes attr; 217f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!XGetWindowAttributes(display_, id.id(), &attr)) { 218f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 219f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 220f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 221f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return attr.map_state == IsViewable; 222f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 223f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 224f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool MoveToFront(const WindowId& id) { 225f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 226f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 227f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 228f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 229f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned int num_children; 230f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window* children; 231f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window parent; 232f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window root; 233f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 234f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Find root window to pass event to. 235f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int status = XQueryTree(display_, id.id(), &root, &parent, &children, 236f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &num_children); 237f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (status == 0) { 238f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_WARNING) << "Failed to query for child windows."; 239f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 240f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 241f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (children != NULL) { 242f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(children); 243f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 244f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 245f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Move the window to front. 246f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRaiseWindow(display_, id.id()); 247f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 248f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Some window managers (e.g., metacity in GNOME) consider it illegal to 249f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // raise a window without also giving it input focus with 250f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough. 251f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom atom = XInternAtom(display_, "_NET_ACTIVE_WINDOW", True); 252f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (atom != None) { 253f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XEvent xev; 254f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org long event_mask; 255f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 256f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.type = ClientMessage; 257f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.serial = 0; 258f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.send_event = True; 259f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.window = id.id(); 260f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.message_type = atom; 261f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 262f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // The format member is set to 8, 16, or 32 and specifies whether the 263f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // data should be viewed as a list of bytes, shorts, or longs. 264f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.format = 32; 265f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 266f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.data.l[0] = 0; 267f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.data.l[1] = 0; 268f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.data.l[2] = 0; 269f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.data.l[3] = 0; 270f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org xev.xclient.data.l[4] = 0; 271f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 272f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org event_mask = SubstructureRedirectMask | SubstructureNotifyMask; 273f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 274f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XSendEvent(display_, root, False, event_mask, &xev); 275f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 276f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFlush(display_); 277f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return true; 278f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 279f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 280f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* GetWindowIcon(const WindowId& id, int* width, int* height) { 281f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 282f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 283f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 284f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 285f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom ret_type; 286f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int format; 287f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned long length, bytes_after, size; 288f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned char* data = NULL; 289f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 290f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Find out the size of the icon data. 291f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (XGetWindowProperty( 292f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org display_, id.id(), net_wm_icon_, 0, 0, False, XA_CARDINAL, 293f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &ret_type, &format, &length, &size, &data) == Success && 294f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org data) { 295f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(data); 296f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } else { 297f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "Failed to get size of the icon."; 298f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 299f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 300f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Get the icon data, the format is one uint32 each for width and height, 301f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // followed by the actual pixel data. 302f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (size >= 2 && 303f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XGetWindowProperty( 304f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org display_, id.id(), net_wm_icon_, 0, size, False, XA_CARDINAL, 305f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &ret_type, &format, &length, &bytes_after, &data) == Success && 306f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org data) { 307f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint32* data_ptr = reinterpret_cast<uint32*>(data); 308f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int w, h; 309f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org w = data_ptr[0]; 310f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org h = data_ptr[1]; 311f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (size < static_cast<unsigned long>(w * h + 2)) { 312f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(data); 313f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "Not a vaild icon."; 314f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 315f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 316f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* rgba = 317f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true); 318f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(data); 319f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *width = w; 320f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *height = h; 321f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return rgba; 322f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } else { 323f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "Failed to get window icon data."; 324f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 325f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 326f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 327f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 328f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* GetWindowThumbnail(const WindowId& id, int width, int height) { 329f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 330f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 331f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 332f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 333f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!has_composite_extension_) { 334f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Without the Xcomposite extension we would only get a good thumbnail if 335f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // the whole window is visible on screen and not covered by any 336f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // other window. This is not something we want so instead, just 337f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // bail out. 338f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_INFO) << "No Xcomposite extension detected."; 339f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 340f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 341f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 342f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 343f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window root; 344f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int x; 345f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int y; 346f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned int src_width; 347f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned int src_height; 348f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned int border_width; 349f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned int depth; 350f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 351f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // In addition to needing X11 server-side support for Xcomposite, it 352f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // actually needs to be turned on for this window in order to get a good 353f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // thumbnail. If the user has modern hardware/drivers but isn't using a 354f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // compositing window manager, that won't be the case. Here we 355f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // automatically turn it on for shareable windows so that we can get 356f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // thumbnails. We used to avoid it because the transition is visually ugly, 357f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // but recent window managers don't always redirect windows which led to 358f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // no thumbnails at all, which is a worse experience. 359f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 360f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Redirect drawing to an offscreen buffer (ie, turn on compositing). 361f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // X11 remembers what has requested this and will turn it off for us when 362f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // we exit. 363f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic); 364f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id()); 365f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!src_pixmap) { 366f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Even if the backing pixmap doesn't exist, this still should have 367f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // succeeded and returned a valid handle (it just wouldn't be a handle to 368f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // anything). So this is a real error path. 369f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed"; 370f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 371f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 372f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!XGetGeometry(display_, src_pixmap, &root, &x, &y, 373f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &src_width, &src_height, &border_width, 374f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &depth)) { 375f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // If the window does not actually have a backing pixmap, this is the path 376f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // that will "fail", so it's a warning rather than an error. 377f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in " 378f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org << "use)"; 379f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFreePixmap(display_, src_pixmap); 380f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 381f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 382f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 383f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // If we get to here, then composite is in use for this window and it has a 384f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // valid backing pixmap. 385f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 386f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XWindowAttributes attr; 387f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!XGetWindowAttributes(display_, id.id(), &attr)) { 388f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 389f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFreePixmap(display_, src_pixmap); 390f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 391f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 392f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 393f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* data = GetDrawableThumbnail(src_pixmap, 394f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org attr.visual, 395f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org src_width, 396f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org src_height, 397f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org width, 398f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org height); 399f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFreePixmap(display_, src_pixmap); 400f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return data; 401f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 402f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 403f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int GetNumDesktops() { 404f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 405f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return -1; 406f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 407f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 408f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return XScreenCount(display_); 409f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 410f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 411f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height) { 412f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 413f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 414f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 415f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 416f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 417f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window root_window = id.id(); 418f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XWindowAttributes attr; 419f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!XGetWindowAttributes(display_, root_window, &attr)) { 420f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 421f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 422f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 423f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 424f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return GetDrawableThumbnail(root_window, 425f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org attr.visual, 426f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org attr.width, 427f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org attr.height, 428f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org width, 429f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org height); 430f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 431f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 432f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) { 433f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!Init()) { 434f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 435f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 436f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XErrorSuppressor error_suppressor(display_); 437f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XWindowAttributes attr; 438f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!XGetWindowAttributes(display_, id.id(), &attr)) { 439f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 440f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 441f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 442f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *width = attr.width; 443f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *height = attr.height; 444f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return true; 445f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 446f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 447f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org private: 448f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* GetDrawableThumbnail(Drawable src_drawable, 449f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Visual* visual, 450f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int src_width, 451f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int src_height, 452f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int dst_width, 453f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int dst_height) { 454f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!has_render_extension_) { 455f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Without the Xrender extension we would have to read the full window and 456f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // scale it down in our process. Xrender is over a decade old so we aren't 457f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // going to expend effort to support that situation. We still need to 458f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // check though because probably some virtual VNC displays are in this 459f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // category. 460f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_INFO) << "No Xrender extension detected."; 461f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 462f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 463f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 464f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderPictFormat* format = XRenderFindVisualFormat(display_, 465f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org visual); 466f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!format) { 467f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XRenderFindVisualFormat() failed"; 468f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 469f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 470f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 471f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Create a picture to reference the window pixmap. 472f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderPictureAttributes pa; 473f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org pa.subwindow_mode = IncludeInferiors; // Don't clip child widgets 474f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Picture src = XRenderCreatePicture(display_, 475f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org src_drawable, 476f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org format, 477f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org CPSubwindowMode, 478f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &pa); 479f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!src) { 480f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XRenderCreatePicture() failed"; 481f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 482f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 483f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 484f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Create a picture to reference the destination pixmap. 485f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Pixmap dst_pixmap = XCreatePixmap(display_, 486f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org src_drawable, 487f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_width, 488f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_height, 489f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org format->depth); 490f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!dst_pixmap) { 491f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XCreatePixmap() failed"; 492f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderFreePicture(display_, src); 493f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 494f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 495f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 496f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL); 497f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!dst) { 498f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XRenderCreatePicture() failed"; 499f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFreePixmap(display_, dst_pixmap); 500f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderFreePicture(display_, src); 501f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return NULL; 502f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 503f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 504f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Clear the background. 505f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderColor transparent = {0}; 506f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderFillRectangle(display_, 507f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org PictOpSrc, 508f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst, 509f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &transparent, 510f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 511f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 512f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_width, 513f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_height); 514f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 515f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Calculate how much we need to scale the image. 516f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org double scale_x = static_cast<double>(dst_width) / 517f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org static_cast<double>(src_width); 518f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org double scale_y = static_cast<double>(dst_height) / 519f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org static_cast<double>(src_height); 520f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org double scale = rtc::_min(scale_y, scale_x); 521f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 522f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int scaled_width = round(src_width * scale); 523f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int scaled_height = round(src_height * scale); 524f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 525f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Render the thumbnail centered on both axis. 526f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int centered_x = (dst_width - scaled_width) / 2; 527f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int centered_y = (dst_height - scaled_height) / 2; 528f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 529f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Scaling matrix 530f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XTransform xform = { { 531f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) }, 532f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) }, 533f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) } 534f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } }; 535f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderSetPictureTransform(display_, src, &xform); 536f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 537f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Apply filter to smooth out the image. 538f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0); 539f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 540f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Render the image to the destination picture. 541f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderComposite(display_, 542f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org PictOpSrc, 543f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org src, 544f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org None, 545f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst, 546f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 547f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 548f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 549f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 550f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org centered_x, 551f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org centered_y, 552f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org scaled_width, 553f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org scaled_height); 554f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 555f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Get the pixel data from the X server. TODO: XGetImage 556f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // might be slow here, compare with ShmGetImage. 557f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XImage* image = XGetImage(display_, 558f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_pixmap, 559f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 560f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 0, 561f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_width, 562f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_height, 563f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org AllPlanes, ZPixmap); 564f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* data = ArgbToRgba(reinterpret_cast<uint32*>(image->data), 565f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org centered_x, 566f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org centered_y, 567f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org scaled_width, 568f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org scaled_height, 569f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_width, 570f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org dst_height, 571f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org false); 572f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XDestroyImage(image); 573f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderFreePicture(display_, dst); 574f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFreePixmap(display_, dst_pixmap); 575f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XRenderFreePicture(display_, src); 576f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return data; 577f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 578f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 579f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* ArgbToRgba(uint32* argb_data, int x, int y, int w, int h, 580f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int stride_x, int stride_y, bool has_alpha) { 581f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* p; 582f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int len = stride_x * stride_y * 4; 583f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint8* data = new uint8[len]; 584f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org memset(data, 0, len); 585f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org p = data + 4 * (y * stride_x + x); 586f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org for (int i = 0; i < h; ++i) { 587f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org for (int j = 0; j < w; ++j) { 588f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint32 argb; 589f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint32 rgba; 590f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org argb = argb_data[stride_x * (y + i) + x + j]; 591f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org rgba = (argb << 8) | (argb >> 24); 592f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *p = rgba >> 24; 593f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ++p; 594f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *p = (rgba >> 16) & 0xff; 595f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ++p; 596f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *p = (rgba >> 8) & 0xff; 597f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ++p; 598f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *p = has_alpha ? rgba & 0xFF : 0xFF; 599f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org ++p; 600f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 601f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org p += (stride_x - w) * 4; 602f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 603f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return data; 604f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 605f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 606f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) { 607f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window parent; 608f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window *children; 609f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int status; 610f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned int num_children; 611f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window root_window = XRootWindow(display_, screen); 612f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org status = XQueryTree(display_, root_window, &root_window, &parent, &children, 613f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &num_children); 614f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (status == 0) { 615f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "Failed to query for child windows."; 616f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 617f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 618f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org for (unsigned int i = 0; i < num_children; ++i) { 619f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Iterate in reverse order to display windows from front to back. 620f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#ifdef CHROMEOS 621f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to 622f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // filter, just return all windows and let the picker scan through them. 623f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window app_window = children[num_children - 1 - i]; 624f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#else 625f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window app_window = GetApplicationWindow(children[num_children - 1 - i]); 626f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org#endif 627f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (app_window && 628f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org !LinuxWindowPicker::IsDesktopElement(display_, app_window)) { 629f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org std::string title; 630f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (GetWindowTitle(app_window, &title)) { 631f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org WindowId id(app_window); 632f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org WindowDescription desc(id, title); 633f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org descriptions->push_back(desc); 634f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 635f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 636f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 637f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (children != NULL) { 638f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(children); 639f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 640f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return true; 641f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 642f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 643f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool GetWindowTitle(Window window, std::string* title) { 644f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int status; 645f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool result = false; 646f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XTextProperty window_name; 647f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org window_name.value = NULL; 648f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (window) { 649f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org status = XGetWMName(display_, window, &window_name); 650f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (status && window_name.value && window_name.nitems) { 651f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int cnt; 652f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org char **list = NULL; 653f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org status = Xutf8TextPropertyToTextList(display_, &window_name, &list, 654f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &cnt); 655f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (status >= Success && cnt && *list) { 656f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (cnt > 1) { 657f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_INFO) << "Window has " << cnt 658f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org << " text properties, only using the first one."; 659f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 660f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org *title = *list; 661f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org result = true; 662f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 663f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (list != NULL) { 664f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFreeStringList(list); 665f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 666f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 667f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (window_name.value != NULL) { 668f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(window_name.value); 669f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 670f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 671f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return result; 672f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 673f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 674f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window GetApplicationWindow(Window window) { 675f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window root, parent; 676f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window app_window = 0; 677f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Window *children; 678f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned int num_children; 679f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom type = None; 680f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int format; 681f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned long nitems, after; 682f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org unsigned char *data; 683f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 684f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int ret = XGetWindowProperty(display_, window, 685f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org wm_state_, 0L, 2, 686f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org False, wm_state_, &type, &format, 687f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &nitems, &after, &data); 688f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (ret != Success) { 689f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret 690f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org << " for window " << window << "."; 691f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return 0; 692f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 693f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (type != None) { 694f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int64 state = static_cast<int64>(*data); 695f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(data); 696f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return state == NormalState ? window : 0; 697f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 698f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(data); 699f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (!XQueryTree(display_, window, &root, &parent, &children, 700f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org &num_children)) { 701f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_ERROR) << "Failed to query for child windows although window" 702f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org << "does not have a valid WM_STATE."; 703f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return 0; 704f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 705f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org for (unsigned int i = 0; i < num_children; ++i) { 706f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org app_window = GetApplicationWindow(children[i]); 707f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (app_window) { 708f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org break; 709f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 710f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 711f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (children != NULL) { 712f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(children); 713f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 714f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return app_window; 715f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 716f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 717f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom wm_state_; 718f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom net_wm_icon_; 719f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Display* display_; 720f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool has_composite_extension_; 721f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool has_render_extension_; 722f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org}; 723f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 724f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgLinuxWindowPicker::LinuxWindowPicker() : enumerator_(new XWindowEnumerator()) { 725f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 726f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 727f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgLinuxWindowPicker::~LinuxWindowPicker() { 728f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 729f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 730f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgbool LinuxWindowPicker::IsDesktopElement(_XDisplay* display, Window window) { 731f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (window == 0) { 732f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org LOG(LS_WARNING) << "Zero is never a valid window."; 733f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return false; 734f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 735f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 736f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // First look for _NET_WM_WINDOW_TYPE. The standard 737f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306) 738f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // says this hint *should* be present on all windows, and we use the existence 739f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not 740f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // a desktop element (that is, only "normal" windows should be shareable). 741f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); 742f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XWindowProperty<uint32_t> window_type(display, window, window_type_atom); 743f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (window_type.succeeded() && window_type.size() > 0) { 744f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Atom normal_window_type_atom = XInternAtom( 745f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org display, "_NET_WM_WINDOW_TYPE_NORMAL", True); 746f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org uint32_t* end = window_type.data() + window_type.size(); 747f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool is_normal = (end != std::find( 748f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org window_type.data(), end, normal_window_type_atom)); 749f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return !is_normal; 750f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 751f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 752f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // Fall back on using the hint. 753f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XClassHint class_hint; 754f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org Status s = XGetClassHint(display, window, &class_hint); 755f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org bool result = false; 756f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (s == 0) { 757f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org // No hints, assume this is a normal application window. 758f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return result; 759f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 760f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org static const std::string gnome_panel("gnome-panel"); 761f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org static const std::string desktop_window("desktop_window"); 762f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 763f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org if (gnome_panel.compare(class_hint.res_name) == 0 || 764f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org desktop_window.compare(class_hint.res_name) == 0) { 765f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org result = true; 766f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org } 767f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(class_hint.res_name); 768f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org XFree(class_hint.res_class); 769f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return result; 770f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 771f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 772f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgbool LinuxWindowPicker::Init() { 773f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->Init(); 774f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 775f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 776f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgbool LinuxWindowPicker::GetWindowList(WindowDescriptionList* descriptions) { 777f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->EnumerateWindows(descriptions); 778f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 779f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 780f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgbool LinuxWindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { 781f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->EnumerateDesktops(descriptions); 782f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 783f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 784f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgbool LinuxWindowPicker::IsVisible(const WindowId& id) { 785f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->IsVisible(id); 786f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 787f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 788f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgbool LinuxWindowPicker::MoveToFront(const WindowId& id) { 789f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->MoveToFront(id); 790f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 791f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 792f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 793f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orguint8* LinuxWindowPicker::GetWindowIcon(const WindowId& id, int* width, 794f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int* height) { 795f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->GetWindowIcon(id, width, height); 796f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 797f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 798f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orguint8* LinuxWindowPicker::GetWindowThumbnail(const WindowId& id, int width, 799f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int height) { 800f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->GetWindowThumbnail(id, width, height); 801f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 802f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 803f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgint LinuxWindowPicker::GetNumDesktops() { 804f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->GetNumDesktops(); 805f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 806f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 807f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orguint8* LinuxWindowPicker::GetDesktopThumbnail(const DesktopId& id, 808f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int width, 809f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int height) { 810f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->GetDesktopThumbnail(id, width, height); 811f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 812f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 813f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.orgbool LinuxWindowPicker::GetDesktopDimensions(const DesktopId& id, int* width, 814f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org int* height) { 815f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org return enumerator_->GetDesktopDimensions(id, width, height); 816f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} 817f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org 818f048872e915a3ee229044ec4bc541f6cbf9e4de1henrike@webrtc.org} // namespace rtc 819