| /* |
| * Copyright 2010 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/base/x11windowpicker.h" |
| |
| #include <math.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include <X11/Xatom.h> |
| #include <X11/extensions/Xcomposite.h> |
| #include <X11/extensions/Xrender.h> |
| #include <X11/Xutil.h> |
| |
| #include "webrtc/base/constructormagic.h" |
| #include "webrtc/base/logging.h" |
| |
| namespace rtc { |
| |
| // Convenience wrapper for XGetWindowProperty results. |
| template <class PropertyType> |
| class XWindowProperty { |
| public: |
| XWindowProperty(Display* display, Window window, Atom property) |
| : data_(NULL) { |
| const int kBitsPerByte = 8; |
| Atom actual_type; |
| int actual_format; |
| unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty |
| int status = XGetWindowProperty(display, window, property, 0L, ~0L, False, |
| AnyPropertyType, &actual_type, |
| &actual_format, &size_, |
| &bytes_after, &data_); |
| succeeded_ = (status == Success); |
| if (!succeeded_) { |
| data_ = NULL; // Ensure nothing is freed. |
| } else if (sizeof(PropertyType) * kBitsPerByte != actual_format) { |
| LOG(LS_WARNING) << "Returned type size differs from " |
| "requested type size."; |
| succeeded_ = false; |
| // We still need to call XFree in this case, so leave data_ alone. |
| } |
| if (!succeeded_) { |
| size_ = 0; |
| } |
| } |
| |
| ~XWindowProperty() { |
| if (data_) { |
| XFree(data_); |
| } |
| } |
| |
| bool succeeded() const { return succeeded_; } |
| size_t size() const { return size_; } |
| const PropertyType* data() const { |
| return reinterpret_cast<PropertyType*>(data_); |
| } |
| PropertyType* data() { |
| return reinterpret_cast<PropertyType*>(data_); |
| } |
| |
| private: |
| bool succeeded_; |
| unsigned long size_; // NOLINT: type required by XGetWindowProperty |
| unsigned char* data_; |
| |
| RTC_DISALLOW_COPY_AND_ASSIGN(XWindowProperty); |
| }; |
| |
| // Stupid X11. It seems none of the synchronous returns codes from X11 calls |
| // are meaningful unless an asynchronous error handler is configured. This |
| // RAII class registers and unregisters an X11 error handler. |
| class XErrorSuppressor { |
| public: |
| explicit XErrorSuppressor(Display* display) |
| : display_(display), original_error_handler_(NULL) { |
| SuppressX11Errors(); |
| } |
| ~XErrorSuppressor() { |
| UnsuppressX11Errors(); |
| } |
| |
| private: |
| static int ErrorHandler(Display* display, XErrorEvent* e) { |
| char buf[256]; |
| XGetErrorText(display, e->error_code, buf, sizeof buf); |
| LOG(LS_WARNING) << "Received X11 error \"" << buf << "\" for request code " |
| << static_cast<unsigned int>(e->request_code); |
| return 0; |
| } |
| |
| void SuppressX11Errors() { |
| XFlush(display_); |
| XSync(display_, False); |
| original_error_handler_ = XSetErrorHandler(&ErrorHandler); |
| } |
| |
| void UnsuppressX11Errors() { |
| XFlush(display_); |
| XSync(display_, False); |
| XErrorHandler handler = XSetErrorHandler(original_error_handler_); |
| if (handler != &ErrorHandler) { |
| LOG(LS_WARNING) << "Unbalanced XSetErrorHandler() calls detected. " |
| << "Final error handler may not be what you expect!"; |
| } |
| original_error_handler_ = NULL; |
| } |
| |
| Display* display_; |
| XErrorHandler original_error_handler_; |
| |
| RTC_DISALLOW_COPY_AND_ASSIGN(XErrorSuppressor); |
| }; |
| |
| // Hiding all X11 specifics inside its own class. This to avoid |
| // conflicts between talk and X11 header declarations. |
| class XWindowEnumerator { |
| public: |
| XWindowEnumerator() |
| : display_(NULL), |
| has_composite_extension_(false), |
| has_render_extension_(false) { |
| } |
| |
| ~XWindowEnumerator() { |
| if (display_ != NULL) { |
| XCloseDisplay(display_); |
| } |
| } |
| |
| bool Init() { |
| if (display_ != NULL) { |
| // Already initialized. |
| return true; |
| } |
| display_ = XOpenDisplay(NULL); |
| if (display_ == NULL) { |
| LOG(LS_ERROR) << "Failed to open display."; |
| return false; |
| } |
| |
| XErrorSuppressor error_suppressor(display_); |
| |
| wm_state_ = XInternAtom(display_, "WM_STATE", True); |
| net_wm_icon_ = XInternAtom(display_, "_NET_WM_ICON", False); |
| |
| int event_base, error_base, major_version, minor_version; |
| if (XCompositeQueryExtension(display_, &event_base, &error_base) && |
| XCompositeQueryVersion(display_, &major_version, &minor_version) && |
| // XCompositeNameWindowPixmap() requires version 0.2 |
| (major_version > 0 || minor_version >= 2)) { |
| has_composite_extension_ = true; |
| } else { |
| LOG(LS_INFO) << "Xcomposite extension not available or too old."; |
| } |
| |
| if (XRenderQueryExtension(display_, &event_base, &error_base) && |
| XRenderQueryVersion(display_, &major_version, &minor_version) && |
| // XRenderSetPictureTransform() requires version 0.6 |
| (major_version > 0 || minor_version >= 6)) { |
| has_render_extension_ = true; |
| } else { |
| LOG(LS_INFO) << "Xrender extension not available or too old."; |
| } |
| return true; |
| } |
| |
| bool EnumerateWindows(WindowDescriptionList* descriptions) { |
| if (!Init()) { |
| return false; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| int num_screens = XScreenCount(display_); |
| bool result = false; |
| for (int i = 0; i < num_screens; ++i) { |
| if (EnumerateScreenWindows(descriptions, i)) { |
| // We know we succeded on at least one screen. |
| result = true; |
| } |
| } |
| return result; |
| } |
| |
| bool EnumerateDesktops(DesktopDescriptionList* descriptions) { |
| if (!Init()) { |
| return false; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| Window default_root_window = XDefaultRootWindow(display_); |
| int num_screens = XScreenCount(display_); |
| for (int i = 0; i < num_screens; ++i) { |
| Window root_window = XRootWindow(display_, i); |
| DesktopId id(DesktopId(root_window, i)); |
| // TODO: Figure out an appropriate desktop title. |
| DesktopDescription desc(id, ""); |
| desc.set_primary(root_window == default_root_window); |
| descriptions->push_back(desc); |
| } |
| return num_screens > 0; |
| } |
| |
| bool IsVisible(const WindowId& id) { |
| if (!Init()) { |
| return false; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| XWindowAttributes attr; |
| if (!XGetWindowAttributes(display_, id.id(), &attr)) { |
| LOG(LS_ERROR) << "XGetWindowAttributes() failed"; |
| return false; |
| } |
| return attr.map_state == IsViewable; |
| } |
| |
| bool MoveToFront(const WindowId& id) { |
| if (!Init()) { |
| return false; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| unsigned int num_children; |
| Window* children; |
| Window parent; |
| Window root; |
| |
| // Find root window to pass event to. |
| int status = XQueryTree(display_, id.id(), &root, &parent, &children, |
| &num_children); |
| if (status == 0) { |
| LOG(LS_WARNING) << "Failed to query for child windows."; |
| return false; |
| } |
| if (children != NULL) { |
| XFree(children); |
| } |
| |
| // Move the window to front. |
| XRaiseWindow(display_, id.id()); |
| |
| // Some window managers (e.g., metacity in GNOME) consider it illegal to |
| // raise a window without also giving it input focus with |
| // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough. |
| Atom atom = XInternAtom(display_, "_NET_ACTIVE_WINDOW", True); |
| if (atom != None) { |
| XEvent xev; |
| long event_mask; |
| |
| xev.xclient.type = ClientMessage; |
| xev.xclient.serial = 0; |
| xev.xclient.send_event = True; |
| xev.xclient.window = id.id(); |
| xev.xclient.message_type = atom; |
| |
| // The format member is set to 8, 16, or 32 and specifies whether the |
| // data should be viewed as a list of bytes, shorts, or longs. |
| xev.xclient.format = 32; |
| |
| xev.xclient.data.l[0] = 0; |
| xev.xclient.data.l[1] = 0; |
| xev.xclient.data.l[2] = 0; |
| xev.xclient.data.l[3] = 0; |
| xev.xclient.data.l[4] = 0; |
| |
| event_mask = SubstructureRedirectMask | SubstructureNotifyMask; |
| |
| XSendEvent(display_, root, False, event_mask, &xev); |
| } |
| XFlush(display_); |
| return true; |
| } |
| |
| uint8_t* GetWindowIcon(const WindowId& id, int* width, int* height) { |
| if (!Init()) { |
| return NULL; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| Atom ret_type; |
| int format; |
| unsigned long length, bytes_after, size; |
| unsigned char* data = NULL; |
| |
| // Find out the size of the icon data. |
| if (XGetWindowProperty( |
| display_, id.id(), net_wm_icon_, 0, 0, False, XA_CARDINAL, |
| &ret_type, &format, &length, &size, &data) == Success && |
| data) { |
| XFree(data); |
| } else { |
| LOG(LS_ERROR) << "Failed to get size of the icon."; |
| return NULL; |
| } |
| // Get the icon data, the format is one uint32_t each for width and height, |
| // followed by the actual pixel data. |
| if (size >= 2 && |
| XGetWindowProperty( |
| display_, id.id(), net_wm_icon_, 0, size, False, XA_CARDINAL, |
| &ret_type, &format, &length, &bytes_after, &data) == Success && |
| data) { |
| uint32_t* data_ptr = reinterpret_cast<uint32_t*>(data); |
| int w, h; |
| w = data_ptr[0]; |
| h = data_ptr[1]; |
| if (size < static_cast<unsigned long>(w * h + 2)) { |
| XFree(data); |
| LOG(LS_ERROR) << "Not a vaild icon."; |
| return NULL; |
| } |
| uint8_t* rgba = ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true); |
| XFree(data); |
| *width = w; |
| *height = h; |
| return rgba; |
| } else { |
| LOG(LS_ERROR) << "Failed to get window icon data."; |
| return NULL; |
| } |
| } |
| |
| uint8_t* GetWindowThumbnail(const WindowId& id, int width, int height) { |
| if (!Init()) { |
| return NULL; |
| } |
| |
| if (!has_composite_extension_) { |
| // Without the Xcomposite extension we would only get a good thumbnail if |
| // the whole window is visible on screen and not covered by any |
| // other window. This is not something we want so instead, just |
| // bail out. |
| LOG(LS_INFO) << "No Xcomposite extension detected."; |
| return NULL; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| |
| Window root; |
| int x; |
| int y; |
| unsigned int src_width; |
| unsigned int src_height; |
| unsigned int border_width; |
| unsigned int depth; |
| |
| // In addition to needing X11 server-side support for Xcomposite, it |
| // actually needs to be turned on for this window in order to get a good |
| // thumbnail. If the user has modern hardware/drivers but isn't using a |
| // compositing window manager, that won't be the case. Here we |
| // automatically turn it on for shareable windows so that we can get |
| // thumbnails. We used to avoid it because the transition is visually ugly, |
| // but recent window managers don't always redirect windows which led to |
| // no thumbnails at all, which is a worse experience. |
| |
| // Redirect drawing to an offscreen buffer (ie, turn on compositing). |
| // X11 remembers what has requested this and will turn it off for us when |
| // we exit. |
| XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic); |
| Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id()); |
| if (!src_pixmap) { |
| // Even if the backing pixmap doesn't exist, this still should have |
| // succeeded and returned a valid handle (it just wouldn't be a handle to |
| // anything). So this is a real error path. |
| LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed"; |
| return NULL; |
| } |
| if (!XGetGeometry(display_, src_pixmap, &root, &x, &y, |
| &src_width, &src_height, &border_width, |
| &depth)) { |
| // If the window does not actually have a backing pixmap, this is the path |
| // that will "fail", so it's a warning rather than an error. |
| LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in " |
| << "use)"; |
| XFreePixmap(display_, src_pixmap); |
| return NULL; |
| } |
| |
| // If we get to here, then composite is in use for this window and it has a |
| // valid backing pixmap. |
| |
| XWindowAttributes attr; |
| if (!XGetWindowAttributes(display_, id.id(), &attr)) { |
| LOG(LS_ERROR) << "XGetWindowAttributes() failed"; |
| XFreePixmap(display_, src_pixmap); |
| return NULL; |
| } |
| |
| uint8_t* data = GetDrawableThumbnail(src_pixmap, attr.visual, src_width, |
| src_height, width, height); |
| XFreePixmap(display_, src_pixmap); |
| return data; |
| } |
| |
| int GetNumDesktops() { |
| if (!Init()) { |
| return -1; |
| } |
| |
| return XScreenCount(display_); |
| } |
| |
| uint8_t* GetDesktopThumbnail(const DesktopId& id, int width, int height) { |
| if (!Init()) { |
| return NULL; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| |
| Window root_window = id.id(); |
| XWindowAttributes attr; |
| if (!XGetWindowAttributes(display_, root_window, &attr)) { |
| LOG(LS_ERROR) << "XGetWindowAttributes() failed"; |
| return NULL; |
| } |
| |
| return GetDrawableThumbnail(root_window, |
| attr.visual, |
| attr.width, |
| attr.height, |
| width, |
| height); |
| } |
| |
| bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) { |
| if (!Init()) { |
| return false; |
| } |
| XErrorSuppressor error_suppressor(display_); |
| XWindowAttributes attr; |
| if (!XGetWindowAttributes(display_, id.id(), &attr)) { |
| LOG(LS_ERROR) << "XGetWindowAttributes() failed"; |
| return false; |
| } |
| *width = attr.width; |
| *height = attr.height; |
| return true; |
| } |
| |
| private: |
| uint8_t* GetDrawableThumbnail(Drawable src_drawable, |
| Visual* visual, |
| int src_width, |
| int src_height, |
| int dst_width, |
| int dst_height) { |
| if (!has_render_extension_) { |
| // Without the Xrender extension we would have to read the full window and |
| // scale it down in our process. Xrender is over a decade old so we aren't |
| // going to expend effort to support that situation. We still need to |
| // check though because probably some virtual VNC displays are in this |
| // category. |
| LOG(LS_INFO) << "No Xrender extension detected."; |
| return NULL; |
| } |
| |
| XRenderPictFormat* format = XRenderFindVisualFormat(display_, |
| visual); |
| if (!format) { |
| LOG(LS_ERROR) << "XRenderFindVisualFormat() failed"; |
| return NULL; |
| } |
| |
| // Create a picture to reference the window pixmap. |
| XRenderPictureAttributes pa; |
| pa.subwindow_mode = IncludeInferiors; // Don't clip child widgets |
| Picture src = XRenderCreatePicture(display_, |
| src_drawable, |
| format, |
| CPSubwindowMode, |
| &pa); |
| if (!src) { |
| LOG(LS_ERROR) << "XRenderCreatePicture() failed"; |
| return NULL; |
| } |
| |
| // Create a picture to reference the destination pixmap. |
| Pixmap dst_pixmap = XCreatePixmap(display_, |
| src_drawable, |
| dst_width, |
| dst_height, |
| format->depth); |
| if (!dst_pixmap) { |
| LOG(LS_ERROR) << "XCreatePixmap() failed"; |
| XRenderFreePicture(display_, src); |
| return NULL; |
| } |
| |
| Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL); |
| if (!dst) { |
| LOG(LS_ERROR) << "XRenderCreatePicture() failed"; |
| XFreePixmap(display_, dst_pixmap); |
| XRenderFreePicture(display_, src); |
| return NULL; |
| } |
| |
| // Clear the background. |
| XRenderColor transparent = {0}; |
| XRenderFillRectangle(display_, |
| PictOpSrc, |
| dst, |
| &transparent, |
| 0, |
| 0, |
| dst_width, |
| dst_height); |
| |
| // Calculate how much we need to scale the image. |
| double scale_x = static_cast<double>(dst_width) / |
| static_cast<double>(src_width); |
| double scale_y = static_cast<double>(dst_height) / |
| static_cast<double>(src_height); |
| double scale = std::min(scale_y, scale_x); |
| |
| int scaled_width = round(src_width * scale); |
| int scaled_height = round(src_height * scale); |
| |
| // Render the thumbnail centered on both axis. |
| int centered_x = (dst_width - scaled_width) / 2; |
| int centered_y = (dst_height - scaled_height) / 2; |
| |
| // Scaling matrix |
| XTransform xform = { { |
| { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) }, |
| { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) }, |
| { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) } |
| } }; |
| XRenderSetPictureTransform(display_, src, &xform); |
| |
| // Apply filter to smooth out the image. |
| XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0); |
| |
| // Render the image to the destination picture. |
| XRenderComposite(display_, |
| PictOpSrc, |
| src, |
| None, |
| dst, |
| 0, |
| 0, |
| 0, |
| 0, |
| centered_x, |
| centered_y, |
| scaled_width, |
| scaled_height); |
| |
| // Get the pixel data from the X server. TODO: XGetImage |
| // might be slow here, compare with ShmGetImage. |
| XImage* image = XGetImage(display_, |
| dst_pixmap, |
| 0, |
| 0, |
| dst_width, |
| dst_height, |
| AllPlanes, ZPixmap); |
| uint8_t* data = ArgbToRgba(reinterpret_cast<uint32_t*>(image->data), |
| centered_x, centered_y, scaled_width, |
| scaled_height, dst_width, dst_height, false); |
| XDestroyImage(image); |
| XRenderFreePicture(display_, dst); |
| XFreePixmap(display_, dst_pixmap); |
| XRenderFreePicture(display_, src); |
| return data; |
| } |
| |
| uint8_t* ArgbToRgba(uint32_t* argb_data, |
| int x, |
| int y, |
| int w, |
| int h, |
| int stride_x, |
| int stride_y, |
| bool has_alpha) { |
| uint8_t* p; |
| int len = stride_x * stride_y * 4; |
| uint8_t* data = new uint8_t[len]; |
| memset(data, 0, len); |
| p = data + 4 * (y * stride_x + x); |
| for (int i = 0; i < h; ++i) { |
| for (int j = 0; j < w; ++j) { |
| uint32_t argb; |
| uint32_t rgba; |
| argb = argb_data[stride_x * (y + i) + x + j]; |
| rgba = (argb << 8) | (argb >> 24); |
| *p = rgba >> 24; |
| ++p; |
| *p = (rgba >> 16) & 0xff; |
| ++p; |
| *p = (rgba >> 8) & 0xff; |
| ++p; |
| *p = has_alpha ? rgba & 0xFF : 0xFF; |
| ++p; |
| } |
| p += (stride_x - w) * 4; |
| } |
| return data; |
| } |
| |
| bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) { |
| Window parent; |
| Window *children; |
| int status; |
| unsigned int num_children; |
| Window root_window = XRootWindow(display_, screen); |
| status = XQueryTree(display_, root_window, &root_window, &parent, &children, |
| &num_children); |
| if (status == 0) { |
| LOG(LS_ERROR) << "Failed to query for child windows."; |
| return false; |
| } |
| for (unsigned int i = 0; i < num_children; ++i) { |
| // Iterate in reverse order to display windows from front to back. |
| #ifdef CHROMEOS |
| // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to |
| // filter, just return all windows and let the picker scan through them. |
| Window app_window = children[num_children - 1 - i]; |
| #else |
| Window app_window = GetApplicationWindow(children[num_children - 1 - i]); |
| #endif |
| if (app_window && |
| !X11WindowPicker::IsDesktopElement(display_, app_window)) { |
| std::string title; |
| if (GetWindowTitle(app_window, &title)) { |
| WindowId id(app_window); |
| WindowDescription desc(id, title); |
| descriptions->push_back(desc); |
| } |
| } |
| } |
| if (children != NULL) { |
| XFree(children); |
| } |
| return true; |
| } |
| |
| bool GetWindowTitle(Window window, std::string* title) { |
| int status; |
| bool result = false; |
| XTextProperty window_name; |
| window_name.value = NULL; |
| if (window) { |
| status = XGetWMName(display_, window, &window_name); |
| if (status && window_name.value && window_name.nitems) { |
| int cnt; |
| char **list = NULL; |
| status = Xutf8TextPropertyToTextList(display_, &window_name, &list, |
| &cnt); |
| if (status >= Success && cnt && *list) { |
| if (cnt > 1) { |
| LOG(LS_INFO) << "Window has " << cnt |
| << " text properties, only using the first one."; |
| } |
| *title = *list; |
| result = true; |
| } |
| if (list != NULL) { |
| XFreeStringList(list); |
| } |
| } |
| if (window_name.value != NULL) { |
| XFree(window_name.value); |
| } |
| } |
| return result; |
| } |
| |
| Window GetApplicationWindow(Window window) { |
| Window root, parent; |
| Window app_window = 0; |
| Window *children; |
| unsigned int num_children; |
| Atom type = None; |
| int format; |
| unsigned long nitems, after; |
| unsigned char *data; |
| |
| int ret = XGetWindowProperty(display_, window, |
| wm_state_, 0L, 2, |
| False, wm_state_, &type, &format, |
| &nitems, &after, &data); |
| if (ret != Success) { |
| LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret |
| << " for window " << window << "."; |
| return 0; |
| } |
| if (type != None) { |
| int64_t state = static_cast<int64_t>(*data); |
| XFree(data); |
| return state == NormalState ? window : 0; |
| } |
| XFree(data); |
| if (!XQueryTree(display_, window, &root, &parent, &children, |
| &num_children)) { |
| LOG(LS_ERROR) << "Failed to query for child windows although window" |
| << "does not have a valid WM_STATE."; |
| return 0; |
| } |
| for (unsigned int i = 0; i < num_children; ++i) { |
| app_window = GetApplicationWindow(children[i]); |
| if (app_window) { |
| break; |
| } |
| } |
| if (children != NULL) { |
| XFree(children); |
| } |
| return app_window; |
| } |
| |
| Atom wm_state_; |
| Atom net_wm_icon_; |
| Display* display_; |
| bool has_composite_extension_; |
| bool has_render_extension_; |
| }; |
| |
| X11WindowPicker::X11WindowPicker() : enumerator_(new XWindowEnumerator()) { |
| } |
| |
| X11WindowPicker::~X11WindowPicker() { |
| } |
| |
| bool X11WindowPicker::IsDesktopElement(_XDisplay* display, Window window) { |
| if (window == 0) { |
| LOG(LS_WARNING) << "Zero is never a valid window."; |
| return false; |
| } |
| |
| // First look for _NET_WM_WINDOW_TYPE. The standard |
| // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306) |
| // says this hint *should* be present on all windows, and we use the existence |
| // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not |
| // a desktop element (that is, only "normal" windows should be shareable). |
| Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); |
| XWindowProperty<uint32_t> window_type(display, window, window_type_atom); |
| if (window_type.succeeded() && window_type.size() > 0) { |
| Atom normal_window_type_atom = XInternAtom( |
| display, "_NET_WM_WINDOW_TYPE_NORMAL", True); |
| uint32_t* end = window_type.data() + window_type.size(); |
| bool is_normal = (end != std::find( |
| window_type.data(), end, normal_window_type_atom)); |
| return !is_normal; |
| } |
| |
| // Fall back on using the hint. |
| XClassHint class_hint; |
| Status s = XGetClassHint(display, window, &class_hint); |
| bool result = false; |
| if (s == 0) { |
| // No hints, assume this is a normal application window. |
| return result; |
| } |
| static const std::string gnome_panel("gnome-panel"); |
| static const std::string desktop_window("desktop_window"); |
| |
| if (gnome_panel.compare(class_hint.res_name) == 0 || |
| desktop_window.compare(class_hint.res_name) == 0) { |
| result = true; |
| } |
| XFree(class_hint.res_name); |
| XFree(class_hint.res_class); |
| return result; |
| } |
| |
| bool X11WindowPicker::Init() { |
| return enumerator_->Init(); |
| } |
| |
| bool X11WindowPicker::GetWindowList(WindowDescriptionList* descriptions) { |
| return enumerator_->EnumerateWindows(descriptions); |
| } |
| |
| bool X11WindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { |
| return enumerator_->EnumerateDesktops(descriptions); |
| } |
| |
| bool X11WindowPicker::IsVisible(const WindowId& id) { |
| return enumerator_->IsVisible(id); |
| } |
| |
| bool X11WindowPicker::MoveToFront(const WindowId& id) { |
| return enumerator_->MoveToFront(id); |
| } |
| |
| uint8_t* X11WindowPicker::GetWindowIcon(const WindowId& id, |
| int* width, |
| int* height) { |
| return enumerator_->GetWindowIcon(id, width, height); |
| } |
| |
| uint8_t* X11WindowPicker::GetWindowThumbnail(const WindowId& id, |
| int width, |
| int height) { |
| return enumerator_->GetWindowThumbnail(id, width, height); |
| } |
| |
| int X11WindowPicker::GetNumDesktops() { |
| return enumerator_->GetNumDesktops(); |
| } |
| |
| uint8_t* X11WindowPicker::GetDesktopThumbnail(const DesktopId& id, |
| int width, |
| int height) { |
| return enumerator_->GetDesktopThumbnail(id, width, height); |
| } |
| |
| bool X11WindowPicker::GetDesktopDimensions(const DesktopId& id, int* width, |
| int* height) { |
| return enumerator_->GetDesktopDimensions(id, width, height); |
| } |
| |
| } // namespace rtc |