~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/libjingle/talk/base/linuxwindowpicker.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * libjingle
 
3
 * Copyright 2010 Google Inc.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions are met:
 
7
 *
 
8
 *  1. Redistributions of source code must retain the above copyright notice,
 
9
 *     this list of conditions and the following disclaimer.
 
10
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 
11
 *     this list of conditions and the following disclaimer in the documentation
 
12
 *     and/or other materials provided with the distribution.
 
13
 *  3. The name of the author may not be used to endorse or promote products
 
14
 *     derived from this software without specific prior written permission.
 
15
 *
 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 
19
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
20
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
21
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
22
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
23
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 
24
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
25
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
26
 */
 
27
 
 
28
#include "talk/base/linuxwindowpicker.h"
 
29
 
 
30
#include <math.h>
 
31
#include <string.h>
 
32
 
 
33
#include <string>
 
34
 
 
35
#include <X11/Xatom.h>
 
36
#include <X11/extensions/Xcomposite.h>
 
37
#include <X11/extensions/Xrender.h>
 
38
#include <X11/Xutil.h>
 
39
 
 
40
#include "talk/base/logging.h"
 
41
 
 
42
namespace talk_base {
 
43
 
 
44
// Stupid X11.  It seems none of the synchronous returns codes from X11 calls
 
45
// are meaningful unless an asynchronous error handler is configured.  This
 
46
// RAII class registers and unregisters an X11 error handler.
 
47
class XErrorSuppressor {
 
48
 public:
 
49
  explicit XErrorSuppressor(Display* display)
 
50
      : display_(display), original_error_handler_(NULL) {
 
51
    SuppressX11Errors();
 
52
  }
 
53
  ~XErrorSuppressor() {
 
54
    UnsuppressX11Errors();
 
55
  }
 
56
 
 
57
 private:
 
58
  static int ErrorHandler(Display* display, XErrorEvent* e) {
 
59
    char buf[256];
 
60
    XGetErrorText(display, e->error_code, buf, sizeof buf);
 
61
    LOG(LS_WARNING) << "Received X11 error \"" << buf << "\" for request code "
 
62
                    << static_cast<unsigned int>(e->request_code);
 
63
    return 0;
 
64
  }
 
65
 
 
66
  void SuppressX11Errors() {
 
67
    XFlush(display_);
 
68
    XSync(display_, False);
 
69
    original_error_handler_ = XSetErrorHandler(&ErrorHandler);
 
70
  }
 
71
 
 
72
  void UnsuppressX11Errors() {
 
73
    XFlush(display_);
 
74
    XSync(display_, False);
 
75
    XErrorHandler handler = XSetErrorHandler(original_error_handler_);
 
76
    if (handler != &ErrorHandler) {
 
77
      LOG(LS_WARNING) << "Unbalanced XSetErrorHandler() calls detected. "
 
78
                      << "Final error handler may not be what you expect!";
 
79
    }
 
80
    original_error_handler_ = NULL;
 
81
  }
 
82
 
 
83
  Display* display_;
 
84
  XErrorHandler original_error_handler_;
 
85
 
 
86
  DISALLOW_COPY_AND_ASSIGN(XErrorSuppressor);
 
87
};
 
88
 
 
89
// Hiding all X11 specifics inside its own class. This to avoid
 
90
// conflicts between talk and X11 header declarations.
 
91
class XWindowEnumerator {
 
92
 public:
 
93
  XWindowEnumerator()
 
94
      : display_(NULL),
 
95
        has_composite_extension_(false),
 
96
        has_render_extension_(false) {
 
97
  }
 
98
 
 
99
  ~XWindowEnumerator() {
 
100
    if (display_ != NULL) {
 
101
      XCloseDisplay(display_);
 
102
    }
 
103
  }
 
104
 
 
105
  bool Init() {
 
106
    if (display_ != NULL) {
 
107
      // Already initialized.
 
108
      return true;
 
109
    }
 
110
    display_ = XOpenDisplay(NULL);
 
111
    if (display_ == NULL) {
 
112
      LOG(LS_ERROR) << "Failed to open display.";
 
113
      return false;
 
114
    }
 
115
 
 
116
    XErrorSuppressor error_suppressor(display_);
 
117
 
 
118
    wm_state_ = XInternAtom(display_, "WM_STATE", True);
 
119
    net_wm_icon_ = XInternAtom(display_, "_NET_WM_ICON", False);
 
120
 
 
121
    int event_base, error_base, major_version, minor_version;
 
122
    if (XCompositeQueryExtension(display_, &event_base, &error_base) &&
 
123
        XCompositeQueryVersion(display_, &major_version, &minor_version) &&
 
124
        // XCompositeNameWindowPixmap() requires version 0.2
 
125
        (major_version > 0 || minor_version >= 2)) {
 
126
      has_composite_extension_ = true;
 
127
    } else {
 
128
      LOG(LS_INFO) << "Xcomposite extension not available or too old.";
 
129
    }
 
130
 
 
131
    if (XRenderQueryExtension(display_, &event_base, &error_base) &&
 
132
        XRenderQueryVersion(display_, &major_version, &minor_version) &&
 
133
        // XRenderSetPictureTransform() requires version 0.6
 
134
        (major_version > 0 || minor_version >= 6)) {
 
135
      has_render_extension_ = true;
 
136
    } else {
 
137
      LOG(LS_INFO) << "Xrender extension not available or too old.";
 
138
    }
 
139
    return true;
 
140
  }
 
141
 
 
142
  bool EnumerateWindows(WindowDescriptionList* descriptions) {
 
143
    if (!Init()) {
 
144
      return false;
 
145
    }
 
146
    XErrorSuppressor error_suppressor(display_);
 
147
    int num_screens = XScreenCount(display_);
 
148
    bool result = false;
 
149
    for (int i = 0; i < num_screens; ++i) {
 
150
      if (EnumerateScreenWindows(descriptions, i)) {
 
151
        // We know we succeded on at least one screen.
 
152
        result = true;
 
153
      }
 
154
    }
 
155
    return result;
 
156
  }
 
157
 
 
158
  bool EnumerateDesktops(DesktopDescriptionList* descriptions) {
 
159
    if (!Init()) {
 
160
      return false;
 
161
    }
 
162
    XErrorSuppressor error_suppressor(display_);
 
163
    int num_screens = XScreenCount(display_);
 
164
    for (int i = 0; i < num_screens; ++i) {
 
165
      Window root_window = XRootWindow(display_, i);
 
166
      DesktopId id(DesktopId(root_window, i));
 
167
      // TODO: Figure out an appropriate desktop title.
 
168
      DesktopDescription desc(id, "");
 
169
      descriptions->push_back(desc);
 
170
    }
 
171
    return num_screens > 0;
 
172
  }
 
173
 
 
174
  bool IsVisible(const WindowId& id) {
 
175
    if (!Init()) {
 
176
      return false;
 
177
    }
 
178
    XErrorSuppressor error_suppressor(display_);
 
179
    XWindowAttributes attr;
 
180
    if (!XGetWindowAttributes(display_, id.id(), &attr)) {
 
181
      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
 
182
      return false;
 
183
    }
 
184
    return attr.map_state == IsViewable;
 
185
  }
 
186
 
 
187
  bool MoveToFront(const WindowId& id) {
 
188
    if (!Init()) {
 
189
      return false;
 
190
    }
 
191
    XErrorSuppressor error_suppressor(display_);
 
192
    unsigned int num_children;
 
193
    Window* children;
 
194
    Window parent;
 
195
    Window root;
 
196
 
 
197
    // Find root window to pass event to.
 
198
    int status = XQueryTree(display_, id.id(), &root, &parent, &children,
 
199
                            &num_children);
 
200
    if (status == 0) {
 
201
      LOG(LS_WARNING) << "Failed to query for child windows.";
 
202
      return false;
 
203
    }
 
204
    if (children != NULL) {
 
205
      XFree(children);
 
206
    }
 
207
 
 
208
    // Move the window to front.
 
209
    XRaiseWindow(display_, id.id());
 
210
 
 
211
    // Some window managers (e.g., metacity in GNOME) consider it illegal to
 
212
    // raise a window without also giving it input focus with
 
213
    // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
 
214
    Atom atom = XInternAtom(display_, "_NET_ACTIVE_WINDOW", True);
 
215
    if (atom != None) {
 
216
      XEvent xev;
 
217
      long event_mask;
 
218
 
 
219
      xev.xclient.type = ClientMessage;
 
220
      xev.xclient.serial = 0;
 
221
      xev.xclient.send_event = True;
 
222
      xev.xclient.window = id.id();
 
223
      xev.xclient.message_type = atom;
 
224
 
 
225
      // The format member is set to 8, 16, or 32 and specifies whether the
 
226
      // data should be viewed as a list of bytes, shorts, or longs.
 
227
      xev.xclient.format = 32;
 
228
 
 
229
      xev.xclient.data.l[0] = 0;
 
230
      xev.xclient.data.l[1] = 0;
 
231
      xev.xclient.data.l[2] = 0;
 
232
      xev.xclient.data.l[3] = 0;
 
233
      xev.xclient.data.l[4] = 0;
 
234
 
 
235
      event_mask = SubstructureRedirectMask | SubstructureNotifyMask;
 
236
 
 
237
      XSendEvent(display_, root, False, event_mask, &xev);
 
238
    }
 
239
    XFlush(display_);
 
240
    return true;
 
241
  }
 
242
 
 
243
  uint8* GetWindowIcon(const WindowId& id, int* width, int* height) {
 
244
    if (!Init()) {
 
245
      return NULL;
 
246
    }
 
247
    XErrorSuppressor error_suppressor(display_);
 
248
    Atom ret_type;
 
249
    int format;
 
250
    unsigned long length, bytes_after, size;
 
251
    unsigned char* data = NULL;
 
252
 
 
253
    // Find out the size of the icon data.
 
254
    if (XGetWindowProperty(
 
255
            display_, id.id(), net_wm_icon_, 0, 0, False, XA_CARDINAL,
 
256
            &ret_type, &format, &length, &size, &data) == Success &&
 
257
        data) {
 
258
      XFree(data);
 
259
    } else {
 
260
      LOG(LS_ERROR) << "Failed to get size of the icon.";
 
261
      return NULL;
 
262
    }
 
263
    // Get the icon data, the format is one uint32 each for width and height,
 
264
    // followed by the actual pixel data.
 
265
    if (size >= 2 &&
 
266
        XGetWindowProperty(
 
267
            display_, id.id(), net_wm_icon_, 0, size, False, XA_CARDINAL,
 
268
            &ret_type, &format, &length, &bytes_after, &data) == Success &&
 
269
        data) {
 
270
      uint32* data_ptr = reinterpret_cast<uint32*>(data);
 
271
      int w, h;
 
272
      w = data_ptr[0];
 
273
      h = data_ptr[1];
 
274
      if (size < static_cast<unsigned long>(w * h + 2)) {
 
275
        XFree(data);
 
276
        LOG(LS_ERROR) << "Not a vaild icon.";
 
277
        return NULL;
 
278
      }
 
279
      uint8* rgba =
 
280
          ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true);
 
281
      XFree(data);
 
282
      *width = w;
 
283
      *height = h;
 
284
      return rgba;
 
285
    } else {
 
286
      LOG(LS_ERROR) << "Failed to get window icon data.";
 
287
      return NULL;
 
288
    }
 
289
  }
 
290
 
 
291
  uint8* GetWindowThumbnail(const WindowId& id, int width, int height) {
 
292
    if (!Init()) {
 
293
      return NULL;
 
294
    }
 
295
 
 
296
    if (!has_composite_extension_) {
 
297
      // Without the Xcomposite extension we would only get a good thumbnail if
 
298
      // the whole window is visible on screen and not covered by any
 
299
      // other window. This is not something we want so instead, just
 
300
      // bail out.
 
301
      LOG(LS_INFO) << "No Xcomposite extension detected.";
 
302
      return NULL;
 
303
    }
 
304
    XErrorSuppressor error_suppressor(display_);
 
305
 
 
306
    Window root;
 
307
    int x;
 
308
    int y;
 
309
    unsigned int src_width;
 
310
    unsigned int src_height;
 
311
    unsigned int border_width;
 
312
    unsigned int depth;
 
313
 
 
314
    // In addition to needing X11 server-side support for Xcomposite, it
 
315
    // actually needs to be turned on for this window in order to get a good
 
316
    // thumbnail. If the user has modern hardware/drivers but isn't using a
 
317
    // compositing window manager, that won't be the case. We could
 
318
    // automatically turn it on for all windows here so that we can get
 
319
    // thumbnails, but the transition is visually ugly, so instead we just want
 
320
    // to detect this case and bail out. To do so, we attempt to retrieve the
 
321
    // backing pixmap for the window, which will only exist if the window has
 
322
    // been redirected to offscreen drawing by a compositing window manager. If
 
323
    // it doesn't exist, the calls will raise errors, so we must suppress errors
 
324
    // to prevent libX11 from aborting the program.
 
325
    Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id());
 
326
    if (!src_pixmap) {
 
327
      // Even if the backing pixmap doesn't exist, this still should have
 
328
      // succeeded and returned a valid handle (it just wouldn't be a handle to
 
329
      // anything). So this is a real error path.
 
330
      LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed";
 
331
      return NULL;
 
332
    }
 
333
    if (!XGetGeometry(display_, src_pixmap, &root, &x, &y,
 
334
                      &src_width, &src_height, &border_width,
 
335
                      &depth)) {
 
336
      // If the window does not actually have a backing pixmap, this is the path
 
337
      // that will "fail", so it's a warning rather than an error.
 
338
      LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in "
 
339
                      << "use)";
 
340
      XFreePixmap(display_, src_pixmap);
 
341
      return NULL;
 
342
    }
 
343
 
 
344
    // If we get to here, then composite is in use for this window and it has a
 
345
    // valid backing pixmap.
 
346
 
 
347
    XWindowAttributes attr;
 
348
    if (!XGetWindowAttributes(display_, id.id(), &attr)) {
 
349
      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
 
350
      XFreePixmap(display_, src_pixmap);
 
351
      return NULL;
 
352
    }
 
353
 
 
354
    uint8* data = GetDrawableThumbnail(src_pixmap,
 
355
                                       attr.visual,
 
356
                                       src_width,
 
357
                                       src_height,
 
358
                                       width,
 
359
                                       height);
 
360
    XFreePixmap(display_, src_pixmap);
 
361
    return data;
 
362
  }
 
363
 
 
364
  int GetNumDesktops() {
 
365
    if (!Init()) {
 
366
      return -1;
 
367
    }
 
368
 
 
369
    return XScreenCount(display_);
 
370
  }
 
371
 
 
372
  uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height) {
 
373
    if (!Init()) {
 
374
      return NULL;
 
375
    }
 
376
    XErrorSuppressor error_suppressor(display_);
 
377
 
 
378
    Window root_window = id.id();
 
379
    XWindowAttributes attr;
 
380
    if (!XGetWindowAttributes(display_, root_window, &attr)) {
 
381
      LOG(LS_ERROR) << "XGetWindowAttributes() failed";
 
382
      return NULL;
 
383
    }
 
384
 
 
385
    return GetDrawableThumbnail(root_window,
 
386
                                attr.visual,
 
387
                                attr.width,
 
388
                                attr.height,
 
389
                                width,
 
390
                                height);
 
391
  }
 
392
 
 
393
 private:
 
394
  uint8* GetDrawableThumbnail(Drawable src_drawable,
 
395
                              Visual* visual,
 
396
                              int src_width,
 
397
                              int src_height,
 
398
                              int dst_width,
 
399
                              int dst_height) {
 
400
    if (!has_render_extension_) {
 
401
      // Without the Xrender extension we would have to read the full window and
 
402
      // scale it down in our process. Xrender is over a decade old so we aren't
 
403
      // going to expend effort to support that situation. We still need to
 
404
      // check though because probably some virtual VNC displays are in this
 
405
      // category.
 
406
      LOG(LS_INFO) << "No Xrender extension detected.";
 
407
      return NULL;
 
408
    }
 
409
 
 
410
    XRenderPictFormat* format = XRenderFindVisualFormat(display_,
 
411
                                                        visual);
 
412
    if (!format) {
 
413
      LOG(LS_ERROR) << "XRenderFindVisualFormat() failed";
 
414
      return NULL;
 
415
    }
 
416
 
 
417
    // Create a picture to reference the window pixmap.
 
418
    XRenderPictureAttributes pa;
 
419
    pa.subwindow_mode = IncludeInferiors;  // Don't clip child widgets
 
420
    Picture src = XRenderCreatePicture(display_,
 
421
                                       src_drawable,
 
422
                                       format,
 
423
                                       CPSubwindowMode,
 
424
                                       &pa);
 
425
    if (!src) {
 
426
      LOG(LS_ERROR) << "XRenderCreatePicture() failed";
 
427
      return NULL;
 
428
    }
 
429
 
 
430
    // Create a picture to reference the destination pixmap.
 
431
    Pixmap dst_pixmap = XCreatePixmap(display_,
 
432
                                      src_drawable,
 
433
                                      dst_width,
 
434
                                      dst_height,
 
435
                                      format->depth);
 
436
    if (!dst_pixmap) {
 
437
      LOG(LS_ERROR) << "XCreatePixmap() failed";
 
438
      XRenderFreePicture(display_, src);
 
439
      return NULL;
 
440
    }
 
441
 
 
442
    Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL);
 
443
    if (!dst) {
 
444
      LOG(LS_ERROR) << "XRenderCreatePicture() failed";
 
445
      XFreePixmap(display_, dst_pixmap);
 
446
      XRenderFreePicture(display_, src);
 
447
      return NULL;
 
448
    }
 
449
 
 
450
    // Clear the background.
 
451
    XRenderColor transparent = {0};
 
452
    XRenderFillRectangle(display_,
 
453
                         PictOpSrc,
 
454
                         dst,
 
455
                         &transparent,
 
456
                         0,
 
457
                         0,
 
458
                         dst_width,
 
459
                         dst_height);
 
460
 
 
461
    // Calculate how much we need to scale the image.
 
462
    double scale_x = static_cast<double>(dst_width) /
 
463
        static_cast<double>(src_width);
 
464
    double scale_y = static_cast<double>(dst_height) /
 
465
        static_cast<double>(src_height);
 
466
    double scale = talk_base::_min(scale_y, scale_x);
 
467
 
 
468
    int scaled_width = round(src_width * scale);
 
469
    int scaled_height = round(src_height * scale);
 
470
 
 
471
    // Render the thumbnail centered on both axis.
 
472
    int centered_x = (dst_width - scaled_width) / 2;
 
473
    int centered_y = (dst_height - scaled_height) / 2;
 
474
 
 
475
    // Scaling matrix
 
476
    XTransform xform = { {
 
477
        { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) },
 
478
        { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) },
 
479
        { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) }
 
480
        } };
 
481
    XRenderSetPictureTransform(display_, src, &xform);
 
482
 
 
483
    // Apply filter to smooth out the image.
 
484
    XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0);
 
485
 
 
486
    // Render the image to the destination picture.
 
487
    XRenderComposite(display_,
 
488
                     PictOpSrc,
 
489
                     src,
 
490
                     None,
 
491
                     dst,
 
492
                     0,
 
493
                     0,
 
494
                     0,
 
495
                     0,
 
496
                     centered_x,
 
497
                     centered_y,
 
498
                     scaled_width,
 
499
                     scaled_height);
 
500
 
 
501
    // Get the pixel data from the X server. TODO: XGetImage
 
502
    // might be slow here, compare with ShmGetImage.
 
503
    XImage* image = XGetImage(display_,
 
504
                              dst_pixmap,
 
505
                              0,
 
506
                              0,
 
507
                              dst_width,
 
508
                              dst_height,
 
509
                              AllPlanes, ZPixmap);
 
510
    uint8* data = ArgbToRgba(reinterpret_cast<uint32*>(image->data),
 
511
                             centered_x,
 
512
                             centered_y,
 
513
                             scaled_width,
 
514
                             scaled_height,
 
515
                             dst_width,
 
516
                             dst_height,
 
517
                             false);
 
518
    XDestroyImage(image);
 
519
    XRenderFreePicture(display_, dst);
 
520
    XFreePixmap(display_, dst_pixmap);
 
521
    XRenderFreePicture(display_, src);
 
522
    return data;
 
523
  }
 
524
 
 
525
  uint8* ArgbToRgba(uint32* argb_data, int x, int y, int w, int h,
 
526
                    int stride_x, int stride_y, bool has_alpha) {
 
527
    uint8* p;
 
528
    int len = stride_x * stride_y * 4;
 
529
    uint8* data = new uint8[len];
 
530
    memset(data, 0, len);
 
531
    p = data + 4 * (y * stride_x + x);
 
532
    for (int i = 0; i < h; ++i) {
 
533
      for (int j = 0; j < w; ++j) {
 
534
        uint32 argb;
 
535
        uint32 rgba;
 
536
        argb = argb_data[stride_x * (y + i) + x + j];
 
537
        rgba = (argb << 8) | (argb >> 24);
 
538
        *p = rgba >> 24;
 
539
        ++p;
 
540
        *p = (rgba >> 16) & 0xff;
 
541
        ++p;
 
542
        *p = (rgba >> 8) & 0xff;
 
543
        ++p;
 
544
        *p = has_alpha ? rgba & 0xFF : 0xFF;
 
545
        ++p;
 
546
      }
 
547
      p += (stride_x - w) * 4;
 
548
    }
 
549
    return data;
 
550
  }
 
551
 
 
552
  bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) {
 
553
    Window parent;
 
554
    Window *children;
 
555
    int status;
 
556
    unsigned int num_children;
 
557
    Window root_window = XRootWindow(display_, screen);
 
558
    status = XQueryTree(display_, root_window, &root_window, &parent, &children,
 
559
                        &num_children);
 
560
    if (status == 0) {
 
561
      LOG(LS_ERROR) << "Failed to query for child windows.";
 
562
      return false;
 
563
    }
 
564
    for (unsigned int i = 0; i < num_children; ++i) {
 
565
      // Iterate in reverse order to display windows from front to back.
 
566
      Window app_window = GetApplicationWindow(children[num_children - 1 - i]);
 
567
      if (app_window &&
 
568
          !LinuxWindowPicker::IsDesktopElement(display_, app_window)) {
 
569
        std::string title;
 
570
        if (GetWindowTitle(app_window, &title)) {
 
571
          WindowId id(app_window);
 
572
          WindowDescription desc(id, title);
 
573
          descriptions->push_back(desc);
 
574
        }
 
575
      }
 
576
    }
 
577
    if (children != NULL) {
 
578
      XFree(children);
 
579
    }
 
580
    return true;
 
581
  }
 
582
 
 
583
  bool GetWindowTitle(Window window, std::string* title) {
 
584
    int status;
 
585
    bool result = false;
 
586
    XTextProperty window_name;
 
587
    window_name.value = NULL;
 
588
    if (window) {
 
589
      status = XGetWMName(display_, window, &window_name);
 
590
      if (status && window_name.value && window_name.nitems) {
 
591
        int cnt;
 
592
        char **list = NULL;
 
593
        status = Xutf8TextPropertyToTextList(display_, &window_name, &list,
 
594
                                             &cnt);
 
595
        if (status >= Success && cnt && *list) {
 
596
          if (cnt > 1) {
 
597
            LOG(LS_INFO) << "Window has " << cnt
 
598
                         << " text properties, only using the first one.";
 
599
          }
 
600
          *title = *list;
 
601
          result = true;
 
602
        }
 
603
        if (list != NULL) {
 
604
          XFreeStringList(list);
 
605
        }
 
606
      }
 
607
      if (window_name.value != NULL) {
 
608
        XFree(window_name.value);
 
609
      }
 
610
    }
 
611
    return result;
 
612
  }
 
613
 
 
614
  Window GetApplicationWindow(Window window) {
 
615
    Window root, parent;
 
616
    Window app_window = 0;
 
617
    Window *children;
 
618
    unsigned int num_children;
 
619
    Atom type = None;
 
620
    int format;
 
621
    unsigned long nitems, after;
 
622
    unsigned char *data;
 
623
 
 
624
    int ret = XGetWindowProperty(display_, window,
 
625
                                 wm_state_, 0L, 2,
 
626
                                 False, wm_state_, &type, &format,
 
627
                                 &nitems, &after, &data);
 
628
    if (ret != Success) {
 
629
      LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret
 
630
                    << " for window " << window << ".";
 
631
      return 0;
 
632
    }
 
633
    if (type != None) {
 
634
      int64 state = static_cast<int64>(*data);
 
635
      XFree(data);
 
636
      return state == NormalState ? window : 0;
 
637
    }
 
638
    XFree(data);
 
639
    if (!XQueryTree(display_, window, &root, &parent, &children,
 
640
                    &num_children)) {
 
641
      LOG(LS_ERROR) << "Failed to query for child windows although window"
 
642
                    << "does not have a valid WM_STATE.";
 
643
      return 0;
 
644
    }
 
645
    for (unsigned int i = 0; i < num_children; ++i) {
 
646
      app_window = GetApplicationWindow(children[i]);
 
647
      if (app_window) {
 
648
        break;
 
649
      }
 
650
    }
 
651
    if (children != NULL) {
 
652
      XFree(children);
 
653
    }
 
654
    return app_window;
 
655
  }
 
656
 
 
657
  Atom wm_state_;
 
658
  Atom net_wm_icon_;
 
659
  Display* display_;
 
660
  bool has_composite_extension_;
 
661
  bool has_render_extension_;
 
662
};
 
663
 
 
664
LinuxWindowPicker::LinuxWindowPicker() : enumerator_(new XWindowEnumerator()) {
 
665
}
 
666
 
 
667
LinuxWindowPicker::~LinuxWindowPicker() {
 
668
}
 
669
 
 
670
bool LinuxWindowPicker::IsDesktopElement(_XDisplay* display, Window window) {
 
671
  if (window == 0) {
 
672
    LOG(LS_WARNING) << "Zero is never a valid window.";
 
673
    return false;
 
674
  }
 
675
  XClassHint class_hint;
 
676
  Status s = XGetClassHint(display, window, &class_hint);
 
677
  bool result = false;
 
678
  if (s == 0) {
 
679
    // No hints, assume this is a normal application window.
 
680
    return result;
 
681
  }
 
682
  static const std::string gnome_panel("gnome-panel");
 
683
  static const std::string desktop_window("desktop_window");
 
684
 
 
685
  if (gnome_panel.compare(class_hint.res_name) == 0 ||
 
686
      desktop_window.compare(class_hint.res_name) == 0) {
 
687
    result = true;
 
688
  }
 
689
  XFree(class_hint.res_name);
 
690
  XFree(class_hint.res_class);
 
691
  return result;
 
692
}
 
693
 
 
694
bool LinuxWindowPicker::Init() {
 
695
  return enumerator_->Init();
 
696
}
 
697
 
 
698
bool LinuxWindowPicker::GetWindowList(WindowDescriptionList* descriptions) {
 
699
  return enumerator_->EnumerateWindows(descriptions);
 
700
}
 
701
 
 
702
bool LinuxWindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) {
 
703
  return enumerator_->EnumerateDesktops(descriptions);
 
704
}
 
705
 
 
706
bool LinuxWindowPicker::IsVisible(const WindowId& id) {
 
707
  return enumerator_->IsVisible(id);
 
708
}
 
709
 
 
710
bool LinuxWindowPicker::MoveToFront(const WindowId& id) {
 
711
  return enumerator_->MoveToFront(id);
 
712
}
 
713
 
 
714
 
 
715
uint8* LinuxWindowPicker::GetWindowIcon(const WindowId& id, int* width,
 
716
                                        int* height) {
 
717
  return enumerator_->GetWindowIcon(id, width, height);
 
718
}
 
719
 
 
720
uint8* LinuxWindowPicker::GetWindowThumbnail(const WindowId& id, int width,
 
721
                                             int height) {
 
722
  return enumerator_->GetWindowThumbnail(id, width, height);
 
723
}
 
724
 
 
725
int LinuxWindowPicker::GetNumDesktops() {
 
726
  return enumerator_->GetNumDesktops();
 
727
}
 
728
 
 
729
uint8* LinuxWindowPicker::GetDesktopThumbnail(const DesktopId& id,
 
730
                                              int width,
 
731
                                              int height) {
 
732
  return enumerator_->GetDesktopThumbnail(id, width, height);
 
733
}
 
734
 
 
735
}  // namespace talk_base