LCOV - code coverage report
Current view: top level - media/webrtc/trunk/webrtc/modules/desktop_capture - window_capturer_x11.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 178 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 16 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  *  Copyright (c) 2013 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 <assert.h>
      12             : #include <string.h>
      13             : #include <X11/Xatom.h>
      14             : #include <X11/extensions/Xcomposite.h>
      15             : #include <X11/extensions/Xrender.h>
      16             : #include <X11/Xutil.h>
      17             : 
      18             : #include <algorithm>
      19             : 
      20             : #include "webrtc/base/constructormagic.h"
      21             : #include "webrtc/base/scoped_ref_ptr.h"
      22             : #include "webrtc/modules/desktop_capture/desktop_capturer.h"
      23             : #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
      24             : #include "webrtc/modules/desktop_capture/desktop_frame.h"
      25             : #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
      26             : #include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
      27             : #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
      28             : #include "webrtc/system_wrappers/include/logging.h"
      29             : #include "webrtc/modules/desktop_capture/x11/shared_x_util.h"
      30             : 
      31             : namespace webrtc {
      32             : 
      33             : namespace {
      34             : 
      35             : class WindowCapturerLinux : public DesktopCapturer,
      36             :                             public SharedXDisplay::XEventHandler {
      37             :  public:
      38             :   WindowCapturerLinux(const DesktopCaptureOptions& options);
      39             :   ~WindowCapturerLinux() override;
      40             : 
      41             :   // DesktopCapturer interface.
      42             :   void Start(Callback* callback) override;
      43             :   void Stop() override;
      44             :   void CaptureFrame() override;
      45             :   bool GetSourceList(SourceList* sources) override;
      46             :   bool SelectSource(SourceId id) override;
      47             :   bool FocusOnSelectedSource() override;
      48             : 
      49             :   // SharedXDisplay::XEventHandler interface.
      50             :   bool HandleXEvent(const XEvent& event) override;
      51             : 
      52             :  private:
      53           0 :   Display* display() { return x_display_->display(); }
      54             : 
      55             :   // Iterates through |window| hierarchy to find first visible window, i.e. one
      56             :   // that has WM_STATE property set to NormalState.
      57             :   // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
      58             :   ::Window GetApplicationWindow(::Window window);
      59             : 
      60             :   // Returns true if the |window| is a desktop element.
      61             :   bool IsDesktopElement(::Window window);
      62             : 
      63             :   // Returns window title for the specified X |window|.
      64             :   bool GetWindowTitle(::Window window, std::string* title);
      65             : 
      66             :   // Returns the id of the owning process.
      67             :   int GetWindowProcessID(::Window window);
      68             : 
      69             :   Callback* callback_ = nullptr;
      70             : 
      71             :   rtc::scoped_refptr<SharedXDisplay> x_display_;
      72             : 
      73             :   Atom wm_state_atom_;
      74             :   Atom window_type_atom_;
      75             :   Atom normal_window_type_atom_;
      76             :   bool has_composite_extension_ = false;
      77             : 
      78             :   ::Window selected_window_ = 0;
      79             :   XServerPixelBuffer x_server_pixel_buffer_;
      80             : 
      81             :   RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
      82             : };
      83             : 
      84           0 : WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options)
      85           0 :     : x_display_(options.x_display()) {
      86             :   // Create Atoms so we don't need to do it every time they are used.
      87           0 :   wm_state_atom_ = XInternAtom(display(), "WM_STATE", True);
      88           0 :   window_type_atom_ = XInternAtom(display(), "_NET_WM_WINDOW_TYPE", True);
      89           0 :   normal_window_type_atom_ = XInternAtom(
      90             :       display(), "_NET_WM_WINDOW_TYPE_NORMAL", True);
      91             : 
      92             :   int event_base, error_base, major_version, minor_version;
      93           0 :   if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
      94           0 :       XCompositeQueryVersion(display(), &major_version, &minor_version) &&
      95             :       // XCompositeNameWindowPixmap() requires version 0.2
      96           0 :       (major_version > 0 || minor_version >= 2)) {
      97           0 :     has_composite_extension_ = true;
      98             :   } else {
      99           0 :     LOG(LS_INFO) << "Xcomposite extension not available or too old.";
     100             :   }
     101             : 
     102           0 :   x_display_->AddEventHandler(ConfigureNotify, this);
     103           0 : }
     104             : 
     105           0 : WindowCapturerLinux::~WindowCapturerLinux() {
     106           0 :   x_display_->RemoveEventHandler(ConfigureNotify, this);
     107           0 : }
     108             : 
     109           0 : bool WindowCapturerLinux::GetSourceList(SourceList* sources) {
     110           0 :   SourceList result;
     111             : 
     112           0 :   XErrorTrap error_trap(display());
     113             : 
     114           0 :   int num_screens = XScreenCount(display());
     115           0 :   for (int screen = 0; screen < num_screens; ++screen) {
     116           0 :     ::Window root_window = XRootWindow(display(), screen);
     117             :     ::Window parent;
     118             :     ::Window *children;
     119             :     unsigned int num_children;
     120           0 :     int status = XQueryTree(display(), root_window, &root_window, &parent,
     121           0 :                             &children, &num_children);
     122           0 :     if (status == 0) {
     123           0 :       LOG(LS_ERROR) << "Failed to query for child windows for screen "
     124           0 :                     << screen;
     125           0 :       continue;
     126             :     }
     127             : 
     128           0 :     for (unsigned int i = 0; i < num_children; ++i) {
     129             :       // Iterate in reverse order to return windows from front to back.
     130             :       ::Window app_window =
     131           0 :           GetApplicationWindow(children[num_children - 1 - i]);
     132           0 :       if (app_window && !IsDesktopElement(app_window)) {
     133           0 :         Source w;
     134           0 :         w.id = app_window;
     135             : 
     136           0 :         unsigned int processId = GetWindowProcessID(app_window);
     137           0 :         w.pid = (pid_t)processId;
     138             : 
     139             :         XWindowAttributes window_attr;
     140           0 :         if(!XGetWindowAttributes(display(),w.id,&window_attr)){
     141           0 :           LOG(LS_ERROR)<<"Bad request for attributes for window ID:"<<w.id;
     142           0 :           continue;
     143             :         }
     144           0 :         if((window_attr.width <= 0) || (window_attr.height <=0)){
     145           0 :           continue;
     146             :         }
     147             : 
     148           0 :         if (GetWindowTitle(app_window, &w.title))
     149           0 :           result.push_back(w);
     150             :       }
     151             :     }
     152             : 
     153           0 :     if (children)
     154           0 :       XFree(children);
     155             :   }
     156             : 
     157           0 :   sources->swap(result);
     158             : 
     159           0 :   return true;
     160             : }
     161             : 
     162           0 : bool WindowCapturerLinux::SelectSource(SourceId id) {
     163           0 :   if (!x_server_pixel_buffer_.Init(display(), id))
     164           0 :     return false;
     165             : 
     166             :   // Tell the X server to send us window resizing events.
     167           0 :   XSelectInput(display(), id, StructureNotifyMask);
     168             : 
     169           0 :   selected_window_ = id;
     170             : 
     171             :   // In addition to needing X11 server-side support for Xcomposite, it actually
     172             :   // needs to be turned on for the window. If the user has modern
     173             :   // hardware/drivers but isn't using a compositing window manager, that won't
     174             :   // be the case. Here we automatically turn it on.
     175             : 
     176             :   // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
     177             :   // remembers who has requested this and will turn it off for us when we exit.
     178           0 :   XCompositeRedirectWindow(display(), id, CompositeRedirectAutomatic);
     179             : 
     180           0 :   return true;
     181             : }
     182             : 
     183           0 : bool WindowCapturerLinux::FocusOnSelectedSource() {
     184           0 :   if (!selected_window_)
     185           0 :     return false;
     186             : 
     187             :   unsigned int num_children;
     188             :   ::Window* children;
     189             :   ::Window parent;
     190             :   ::Window root;
     191             :   // Find the root window to pass event to.
     192           0 :   int status = XQueryTree(
     193           0 :       display(), selected_window_, &root, &parent, &children, &num_children);
     194           0 :   if (status == 0) {
     195           0 :     LOG(LS_ERROR) << "Failed to query for the root window.";
     196           0 :     return false;
     197             :   }
     198             : 
     199           0 :   if (children)
     200           0 :     XFree(children);
     201             : 
     202           0 :   XRaiseWindow(display(), selected_window_);
     203             : 
     204             :   // Some window managers (e.g., metacity in GNOME) consider it illegal to
     205             :   // raise a window without also giving it input focus with
     206             :   // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
     207           0 :   Atom atom = XInternAtom(display(), "_NET_ACTIVE_WINDOW", True);
     208           0 :   if (atom != None) {
     209             :     XEvent xev;
     210           0 :     xev.xclient.type = ClientMessage;
     211           0 :     xev.xclient.serial = 0;
     212           0 :     xev.xclient.send_event = True;
     213           0 :     xev.xclient.window = selected_window_;
     214           0 :     xev.xclient.message_type = atom;
     215             : 
     216             :     // The format member is set to 8, 16, or 32 and specifies whether the
     217             :     // data should be viewed as a list of bytes, shorts, or longs.
     218           0 :     xev.xclient.format = 32;
     219             : 
     220           0 :     memset(xev.xclient.data.l, 0, sizeof(xev.xclient.data.l));
     221             : 
     222           0 :     XSendEvent(display(),
     223             :                root,
     224             :                False,
     225             :                SubstructureRedirectMask | SubstructureNotifyMask,
     226           0 :                &xev);
     227             :   }
     228           0 :   XFlush(display());
     229           0 :   return true;
     230             : }
     231             : 
     232           0 : void WindowCapturerLinux::Start(Callback* callback) {
     233           0 :   assert(!callback_);
     234           0 :   assert(callback);
     235             : 
     236           0 :   callback_ = callback;
     237           0 : }
     238             : 
     239           0 : void WindowCapturerLinux::Stop() {
     240           0 :   callback_ = NULL;
     241           0 : }
     242             : 
     243           0 : void WindowCapturerLinux::CaptureFrame() {
     244           0 :   x_display_->ProcessPendingXEvents();
     245             : 
     246           0 :   if (!x_server_pixel_buffer_.IsWindowValid()) {
     247           0 :     LOG(LS_INFO) << "The window is no longer valid.";
     248           0 :     callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
     249           0 :     return;
     250             :   }
     251             : 
     252           0 :   if (!has_composite_extension_) {
     253             :     // Without the Xcomposite extension we capture when the whole window is
     254             :     // visible on screen and not covered by any other window. This is not
     255             :     // something we want so instead, just bail out.
     256           0 :     LOG(LS_INFO) << "No Xcomposite extension detected.";
     257           0 :     callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
     258           0 :     return;
     259             :   }
     260             : 
     261             :   std::unique_ptr<DesktopFrame> frame(
     262           0 :       new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
     263             : 
     264           0 :   x_server_pixel_buffer_.Synchronize();
     265           0 :   if (!x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
     266             :                                           frame.get())) {
     267           0 :     callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
     268           0 :     return;
     269             :   }
     270             : 
     271           0 :   frame->mutable_updated_region()->SetRect(
     272           0 :       DesktopRect::MakeSize(frame->size()));
     273             : 
     274           0 :   callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
     275             : }
     276             : 
     277           0 : bool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
     278           0 :   if (event.type == ConfigureNotify) {
     279           0 :     XConfigureEvent xce = event.xconfigure;
     280           0 :     if (!DesktopSize(xce.width, xce.height).equals(
     281             :             x_server_pixel_buffer_.window_size())) {
     282           0 :       if (!x_server_pixel_buffer_.Init(display(), selected_window_)) {
     283           0 :         LOG(LS_ERROR) << "Failed to initialize pixel buffer after resizing.";
     284             :       }
     285           0 :       return true;
     286             :     }
     287             :   }
     288           0 :   return false;
     289             : }
     290             : 
     291           0 : ::Window WindowCapturerLinux::GetApplicationWindow(::Window window) {
     292             :   // Get WM_STATE property of the window.
     293           0 :   XWindowProperty<uint32_t> window_state(display(), window, wm_state_atom_);
     294             : 
     295             :   // WM_STATE is considered to be set to WithdrawnState when it missing.
     296           0 :   int32_t state = window_state.is_valid() ?
     297           0 :       *window_state.data() : WithdrawnState;
     298             : 
     299           0 :   if (state == NormalState) {
     300             :     // Window has WM_STATE==NormalState. Return it.
     301           0 :     return window;
     302           0 :   } else if (state == IconicState) {
     303             :     // Window is in minimized. Skip it.
     304           0 :     return 0;
     305             :   }
     306             : 
     307             :   // If the window is in WithdrawnState then look at all of its children.
     308             :   ::Window root, parent;
     309             :   ::Window *children;
     310             :   unsigned int num_children;
     311           0 :   if (!XQueryTree(display(), window, &root, &parent, &children,
     312             :                   &num_children)) {
     313           0 :     LOG(LS_ERROR) << "Failed to query for child windows although window"
     314           0 :                   << "does not have a valid WM_STATE.";
     315           0 :     return 0;
     316             :   }
     317           0 :   ::Window app_window = 0;
     318           0 :   for (unsigned int i = 0; i < num_children; ++i) {
     319           0 :     app_window = GetApplicationWindow(children[i]);
     320           0 :     if (app_window)
     321           0 :       break;
     322             :   }
     323             : 
     324           0 :   if (children)
     325           0 :     XFree(children);
     326           0 :   return app_window;
     327             : }
     328             : 
     329           0 : bool WindowCapturerLinux::IsDesktopElement(::Window window) {
     330           0 :   if (window == 0)
     331           0 :     return false;
     332             : 
     333             :   // First look for _NET_WM_WINDOW_TYPE. The standard
     334             :   // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
     335             :   // says this hint *should* be present on all windows, and we use the existence
     336             :   // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
     337             :   // a desktop element (that is, only "normal" windows should be shareable).
     338           0 :   XWindowProperty<uint32_t> window_type(display(), window, window_type_atom_);
     339           0 :   if (window_type.is_valid() && window_type.size() > 0) {
     340           0 :     uint32_t* end = window_type.data() + window_type.size();
     341           0 :     bool is_normal = (end != std::find(
     342           0 :         window_type.data(), end, normal_window_type_atom_));
     343           0 :     return !is_normal;
     344             :   }
     345             : 
     346             :   // Fall back on using the hint.
     347             :   XClassHint class_hint;
     348           0 :   Status status = XGetClassHint(display(), window, &class_hint);
     349           0 :   bool result = false;
     350           0 :   if (status == 0) {
     351             :     // No hints, assume this is a normal application window.
     352           0 :     return result;
     353             :   }
     354             : 
     355           0 :   if (strcmp("gnome-panel", class_hint.res_name) == 0 ||
     356           0 :       strcmp("desktop_window", class_hint.res_name) == 0) {
     357           0 :     result = true;
     358             :   }
     359           0 :   XFree(class_hint.res_name);
     360           0 :   XFree(class_hint.res_class);
     361           0 :   return result;
     362             : }
     363             : 
     364           0 : bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) {
     365             :   int status;
     366           0 :   bool result = false;
     367             :   XTextProperty window_name;
     368           0 :   window_name.value = nullptr;
     369           0 :   if (window) {
     370           0 :     status = XGetWMName(display(), window, &window_name);
     371           0 :     if (status && window_name.value && window_name.nitems) {
     372             :       int cnt;
     373           0 :       char** list = nullptr;
     374           0 :       status = Xutf8TextPropertyToTextList(display(), &window_name, &list,
     375           0 :                                            &cnt);
     376           0 :       if (status >= Success && cnt && *list) {
     377           0 :         if (cnt > 1) {
     378           0 :           LOG(LS_INFO) << "Window has " << cnt
     379           0 :                        << " text properties, only using the first one.";
     380             :         }
     381           0 :         *title = *list;
     382           0 :         result = true;
     383             :       }
     384           0 :       if (list)
     385           0 :         XFreeStringList(list);
     386             :     }
     387           0 :     if (window_name.value)
     388           0 :       XFree(window_name.value);
     389             :   }
     390           0 :   return result;
     391             : }
     392             : 
     393             : }  // namespace
     394             : 
     395           0 : int WindowCapturerLinux::GetWindowProcessID(::Window window) {
     396             :   // Get _NET_WM_PID property of the window.
     397           0 :   Atom process_atom = XInternAtom(display(), "_NET_WM_PID", True);
     398           0 :   XWindowProperty<uint32_t> process_id(display(), window, process_atom);
     399             : 
     400           0 :   return process_id.is_valid() ? *process_id.data() : 0;
     401             : }
     402             : 
     403             : // static
     404           0 : std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
     405             :     const DesktopCaptureOptions& options) {
     406           0 :   if (!options.x_display())
     407           0 :     return nullptr;
     408           0 :   return std::unique_ptr<DesktopCapturer>(new WindowCapturerLinux(options));
     409             : }
     410             : 
     411             : }  // namespace webrtc

Generated by: LCOV version 1.13