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