tor-browser

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

screen_drawer_linux.cc (6257B)


      1 /*
      2 *  Copyright (c) 2016 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 <X11/X.h>
     12 #include <X11/Xlib.h>
     13 
     14 #include <cstring>
     15 #include <memory>
     16 
     17 #include "api/scoped_refptr.h"
     18 #include "modules/desktop_capture/desktop_capture_types.h"
     19 #include "modules/desktop_capture/desktop_geometry.h"
     20 #include "modules/desktop_capture/linux/x11/shared_x_display.h"
     21 #include "modules/desktop_capture/rgba_color.h"
     22 #include "modules/desktop_capture/screen_drawer.h"
     23 #include "modules/desktop_capture/screen_drawer_lock_posix.h"
     24 #include "rtc_base/checks.h"
     25 #include "rtc_base/thread.h"
     26 
     27 namespace webrtc {
     28 
     29 namespace {
     30 
     31 // A ScreenDrawer implementation for X11.
     32 class ScreenDrawerLinux : public ScreenDrawer {
     33 public:
     34  ScreenDrawerLinux();
     35  ~ScreenDrawerLinux() override;
     36 
     37  // ScreenDrawer interface.
     38  DesktopRect DrawableRegion() override;
     39  void DrawRectangle(DesktopRect rect, RgbaColor color) override;
     40  void Clear() override;
     41  void WaitForPendingDraws() override;
     42  bool MayDrawIncompleteShapes() override;
     43  WindowId window_id() const override;
     44 
     45 private:
     46  // Bring the window to the front, this can help to avoid the impact from other
     47  // windows or shadow effect.
     48  void BringToFront();
     49 
     50  scoped_refptr<SharedXDisplay> display_;
     51  int screen_num_;
     52  DesktopRect rect_;
     53  Window window_;
     54  GC context_;
     55  Colormap colormap_;
     56 };
     57 
     58 ScreenDrawerLinux::ScreenDrawerLinux() {
     59  display_ = SharedXDisplay::CreateDefault();
     60  RTC_CHECK(display_.get());
     61  screen_num_ = DefaultScreen(display_->display());
     62  XWindowAttributes root_attributes;
     63  if (!XGetWindowAttributes(display_->display(),
     64                            RootWindow(display_->display(), screen_num_),
     65                            &root_attributes)) {
     66    RTC_DCHECK_NOTREACHED() << "Failed to get root window size.";
     67  }
     68  window_ = XCreateSimpleWindow(
     69      display_->display(), RootWindow(display_->display(), screen_num_), 0, 0,
     70      root_attributes.width, root_attributes.height, 0,
     71      BlackPixel(display_->display(), screen_num_),
     72      BlackPixel(display_->display(), screen_num_));
     73  XSelectInput(display_->display(), window_, StructureNotifyMask);
     74  XMapWindow(display_->display(), window_);
     75  while (true) {
     76    XEvent event;
     77    XNextEvent(display_->display(), &event);
     78    if (event.type == MapNotify) {
     79      break;
     80    }
     81  }
     82  XFlush(display_->display());
     83  Window child;
     84  int x, y;
     85  if (!XTranslateCoordinates(display_->display(), window_,
     86                             RootWindow(display_->display(), screen_num_), 0, 0,
     87                             &x, &y, &child)) {
     88    RTC_DCHECK_NOTREACHED() << "Failed to get window position.";
     89  }
     90  // Some window manager does not allow a window to cover two or more monitors.
     91  // So if the window is on the first monitor of a two-monitor system, the
     92  // second half won't be able to show up without changing configurations of WM,
     93  // and its DrawableRegion() is not accurate.
     94  rect_ = DesktopRect::MakeLTRB(x, y, root_attributes.width,
     95                                root_attributes.height);
     96  context_ = DefaultGC(display_->display(), screen_num_);
     97  colormap_ = DefaultColormap(display_->display(), screen_num_);
     98  BringToFront();
     99  // Wait for window animations.
    100  Thread::SleepMs(200);
    101 }
    102 
    103 ScreenDrawerLinux::~ScreenDrawerLinux() {
    104  XUnmapWindow(display_->display(), window_);
    105  XDestroyWindow(display_->display(), window_);
    106 }
    107 
    108 DesktopRect ScreenDrawerLinux::DrawableRegion() {
    109  return rect_;
    110 }
    111 
    112 void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) {
    113  rect.Translate(-rect_.left(), -rect_.top());
    114  XColor xcolor;
    115  // X11 does not support Alpha.
    116  // X11 uses 16 bits for each primary color, so we need to slightly normalize
    117  // a 8 bits channel to 16 bits channel, by setting the low 8 bits as its high
    118  // 8 bits to avoid a mismatch of color returned by capturer.
    119  xcolor.red = (color.red << 8) + color.red;
    120  xcolor.green = (color.green << 8) + color.green;
    121  xcolor.blue = (color.blue << 8) + color.blue;
    122  xcolor.flags = DoRed | DoGreen | DoBlue;
    123  XAllocColor(display_->display(), colormap_, &xcolor);
    124  XSetForeground(display_->display(), context_, xcolor.pixel);
    125  XFillRectangle(display_->display(), window_, context_, rect.left(),
    126                 rect.top(), rect.width(), rect.height());
    127  XFlush(display_->display());
    128 }
    129 
    130 void ScreenDrawerLinux::Clear() {
    131  DrawRectangle(rect_, RgbaColor(0, 0, 0));
    132 }
    133 
    134 // TODO(zijiehe): Find the right signal from X11 to indicate the finish of all
    135 // pending paintings.
    136 void ScreenDrawerLinux::WaitForPendingDraws() {
    137  Thread::SleepMs(50);
    138 }
    139 
    140 bool ScreenDrawerLinux::MayDrawIncompleteShapes() {
    141  return true;
    142 }
    143 
    144 WindowId ScreenDrawerLinux::window_id() const {
    145  return window_;
    146 }
    147 
    148 void ScreenDrawerLinux::BringToFront() {
    149  Atom state_above = XInternAtom(display_->display(), "_NET_WM_STATE_ABOVE", 1);
    150  Atom window_state = XInternAtom(display_->display(), "_NET_WM_STATE", 1);
    151  if (state_above == None || window_state == None) {
    152    // Fallback to use XRaiseWindow, it's not reliable if two windows are both
    153    // raise itself to the top.
    154    XRaiseWindow(display_->display(), window_);
    155    return;
    156  }
    157 
    158  XEvent event;
    159  memset(&event, 0, sizeof(event));
    160  event.type = ClientMessage;
    161  event.xclient.window = window_;
    162  event.xclient.message_type = window_state;
    163  event.xclient.format = 32;
    164  event.xclient.data.l[0] = 1;  // _NET_WM_STATE_ADD
    165  event.xclient.data.l[1] = state_above;
    166  XSendEvent(display_->display(), RootWindow(display_->display(), screen_num_),
    167             False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
    168 }
    169 
    170 }  // namespace
    171 
    172 // static
    173 std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
    174  return std::make_unique<ScreenDrawerLockPosix>();
    175 }
    176 
    177 // static
    178 std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
    179  if (SharedXDisplay::CreateDefault()) {
    180    return std::make_unique<ScreenDrawerLinux>();
    181  }
    182  return nullptr;
    183 }
    184 
    185 }  // namespace webrtc