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 #include <string.h>
14 
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/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 "system_wrappers/include/sleep.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   rtc::scoped_refptr<SharedXDisplay> display_;
51   int screen_num_;
52   DesktopRect rect_;
53   Window window_;
54   GC context_;
55   Colormap colormap_;
56 };
57 
ScreenDrawerLinux()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_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_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   SleepMs(200);
101 }
102 
~ScreenDrawerLinux()103 ScreenDrawerLinux::~ScreenDrawerLinux() {
104   XUnmapWindow(display_->display(), window_);
105   XDestroyWindow(display_->display(), window_);
106 }
107 
DrawableRegion()108 DesktopRect ScreenDrawerLinux::DrawableRegion() {
109   return rect_;
110 }
111 
DrawRectangle(DesktopRect rect,RgbaColor color)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 
Clear()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.
WaitForPendingDraws()136 void ScreenDrawerLinux::WaitForPendingDraws() {
137   SleepMs(50);
138 }
139 
MayDrawIncompleteShapes()140 bool ScreenDrawerLinux::MayDrawIncompleteShapes() {
141   return true;
142 }
143 
window_id() const144 WindowId ScreenDrawerLinux::window_id() const {
145   return window_;
146 }
147 
BringToFront()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
Create()173 std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
174   return std::make_unique<ScreenDrawerLockPosix>();
175 }
176 
177 // static
Create()178 std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
179   if (SharedXDisplay::CreateDefault().get()) {
180     return std::make_unique<ScreenDrawerLinux>();
181   }
182   return nullptr;
183 }
184 
185 }  // namespace webrtc
186