| /* | 
 |  *  Copyright (c) 2016 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 <X11/X.h> | 
 | #include <X11/Xlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "api/scoped_refptr.h" | 
 | #include "modules/desktop_capture/desktop_capture_types.h" | 
 | #include "modules/desktop_capture/desktop_geometry.h" | 
 | #include "modules/desktop_capture/linux/x11/shared_x_display.h" | 
 | #include "modules/desktop_capture/rgba_color.h" | 
 | #include "modules/desktop_capture/screen_drawer.h" | 
 | #include "modules/desktop_capture/screen_drawer_lock_posix.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "system_wrappers/include/sleep.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | namespace { | 
 |  | 
 | // A ScreenDrawer implementation for X11. | 
 | class ScreenDrawerLinux : public ScreenDrawer { | 
 |  public: | 
 |   ScreenDrawerLinux(); | 
 |   ~ScreenDrawerLinux() override; | 
 |  | 
 |   // ScreenDrawer interface. | 
 |   DesktopRect DrawableRegion() override; | 
 |   void DrawRectangle(DesktopRect rect, RgbaColor color) override; | 
 |   void Clear() override; | 
 |   void WaitForPendingDraws() override; | 
 |   bool MayDrawIncompleteShapes() override; | 
 |   WindowId window_id() const override; | 
 |  | 
 |  private: | 
 |   // Bring the window to the front, this can help to avoid the impact from other | 
 |   // windows or shadow effect. | 
 |   void BringToFront(); | 
 |  | 
 |   rtc::scoped_refptr<SharedXDisplay> display_; | 
 |   int screen_num_; | 
 |   DesktopRect rect_; | 
 |   Window window_; | 
 |   GC context_; | 
 |   Colormap colormap_; | 
 | }; | 
 |  | 
 | ScreenDrawerLinux::ScreenDrawerLinux() { | 
 |   display_ = SharedXDisplay::CreateDefault(); | 
 |   RTC_CHECK(display_.get()); | 
 |   screen_num_ = DefaultScreen(display_->display()); | 
 |   XWindowAttributes root_attributes; | 
 |   if (!XGetWindowAttributes(display_->display(), | 
 |                             RootWindow(display_->display(), screen_num_), | 
 |                             &root_attributes)) { | 
 |     RTC_DCHECK_NOTREACHED() << "Failed to get root window size."; | 
 |   } | 
 |   window_ = XCreateSimpleWindow( | 
 |       display_->display(), RootWindow(display_->display(), screen_num_), 0, 0, | 
 |       root_attributes.width, root_attributes.height, 0, | 
 |       BlackPixel(display_->display(), screen_num_), | 
 |       BlackPixel(display_->display(), screen_num_)); | 
 |   XSelectInput(display_->display(), window_, StructureNotifyMask); | 
 |   XMapWindow(display_->display(), window_); | 
 |   while (true) { | 
 |     XEvent event; | 
 |     XNextEvent(display_->display(), &event); | 
 |     if (event.type == MapNotify) { | 
 |       break; | 
 |     } | 
 |   } | 
 |   XFlush(display_->display()); | 
 |   Window child; | 
 |   int x, y; | 
 |   if (!XTranslateCoordinates(display_->display(), window_, | 
 |                              RootWindow(display_->display(), screen_num_), 0, 0, | 
 |                              &x, &y, &child)) { | 
 |     RTC_DCHECK_NOTREACHED() << "Failed to get window position."; | 
 |   } | 
 |   // Some window manager does not allow a window to cover two or more monitors. | 
 |   // So if the window is on the first monitor of a two-monitor system, the | 
 |   // second half won't be able to show up without changing configurations of WM, | 
 |   // and its DrawableRegion() is not accurate. | 
 |   rect_ = DesktopRect::MakeLTRB(x, y, root_attributes.width, | 
 |                                 root_attributes.height); | 
 |   context_ = DefaultGC(display_->display(), screen_num_); | 
 |   colormap_ = DefaultColormap(display_->display(), screen_num_); | 
 |   BringToFront(); | 
 |   // Wait for window animations. | 
 |   SleepMs(200); | 
 | } | 
 |  | 
 | ScreenDrawerLinux::~ScreenDrawerLinux() { | 
 |   XUnmapWindow(display_->display(), window_); | 
 |   XDestroyWindow(display_->display(), window_); | 
 | } | 
 |  | 
 | DesktopRect ScreenDrawerLinux::DrawableRegion() { | 
 |   return rect_; | 
 | } | 
 |  | 
 | void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) { | 
 |   rect.Translate(-rect_.left(), -rect_.top()); | 
 |   XColor xcolor; | 
 |   // X11 does not support Alpha. | 
 |   // X11 uses 16 bits for each primary color, so we need to slightly normalize | 
 |   // a 8 bits channel to 16 bits channel, by setting the low 8 bits as its high | 
 |   // 8 bits to avoid a mismatch of color returned by capturer. | 
 |   xcolor.red = (color.red << 8) + color.red; | 
 |   xcolor.green = (color.green << 8) + color.green; | 
 |   xcolor.blue = (color.blue << 8) + color.blue; | 
 |   xcolor.flags = DoRed | DoGreen | DoBlue; | 
 |   XAllocColor(display_->display(), colormap_, &xcolor); | 
 |   XSetForeground(display_->display(), context_, xcolor.pixel); | 
 |   XFillRectangle(display_->display(), window_, context_, rect.left(), | 
 |                  rect.top(), rect.width(), rect.height()); | 
 |   XFlush(display_->display()); | 
 | } | 
 |  | 
 | void ScreenDrawerLinux::Clear() { | 
 |   DrawRectangle(rect_, RgbaColor(0, 0, 0)); | 
 | } | 
 |  | 
 | // TODO(zijiehe): Find the right signal from X11 to indicate the finish of all | 
 | // pending paintings. | 
 | void ScreenDrawerLinux::WaitForPendingDraws() { | 
 |   SleepMs(50); | 
 | } | 
 |  | 
 | bool ScreenDrawerLinux::MayDrawIncompleteShapes() { | 
 |   return true; | 
 | } | 
 |  | 
 | WindowId ScreenDrawerLinux::window_id() const { | 
 |   return window_; | 
 | } | 
 |  | 
 | void ScreenDrawerLinux::BringToFront() { | 
 |   Atom state_above = XInternAtom(display_->display(), "_NET_WM_STATE_ABOVE", 1); | 
 |   Atom window_state = XInternAtom(display_->display(), "_NET_WM_STATE", 1); | 
 |   if (state_above == None || window_state == None) { | 
 |     // Fallback to use XRaiseWindow, it's not reliable if two windows are both | 
 |     // raise itself to the top. | 
 |     XRaiseWindow(display_->display(), window_); | 
 |     return; | 
 |   } | 
 |  | 
 |   XEvent event; | 
 |   memset(&event, 0, sizeof(event)); | 
 |   event.type = ClientMessage; | 
 |   event.xclient.window = window_; | 
 |   event.xclient.message_type = window_state; | 
 |   event.xclient.format = 32; | 
 |   event.xclient.data.l[0] = 1;  // _NET_WM_STATE_ADD | 
 |   event.xclient.data.l[1] = state_above; | 
 |   XSendEvent(display_->display(), RootWindow(display_->display(), screen_num_), | 
 |              False, SubstructureRedirectMask | SubstructureNotifyMask, &event); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() { | 
 |   return std::make_unique<ScreenDrawerLockPosix>(); | 
 | } | 
 |  | 
 | // static | 
 | std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() { | 
 |   if (SharedXDisplay::CreateDefault().get()) { | 
 |     return std::make_unique<ScreenDrawerLinux>(); | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | }  // namespace webrtc |