tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

window_capturer_x11.cc (8949B)


      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 "modules/desktop_capture/linux/x11/window_capturer_x11.h"
     12 
     13 #include <X11/X.h>
     14 #include <X11/Xlib.h>
     15 #include <X11/Xutil.h>
     16 #include <X11/extensions/Xcomposite.h>
     17 #include <X11/extensions/composite.h>
     18 
     19 #include <cstring>
     20 #include <memory>
     21 #include <string>
     22 #include <utility>
     23 
     24 #include "api/scoped_refptr.h"
     25 #include "modules/desktop_capture/desktop_capture_options.h"
     26 #include "modules/desktop_capture/desktop_capture_types.h"
     27 #include "modules/desktop_capture/desktop_capturer.h"
     28 #include "modules/desktop_capture/desktop_frame.h"
     29 #include "modules/desktop_capture/desktop_geometry.h"
     30 #include "modules/desktop_capture/desktop_region.h"
     31 #include "modules/desktop_capture/linux/x11/shared_x_display.h"
     32 #include "modules/desktop_capture/linux/x11/window_finder_x11.h"
     33 #include "modules/desktop_capture/linux/x11/window_list_utils.h"
     34 #include "rtc_base/checks.h"
     35 #include "rtc_base/logging.h"
     36 #include "rtc_base/trace_event.h"
     37 
     38 namespace webrtc {
     39 
     40 WindowCapturerX11::WindowCapturerX11(const DesktopCaptureOptions& options)
     41    : x_display_(options.x_display()),
     42      atom_cache_(display()),
     43      window_finder_(&atom_cache_) {
     44  int event_base, error_base, major_version, minor_version;
     45  if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
     46      XCompositeQueryVersion(display(), &major_version, &minor_version) &&
     47      // XCompositeNameWindowPixmap() requires version 0.2
     48      (major_version > 0 || minor_version >= 2)) {
     49    has_composite_extension_ = true;
     50  } else {
     51    RTC_LOG(LS_INFO) << "Xcomposite extension not available or too old.";
     52  }
     53 
     54  x_display_->AddEventHandler(ConfigureNotify, this);
     55 }
     56 
     57 WindowCapturerX11::~WindowCapturerX11() {
     58  x_display_->RemoveEventHandler(ConfigureNotify, this);
     59 }
     60 
     61 bool WindowCapturerX11::GetSourceList(SourceList* sources) {
     62  return GetWindowList(&atom_cache_, [this, sources](::Window window) {
     63    Source w;
     64    w.id = window;
     65    w.pid = (pid_t)GetWindowProcessID(window);
     66    if (this->GetWindowTitle(window, &w.title)) {
     67      sources->push_back(w);
     68    }
     69    return true;
     70  });
     71 }
     72 
     73 bool WindowCapturerX11::SelectSource(SourceId id) {
     74  if (!x_server_pixel_buffer_.Init(&atom_cache_, id))
     75    return false;
     76 
     77  // Tell the X server to send us window resizing events.
     78  XSelectInput(display(), id, StructureNotifyMask);
     79 
     80  selected_window_ = id;
     81 
     82  // In addition to needing X11 server-side support for Xcomposite, it actually
     83  // needs to be turned on for the window. If the user has modern
     84  // hardware/drivers but isn't using a compositing window manager, that won't
     85  // be the case. Here we automatically turn it on.
     86 
     87  // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
     88  // remembers who has requested this and will turn it off for us when we exit.
     89  XCompositeRedirectWindow(display(), id, CompositeRedirectAutomatic);
     90 
     91  return true;
     92 }
     93 
     94 bool WindowCapturerX11::FocusOnSelectedSource() {
     95  if (!selected_window_)
     96    return false;
     97 
     98  unsigned int num_children;
     99  ::Window* children;
    100  ::Window parent;
    101  ::Window root;
    102  // Find the root window to pass event to.
    103  int status = XQueryTree(display(), selected_window_, &root, &parent,
    104                          &children, &num_children);
    105  if (status == 0) {
    106    RTC_LOG(LS_ERROR) << "Failed to query for the root window.";
    107    return false;
    108  }
    109 
    110  if (children)
    111    XFree(children);
    112 
    113  XRaiseWindow(display(), selected_window_);
    114 
    115  // Some window managers (e.g., metacity in GNOME) consider it illegal to
    116  // raise a window without also giving it input focus with
    117  // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
    118  Atom atom = XInternAtom(display(), "_NET_ACTIVE_WINDOW", True);
    119  if (atom != None) {
    120    XEvent xev;
    121    xev.xclient.type = ClientMessage;
    122    xev.xclient.serial = 0;
    123    xev.xclient.send_event = True;
    124    xev.xclient.window = selected_window_;
    125    xev.xclient.message_type = atom;
    126 
    127    // The format member is set to 8, 16, or 32 and specifies whether the
    128    // data should be viewed as a list of bytes, shorts, or longs.
    129    xev.xclient.format = 32;
    130 
    131    memset(xev.xclient.data.l, 0, sizeof(xev.xclient.data.l));
    132 
    133    XSendEvent(display(), root, False,
    134               SubstructureRedirectMask | SubstructureNotifyMask, &xev);
    135  }
    136  XFlush(display());
    137  return true;
    138 }
    139 
    140 void WindowCapturerX11::Start(Callback* callback) {
    141  RTC_DCHECK(!callback_);
    142  RTC_DCHECK(callback);
    143 
    144  callback_ = callback;
    145 }
    146 
    147 void WindowCapturerX11::CaptureFrame() {
    148  TRACE_EVENT0("webrtc", "WindowCapturerX11::CaptureFrame");
    149 
    150  if (!x_server_pixel_buffer_.IsWindowValid()) {
    151    RTC_LOG(LS_ERROR) << "The window is no longer valid.";
    152    callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
    153    return;
    154  }
    155 
    156  x_display_->ProcessPendingXEvents();
    157 
    158  if (!has_composite_extension_) {
    159    // Without the Xcomposite extension we capture when the whole window is
    160    // visible on screen and not covered by any other window. This is not
    161    // something we want so instead, just bail out.
    162    RTC_LOG(LS_ERROR) << "No Xcomposite extension detected.";
    163    callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
    164    return;
    165  }
    166 
    167  if (GetWindowState(&atom_cache_, selected_window_) == IconicState) {
    168    // Window is in minimized. Return a 1x1 frame as same as OSX/Win does.
    169    std::unique_ptr<DesktopFrame> frame(
    170        new BasicDesktopFrame(DesktopSize(1, 1), FOURCC_ARGB));
    171    callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
    172    return;
    173  }
    174 
    175  std::unique_ptr<DesktopFrame> frame(
    176      new BasicDesktopFrame(x_server_pixel_buffer_.window_size(), FOURCC_ARGB));
    177 
    178  x_server_pixel_buffer_.Synchronize();
    179  if (!x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
    180                                          frame.get())) {
    181    RTC_LOG(LS_WARNING) << "Temporarily failed to capture winodw.";
    182    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    183    return;
    184  }
    185 
    186  frame->mutable_updated_region()->SetRect(
    187      DesktopRect::MakeSize(frame->size()));
    188  frame->set_top_left(x_server_pixel_buffer_.window_rect().top_left());
    189  frame->set_capturer_id(DesktopCapturerId::kX11CapturerLinux);
    190 
    191  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
    192 }
    193 
    194 bool WindowCapturerX11::IsOccluded(const DesktopVector& pos) {
    195  return window_finder_.GetWindowUnderPoint(pos) !=
    196         static_cast<WindowId>(selected_window_);
    197 }
    198 
    199 bool WindowCapturerX11::HandleXEvent(const XEvent& event) {
    200  if (event.type == ConfigureNotify) {
    201    XConfigureEvent xce = event.xconfigure;
    202    if (xce.window == selected_window_) {
    203      if (!DesktopRectFromXAttributes(xce).equals(
    204              x_server_pixel_buffer_.window_rect())) {
    205        if (!x_server_pixel_buffer_.Init(&atom_cache_, selected_window_)) {
    206          RTC_LOG(LS_ERROR)
    207              << "Failed to initialize pixel buffer after resizing.";
    208        }
    209      }
    210    }
    211  }
    212 
    213  // Always returns false, so other observers can still receive the events.
    214  return false;
    215 }
    216 
    217 bool WindowCapturerX11::GetWindowTitle(::Window window, std::string* title) {
    218  int status;
    219  bool result = false;
    220  XTextProperty window_name;
    221  window_name.value = nullptr;
    222  if (window) {
    223    status = XGetWMName(display(), window, &window_name);
    224    if (status && window_name.value && window_name.nitems) {
    225      int cnt;
    226      char** list = nullptr;
    227      status =
    228          Xutf8TextPropertyToTextList(display(), &window_name, &list, &cnt);
    229      if (status >= Success && cnt && *list) {
    230        if (cnt > 1) {
    231          RTC_LOG(LS_INFO) << "Window has " << cnt
    232                           << " text properties, only using the first one.";
    233        }
    234        *title = *list;
    235        result = true;
    236      }
    237      if (list)
    238        XFreeStringList(list);
    239    }
    240    if (window_name.value)
    241      XFree(window_name.value);
    242  }
    243  return result;
    244 }
    245 
    246 int WindowCapturerX11::GetWindowProcessID(::Window window) {
    247  // Get _NET_WM_PID property of the window.
    248  Atom process_atom = XInternAtom(display(), "_NET_WM_PID", True);
    249  XWindowProperty<uint32_t> process_id(display(), window, process_atom);
    250 
    251  return process_id.is_valid() ? *process_id.data() : 0;
    252 }
    253 
    254 // static
    255 std::unique_ptr<DesktopCapturer> WindowCapturerX11::CreateRawWindowCapturer(
    256    const DesktopCaptureOptions& options) {
    257  if (!options.x_display())
    258    return nullptr;
    259 
    260  RTC_LOG(LS_INFO)
    261      << "video capture: WindowCapturerX11::CreateRawWindowCapturer creates "
    262         "DesktopCapturer of type WindowCapturerX11";
    263  return std::unique_ptr<DesktopCapturer>(new WindowCapturerX11(options));
    264 }
    265 
    266 }  // namespace webrtc