blob: cbd8ba8612b77990adce0acf5af5949f7c48d9c4 [file] [log] [blame]
zijiehe49c01d72016-08-16 17:33:55 -07001/*
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
zijiehe6a4607e2016-10-18 18:22:18 -070011#include <string.h>
zijiehee0839092016-11-08 12:47:14 -080012#include <X11/Xlib.h>
zijiehe6a4607e2016-10-18 18:22:18 -070013
zijiehe49c01d72016-08-16 17:33:55 -070014#include <memory>
15
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "modules/desktop_capture/screen_drawer.h"
17#include "modules/desktop_capture/screen_drawer_lock_posix.h"
18#include "modules/desktop_capture/x11/shared_x_display.h"
19#include "rtc_base/checks.h"
20#include "rtc_base/ptr_util.h"
21#include "system_wrappers/include/sleep.h"
zijiehe49c01d72016-08-16 17:33:55 -070022
23namespace webrtc {
24
25namespace {
26
27// A ScreenDrawer implementation for X11.
28class ScreenDrawerLinux : public ScreenDrawer {
29 public:
30 ScreenDrawerLinux();
31 ~ScreenDrawerLinux() override;
32
33 // ScreenDrawer interface.
34 DesktopRect DrawableRegion() override;
zijiehe0f49dac2016-09-07 11:52:25 -070035 void DrawRectangle(DesktopRect rect, RgbaColor color) override;
zijiehe49c01d72016-08-16 17:33:55 -070036 void Clear() override;
zijiehe0f49dac2016-09-07 11:52:25 -070037 void WaitForPendingDraws() override;
zijiehe6a4607e2016-10-18 18:22:18 -070038 bool MayDrawIncompleteShapes() override;
Zijie He77b7a1d2017-09-01 15:51:14 -070039 WindowId window_id() const override;
zijiehe49c01d72016-08-16 17:33:55 -070040
41 private:
zijiehe6a4607e2016-10-18 18:22:18 -070042 // Bring the window to the front, this can help to avoid the impact from other
43 // windows or shadow effect.
44 void BringToFront();
45
zijiehe49c01d72016-08-16 17:33:55 -070046 rtc::scoped_refptr<SharedXDisplay> display_;
zijiehe49c01d72016-08-16 17:33:55 -070047 int screen_num_;
48 DesktopRect rect_;
49 Window window_;
50 GC context_;
51 Colormap colormap_;
52};
53
54ScreenDrawerLinux::ScreenDrawerLinux() {
55 display_ = SharedXDisplay::CreateDefault();
56 RTC_CHECK(display_.get());
zijiehe49c01d72016-08-16 17:33:55 -070057 screen_num_ = DefaultScreen(display_->display());
zijiehe0f49dac2016-09-07 11:52:25 -070058 XWindowAttributes root_attributes;
59 if (!XGetWindowAttributes(display_->display(),
60 RootWindow(display_->display(), screen_num_),
61 &root_attributes)) {
nisseeb4ca4e2017-01-12 02:24:27 -080062 RTC_NOTREACHED() << "Failed to get root window size.";
zijiehe0f49dac2016-09-07 11:52:25 -070063 }
64 window_ = XCreateSimpleWindow(
65 display_->display(), RootWindow(display_->display(), screen_num_), 0, 0,
66 root_attributes.width, root_attributes.height, 0,
67 BlackPixel(display_->display(), screen_num_),
68 BlackPixel(display_->display(), screen_num_));
zijiehe49c01d72016-08-16 17:33:55 -070069 XSelectInput(display_->display(), window_, StructureNotifyMask);
70 XMapWindow(display_->display(), window_);
71 while (true) {
72 XEvent event;
73 XNextEvent(display_->display(), &event);
74 if (event.type == MapNotify) {
75 break;
76 }
77 }
78 XFlush(display_->display());
zijiehe0f49dac2016-09-07 11:52:25 -070079 Window child;
80 int x, y;
81 if (!XTranslateCoordinates(display_->display(), window_,
82 RootWindow(display_->display(), screen_num_), 0, 0,
83 &x, &y, &child)) {
nisseeb4ca4e2017-01-12 02:24:27 -080084 RTC_NOTREACHED() << "Failed to get window position.";
zijiehe0f49dac2016-09-07 11:52:25 -070085 }
86 // Some window manager does not allow a window to cover two or more monitors.
87 // So if the window is on the first monitor of a two-monitor system, the
88 // second half won't be able to show up without changing configurations of WM,
89 // and its DrawableRegion() is not accurate.
90 rect_ = DesktopRect::MakeLTRB(x, y, root_attributes.width,
91 root_attributes.height);
zijiehe49c01d72016-08-16 17:33:55 -070092 context_ = DefaultGC(display_->display(), screen_num_);
93 colormap_ = DefaultColormap(display_->display(), screen_num_);
zijiehe6a4607e2016-10-18 18:22:18 -070094 BringToFront();
zijiehe0f49dac2016-09-07 11:52:25 -070095 // Wait for window animations.
96 SleepMs(200);
zijiehe49c01d72016-08-16 17:33:55 -070097}
98
99ScreenDrawerLinux::~ScreenDrawerLinux() {
100 XUnmapWindow(display_->display(), window_);
101 XDestroyWindow(display_->display(), window_);
102}
103
104DesktopRect ScreenDrawerLinux::DrawableRegion() {
105 return rect_;
106}
107
zijiehe0f49dac2016-09-07 11:52:25 -0700108void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) {
109 rect.Translate(-rect_.left(), -rect_.top());
110 XColor xcolor;
zijiehe49c01d72016-08-16 17:33:55 -0700111 // X11 does not support Alpha.
zijiehe0f49dac2016-09-07 11:52:25 -0700112 // X11 uses 16 bits for each primary color, so we need to slightly normalize
113 // a 8 bits channel to 16 bits channel, by setting the low 8 bits as its high
114 // 8 bits to avoid a mismatch of color returned by capturer.
115 xcolor.red = (color.red << 8) + color.red;
116 xcolor.green = (color.green << 8) + color.green;
117 xcolor.blue = (color.blue << 8) + color.blue;
118 xcolor.flags = DoRed | DoGreen | DoBlue;
119 XAllocColor(display_->display(), colormap_, &xcolor);
120 XSetForeground(display_->display(), context_, xcolor.pixel);
zijiehe49c01d72016-08-16 17:33:55 -0700121 XFillRectangle(display_->display(), window_, context_, rect.left(),
122 rect.top(), rect.width(), rect.height());
123 XFlush(display_->display());
124}
125
126void ScreenDrawerLinux::Clear() {
zijiehe0f49dac2016-09-07 11:52:25 -0700127 DrawRectangle(rect_, RgbaColor(0, 0, 0));
128}
129
130// TODO(zijiehe): Find the right signal from X11 to indicate the finish of all
131// pending paintings.
132void ScreenDrawerLinux::WaitForPendingDraws() {
133 SleepMs(50);
zijiehe49c01d72016-08-16 17:33:55 -0700134}
135
zijiehe6a4607e2016-10-18 18:22:18 -0700136bool ScreenDrawerLinux::MayDrawIncompleteShapes() {
137 return true;
138}
139
Zijie He77b7a1d2017-09-01 15:51:14 -0700140WindowId ScreenDrawerLinux::window_id() const {
141 return window_;
142}
143
zijiehe6a4607e2016-10-18 18:22:18 -0700144void ScreenDrawerLinux::BringToFront() {
145 Atom state_above = XInternAtom(display_->display(), "_NET_WM_STATE_ABOVE", 1);
146 Atom window_state = XInternAtom(display_->display(), "_NET_WM_STATE", 1);
147 if (state_above == None || window_state == None) {
148 // Fallback to use XRaiseWindow, it's not reliable if two windows are both
149 // raise itself to the top.
150 XRaiseWindow(display_->display(), window_);
151 return;
152 }
153
154 XEvent event;
155 memset(&event, 0, sizeof(event));
156 event.type = ClientMessage;
157 event.xclient.window = window_;
158 event.xclient.message_type = window_state;
159 event.xclient.format = 32;
160 event.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
161 event.xclient.data.l[1] = state_above;
162 XSendEvent(display_->display(), RootWindow(display_->display(), screen_num_),
163 False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
164}
165
zijiehe49c01d72016-08-16 17:33:55 -0700166} // namespace
167
168// static
zijiehe6a4607e2016-10-18 18:22:18 -0700169std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
Zijie He825f65e2017-08-16 14:56:42 -0700170 return rtc::MakeUnique<ScreenDrawerLockPosix>();
zijiehe6a4607e2016-10-18 18:22:18 -0700171}
172
173// static
zijiehe49c01d72016-08-16 17:33:55 -0700174std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
zijiehe0f49dac2016-09-07 11:52:25 -0700175 if (SharedXDisplay::CreateDefault().get()) {
Zijie He825f65e2017-08-16 14:56:42 -0700176 return rtc::MakeUnique<ScreenDrawerLinux>();
zijiehe0f49dac2016-09-07 11:52:25 -0700177 }
178 return nullptr;
zijiehe49c01d72016-08-16 17:33:55 -0700179}
180
181} // namespace webrtc