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  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  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* 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 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* data_ptr = reinterpret_cast<uint32*>(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* rgba =
317          ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true);
318      XFree(data);
319      *width = w;
320      *height = h;
321      return rgba;
322    } else {
323      LOG(LS_ERROR) << "Failed to get window icon data.";
324      return NULL;
325    }
326  }
327
328  uint8* GetWindowThumbnail(const WindowId& id, int width, int height) {
329    if (!Init()) {
330      return NULL;
331    }
332
333    if (!has_composite_extension_) {
334      // Without the Xcomposite extension we would only get a good thumbnail if
335      // the whole window is visible on screen and not covered by any
336      // other window. This is not something we want so instead, just
337      // bail out.
338      LOG(LS_INFO) << "No Xcomposite extension detected.";
339      return NULL;
340    }
341    XErrorSuppressor error_suppressor(display_);
342
343    Window root;
344    int x;
345    int y;
346    unsigned int src_width;
347    unsigned int src_height;
348    unsigned int border_width;
349    unsigned int depth;
350
351    // In addition to needing X11 server-side support for Xcomposite, it
352    // actually needs to be turned on for this window in order to get a good
353    // thumbnail. If the user has modern hardware/drivers but isn't using a
354    // compositing window manager, that won't be the case. Here we
355    // automatically turn it on for shareable windows so that we can get
356    // thumbnails. We used to avoid it because the transition is visually ugly,
357    // but recent window managers don't always redirect windows which led to
358    // no thumbnails at all, which is a worse experience.
359
360    // Redirect drawing to an offscreen buffer (ie, turn on compositing).
361    // X11 remembers what has requested this and will turn it off for us when
362    // we exit.
363    XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic);
364    Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id());
365    if (!src_pixmap) {
366      // Even if the backing pixmap doesn't exist, this still should have
367      // succeeded and returned a valid handle (it just wouldn't be a handle to
368      // anything). So this is a real error path.
369      LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed";
370      return NULL;
371    }
372    if (!XGetGeometry(display_, src_pixmap, &root, &x, &y,
373                      &src_width, &src_height, &border_width,
374                      &depth)) {
375      // If the window does not actually have a backing pixmap, this is the path
376      // that will "fail", so it's a warning rather than an error.
377      LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in "
378                      << "use)";
379      XFreePixmap(display_, src_pixmap);
380      return NULL;
381    }
382
383    // If we get to here, then composite is in use for this window and it has a
384    // valid backing pixmap.
385
386    XWindowAttributes attr;
387    if (!XGetWindowAttributes(display_, id.id(), &attr)) {
388      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
389      XFreePixmap(display_, src_pixmap);
390      return NULL;
391    }
392
393    uint8* data = GetDrawableThumbnail(src_pixmap,
394                                       attr.visual,
395                                       src_width,
396                                       src_height,
397                                       width,
398                                       height);
399    XFreePixmap(display_, src_pixmap);
400    return data;
401  }
402
403  int GetNumDesktops() {
404    if (!Init()) {
405      return -1;
406    }
407
408    return XScreenCount(display_);
409  }
410
411  uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height) {
412    if (!Init()) {
413      return NULL;
414    }
415    XErrorSuppressor error_suppressor(display_);
416
417    Window root_window = id.id();
418    XWindowAttributes attr;
419    if (!XGetWindowAttributes(display_, root_window, &attr)) {
420      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
421      return NULL;
422    }
423
424    return GetDrawableThumbnail(root_window,
425                                attr.visual,
426                                attr.width,
427                                attr.height,
428                                width,
429                                height);
430  }
431
432  bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) {
433    if (!Init()) {
434      return false;
435    }
436    XErrorSuppressor error_suppressor(display_);
437    XWindowAttributes attr;
438    if (!XGetWindowAttributes(display_, id.id(), &attr)) {
439      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
440      return false;
441    }
442    *width = attr.width;
443    *height = attr.height;
444    return true;
445  }
446
447 private:
448  uint8* GetDrawableThumbnail(Drawable src_drawable,
449                              Visual* visual,
450                              int src_width,
451                              int src_height,
452                              int dst_width,
453                              int dst_height) {
454    if (!has_render_extension_) {
455      // Without the Xrender extension we would have to read the full window and
456      // scale it down in our process. Xrender is over a decade old so we aren't
457      // going to expend effort to support that situation. We still need to
458      // check though because probably some virtual VNC displays are in this
459      // category.
460      LOG(LS_INFO) << "No Xrender extension detected.";
461      return NULL;
462    }
463
464    XRenderPictFormat* format = XRenderFindVisualFormat(display_,
465                                                        visual);
466    if (!format) {
467      LOG(LS_ERROR) << "XRenderFindVisualFormat() failed";
468      return NULL;
469    }
470
471    // Create a picture to reference the window pixmap.
472    XRenderPictureAttributes pa;
473    pa.subwindow_mode = IncludeInferiors;  // Don't clip child widgets
474    Picture src = XRenderCreatePicture(display_,
475                                       src_drawable,
476                                       format,
477                                       CPSubwindowMode,
478                                       &pa);
479    if (!src) {
480      LOG(LS_ERROR) << "XRenderCreatePicture() failed";
481      return NULL;
482    }
483
484    // Create a picture to reference the destination pixmap.
485    Pixmap dst_pixmap = XCreatePixmap(display_,
486                                      src_drawable,
487                                      dst_width,
488                                      dst_height,
489                                      format->depth);
490    if (!dst_pixmap) {
491      LOG(LS_ERROR) << "XCreatePixmap() failed";
492      XRenderFreePicture(display_, src);
493      return NULL;
494    }
495
496    Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL);
497    if (!dst) {
498      LOG(LS_ERROR) << "XRenderCreatePicture() failed";
499      XFreePixmap(display_, dst_pixmap);
500      XRenderFreePicture(display_, src);
501      return NULL;
502    }
503
504    // Clear the background.
505    XRenderColor transparent = {0};
506    XRenderFillRectangle(display_,
507                         PictOpSrc,
508                         dst,
509                         &transparent,
510                         0,
511                         0,
512                         dst_width,
513                         dst_height);
514
515    // Calculate how much we need to scale the image.
516    double scale_x = static_cast<double>(dst_width) /
517        static_cast<double>(src_width);
518    double scale_y = static_cast<double>(dst_height) /
519        static_cast<double>(src_height);
520    double scale = rtc::_min(scale_y, scale_x);
521
522    int scaled_width = round(src_width * scale);
523    int scaled_height = round(src_height * scale);
524
525    // Render the thumbnail centered on both axis.
526    int centered_x = (dst_width - scaled_width) / 2;
527    int centered_y = (dst_height - scaled_height) / 2;
528
529    // Scaling matrix
530    XTransform xform = { {
531        { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) },
532        { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) },
533        { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) }
534        } };
535    XRenderSetPictureTransform(display_, src, &xform);
536
537    // Apply filter to smooth out the image.
538    XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0);
539
540    // Render the image to the destination picture.
541    XRenderComposite(display_,
542                     PictOpSrc,
543                     src,
544                     None,
545                     dst,
546                     0,
547                     0,
548                     0,
549                     0,
550                     centered_x,
551                     centered_y,
552                     scaled_width,
553                     scaled_height);
554
555    // Get the pixel data from the X server. TODO: XGetImage
556    // might be slow here, compare with ShmGetImage.
557    XImage* image = XGetImage(display_,
558                              dst_pixmap,
559                              0,
560                              0,
561                              dst_width,
562                              dst_height,
563                              AllPlanes, ZPixmap);
564    uint8* data = ArgbToRgba(reinterpret_cast<uint32*>(image->data),
565                             centered_x,
566                             centered_y,
567                             scaled_width,
568                             scaled_height,
569                             dst_width,
570                             dst_height,
571                             false);
572    XDestroyImage(image);
573    XRenderFreePicture(display_, dst);
574    XFreePixmap(display_, dst_pixmap);
575    XRenderFreePicture(display_, src);
576    return data;
577  }
578
579  uint8* ArgbToRgba(uint32* argb_data, int x, int y, int w, int h,
580                    int stride_x, int stride_y, bool has_alpha) {
581    uint8* p;
582    int len = stride_x * stride_y * 4;
583    uint8* data = new uint8[len];
584    memset(data, 0, len);
585    p = data + 4 * (y * stride_x + x);
586    for (int i = 0; i < h; ++i) {
587      for (int j = 0; j < w; ++j) {
588        uint32 argb;
589        uint32 rgba;
590        argb = argb_data[stride_x * (y + i) + x + j];
591        rgba = (argb << 8) | (argb >> 24);
592        *p = rgba >> 24;
593        ++p;
594        *p = (rgba >> 16) & 0xff;
595        ++p;
596        *p = (rgba >> 8) & 0xff;
597        ++p;
598        *p = has_alpha ? rgba & 0xFF : 0xFF;
599        ++p;
600      }
601      p += (stride_x - w) * 4;
602    }
603    return data;
604  }
605
606  bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) {
607    Window parent;
608    Window *children;
609    int status;
610    unsigned int num_children;
611    Window root_window = XRootWindow(display_, screen);
612    status = XQueryTree(display_, root_window, &root_window, &parent, &children,
613                        &num_children);
614    if (status == 0) {
615      LOG(LS_ERROR) << "Failed to query for child windows.";
616      return false;
617    }
618    for (unsigned int i = 0; i < num_children; ++i) {
619      // Iterate in reverse order to display windows from front to back.
620#ifdef CHROMEOS
621      // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to
622      // filter, just return all windows and let the picker scan through them.
623      Window app_window = children[num_children - 1 - i];
624#else
625      Window app_window = GetApplicationWindow(children[num_children - 1 - i]);
626#endif
627      if (app_window &&
628          !X11WindowPicker::IsDesktopElement(display_, app_window)) {
629        std::string title;
630        if (GetWindowTitle(app_window, &title)) {
631          WindowId id(app_window);
632          WindowDescription desc(id, title);
633          descriptions->push_back(desc);
634        }
635      }
636    }
637    if (children != NULL) {
638      XFree(children);
639    }
640    return true;
641  }
642
643  bool GetWindowTitle(Window window, std::string* title) {
644    int status;
645    bool result = false;
646    XTextProperty window_name;
647    window_name.value = NULL;
648    if (window) {
649      status = XGetWMName(display_, window, &window_name);
650      if (status && window_name.value && window_name.nitems) {
651        int cnt;
652        char **list = NULL;
653        status = Xutf8TextPropertyToTextList(display_, &window_name, &list,
654                                             &cnt);
655        if (status >= Success && cnt && *list) {
656          if (cnt > 1) {
657            LOG(LS_INFO) << "Window has " << cnt
658                         << " text properties, only using the first one.";
659          }
660          *title = *list;
661          result = true;
662        }
663        if (list != NULL) {
664          XFreeStringList(list);
665        }
666      }
667      if (window_name.value != NULL) {
668        XFree(window_name.value);
669      }
670    }
671    return result;
672  }
673
674  Window GetApplicationWindow(Window window) {
675    Window root, parent;
676    Window app_window = 0;
677    Window *children;
678    unsigned int num_children;
679    Atom type = None;
680    int format;
681    unsigned long nitems, after;
682    unsigned char *data;
683
684    int ret = XGetWindowProperty(display_, window,
685                                 wm_state_, 0L, 2,
686                                 False, wm_state_, &type, &format,
687                                 &nitems, &after, &data);
688    if (ret != Success) {
689      LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret
690                    << " for window " << window << ".";
691      return 0;
692    }
693    if (type != None) {
694      int64 state = static_cast<int64>(*data);
695      XFree(data);
696      return state == NormalState ? window : 0;
697    }
698    XFree(data);
699    if (!XQueryTree(display_, window, &root, &parent, &children,
700                    &num_children)) {
701      LOG(LS_ERROR) << "Failed to query for child windows although window"
702                    << "does not have a valid WM_STATE.";
703      return 0;
704    }
705    for (unsigned int i = 0; i < num_children; ++i) {
706      app_window = GetApplicationWindow(children[i]);
707      if (app_window) {
708        break;
709      }
710    }
711    if (children != NULL) {
712      XFree(children);
713    }
714    return app_window;
715  }
716
717  Atom wm_state_;
718  Atom net_wm_icon_;
719  Display* display_;
720  bool has_composite_extension_;
721  bool has_render_extension_;
722};
723
724X11WindowPicker::X11WindowPicker() : enumerator_(new XWindowEnumerator()) {
725}
726
727X11WindowPicker::~X11WindowPicker() {
728}
729
730bool X11WindowPicker::IsDesktopElement(_XDisplay* display, Window window) {
731  if (window == 0) {
732    LOG(LS_WARNING) << "Zero is never a valid window.";
733    return false;
734  }
735
736  // First look for _NET_WM_WINDOW_TYPE. The standard
737  // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
738  // says this hint *should* be present on all windows, and we use the existence
739  // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
740  // a desktop element (that is, only "normal" windows should be shareable).
741  Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True);
742  XWindowProperty<uint32_t> window_type(display, window, window_type_atom);
743  if (window_type.succeeded() && window_type.size() > 0) {
744    Atom normal_window_type_atom = XInternAtom(
745        display, "_NET_WM_WINDOW_TYPE_NORMAL", True);
746    uint32_t* end = window_type.data() + window_type.size();
747    bool is_normal = (end != std::find(
748        window_type.data(), end, normal_window_type_atom));
749    return !is_normal;
750  }
751
752  // Fall back on using the hint.
753  XClassHint class_hint;
754  Status s = XGetClassHint(display, window, &class_hint);
755  bool result = false;
756  if (s == 0) {
757    // No hints, assume this is a normal application window.
758    return result;
759  }
760  static const std::string gnome_panel("gnome-panel");
761  static const std::string desktop_window("desktop_window");
762
763  if (gnome_panel.compare(class_hint.res_name) == 0 ||
764      desktop_window.compare(class_hint.res_name) == 0) {
765    result = true;
766  }
767  XFree(class_hint.res_name);
768  XFree(class_hint.res_class);
769  return result;
770}
771
772bool X11WindowPicker::Init() {
773  return enumerator_->Init();
774}
775
776bool X11WindowPicker::GetWindowList(WindowDescriptionList* descriptions) {
777  return enumerator_->EnumerateWindows(descriptions);
778}
779
780bool X11WindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) {
781  return enumerator_->EnumerateDesktops(descriptions);
782}
783
784bool X11WindowPicker::IsVisible(const WindowId& id) {
785  return enumerator_->IsVisible(id);
786}
787
788bool X11WindowPicker::MoveToFront(const WindowId& id) {
789  return enumerator_->MoveToFront(id);
790}
791
792
793uint8* X11WindowPicker::GetWindowIcon(const WindowId& id, int* width,
794                                        int* height) {
795  return enumerator_->GetWindowIcon(id, width, height);
796}
797
798uint8* X11WindowPicker::GetWindowThumbnail(const WindowId& id, int width,
799                                             int height) {
800  return enumerator_->GetWindowThumbnail(id, width, height);
801}
802
803int X11WindowPicker::GetNumDesktops() {
804  return enumerator_->GetNumDesktops();
805}
806
807uint8* X11WindowPicker::GetDesktopThumbnail(const DesktopId& id,
808                                              int width,
809                                              int height) {
810  return enumerator_->GetDesktopThumbnail(id, width, height);
811}
812
813bool X11WindowPicker::GetDesktopDimensions(const DesktopId& id, int* width,
814                                             int* height) {
815  return enumerator_->GetDesktopDimensions(id, width, height);
816}
817
818}  // namespace rtc
819