blob: c78e6843fe0607ec90d766515ecbcec5d46c62f6 [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 <fcntl.h>
12#include <sys/stat.h>
13#include <semaphore.h>
14#include <string.h>
zijiehee0839092016-11-08 12:47:14 -080015#include <X11/Xlib.h>
zijiehe6a4607e2016-10-18 18:22:18 -070016
zijiehe49c01d72016-08-16 17:33:55 -070017#include <memory>
18
19#include "webrtc/base/checks.h"
20#include "webrtc/modules/desktop_capture/screen_drawer.h"
21#include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
zijiehe0f49dac2016-09-07 11:52:25 -070022#include "webrtc/system_wrappers/include/sleep.h"
zijiehe49c01d72016-08-16 17:33:55 -070023
24namespace webrtc {
25
26namespace {
27
zijiehe6a4607e2016-10-18 18:22:18 -070028static constexpr char kSemaphoreName[] =
29 "/global-screen-drawer-linux-54fe5552-8047-11e6-a725-3f429a5b4fb4";
30
31class ScreenDrawerLockLinux : public ScreenDrawerLock {
32 public:
33 ScreenDrawerLockLinux();
zijiehe6be0a652016-10-27 16:50:35 -070034 ~ScreenDrawerLockLinux() override;
zijiehe6a4607e2016-10-18 18:22:18 -070035
36 private:
37 sem_t* semaphore_;
38};
39
40ScreenDrawerLockLinux::ScreenDrawerLockLinux() {
41 semaphore_ =
42 sem_open(kSemaphoreName, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 1);
43 sem_wait(semaphore_);
44}
45
46ScreenDrawerLockLinux::~ScreenDrawerLockLinux() {
47 sem_post(semaphore_);
48 sem_close(semaphore_);
49 // sem_unlink(kSemaphoreName);
50}
51
zijiehe49c01d72016-08-16 17:33:55 -070052// A ScreenDrawer implementation for X11.
53class ScreenDrawerLinux : public ScreenDrawer {
54 public:
55 ScreenDrawerLinux();
56 ~ScreenDrawerLinux() override;
57
58 // ScreenDrawer interface.
59 DesktopRect DrawableRegion() override;
zijiehe0f49dac2016-09-07 11:52:25 -070060 void DrawRectangle(DesktopRect rect, RgbaColor color) override;
zijiehe49c01d72016-08-16 17:33:55 -070061 void Clear() override;
zijiehe0f49dac2016-09-07 11:52:25 -070062 void WaitForPendingDraws() override;
zijiehe6a4607e2016-10-18 18:22:18 -070063 bool MayDrawIncompleteShapes() override;
zijiehe49c01d72016-08-16 17:33:55 -070064
65 private:
zijiehe6a4607e2016-10-18 18:22:18 -070066 // Bring the window to the front, this can help to avoid the impact from other
67 // windows or shadow effect.
68 void BringToFront();
69
zijiehe49c01d72016-08-16 17:33:55 -070070 rtc::scoped_refptr<SharedXDisplay> display_;
zijiehe49c01d72016-08-16 17:33:55 -070071 int screen_num_;
72 DesktopRect rect_;
73 Window window_;
74 GC context_;
75 Colormap colormap_;
76};
77
78ScreenDrawerLinux::ScreenDrawerLinux() {
79 display_ = SharedXDisplay::CreateDefault();
80 RTC_CHECK(display_.get());
zijiehe49c01d72016-08-16 17:33:55 -070081 screen_num_ = DefaultScreen(display_->display());
zijiehe0f49dac2016-09-07 11:52:25 -070082 XWindowAttributes root_attributes;
83 if (!XGetWindowAttributes(display_->display(),
84 RootWindow(display_->display(), screen_num_),
85 &root_attributes)) {
86 RTC_DCHECK(false) << "Failed to get root window size.";
87 }
88 window_ = XCreateSimpleWindow(
89 display_->display(), RootWindow(display_->display(), screen_num_), 0, 0,
90 root_attributes.width, root_attributes.height, 0,
91 BlackPixel(display_->display(), screen_num_),
92 BlackPixel(display_->display(), screen_num_));
zijiehe49c01d72016-08-16 17:33:55 -070093 XSelectInput(display_->display(), window_, StructureNotifyMask);
94 XMapWindow(display_->display(), window_);
95 while (true) {
96 XEvent event;
97 XNextEvent(display_->display(), &event);
98 if (event.type == MapNotify) {
99 break;
100 }
101 }
102 XFlush(display_->display());
zijiehe0f49dac2016-09-07 11:52:25 -0700103 Window child;
104 int x, y;
105 if (!XTranslateCoordinates(display_->display(), window_,
106 RootWindow(display_->display(), screen_num_), 0, 0,
107 &x, &y, &child)) {
108 RTC_DCHECK(false) << "Failed to get window position.";
109 }
110 // Some window manager does not allow a window to cover two or more monitors.
111 // So if the window is on the first monitor of a two-monitor system, the
112 // second half won't be able to show up without changing configurations of WM,
113 // and its DrawableRegion() is not accurate.
114 rect_ = DesktopRect::MakeLTRB(x, y, root_attributes.width,
115 root_attributes.height);
zijiehe49c01d72016-08-16 17:33:55 -0700116 context_ = DefaultGC(display_->display(), screen_num_);
117 colormap_ = DefaultColormap(display_->display(), screen_num_);
zijiehe6a4607e2016-10-18 18:22:18 -0700118 BringToFront();
zijiehe0f49dac2016-09-07 11:52:25 -0700119 // Wait for window animations.
120 SleepMs(200);
zijiehe49c01d72016-08-16 17:33:55 -0700121}
122
123ScreenDrawerLinux::~ScreenDrawerLinux() {
124 XUnmapWindow(display_->display(), window_);
125 XDestroyWindow(display_->display(), window_);
126}
127
128DesktopRect ScreenDrawerLinux::DrawableRegion() {
129 return rect_;
130}
131
zijiehe0f49dac2016-09-07 11:52:25 -0700132void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) {
133 rect.Translate(-rect_.left(), -rect_.top());
134 XColor xcolor;
zijiehe49c01d72016-08-16 17:33:55 -0700135 // X11 does not support Alpha.
zijiehe0f49dac2016-09-07 11:52:25 -0700136 // X11 uses 16 bits for each primary color, so we need to slightly normalize
137 // a 8 bits channel to 16 bits channel, by setting the low 8 bits as its high
138 // 8 bits to avoid a mismatch of color returned by capturer.
139 xcolor.red = (color.red << 8) + color.red;
140 xcolor.green = (color.green << 8) + color.green;
141 xcolor.blue = (color.blue << 8) + color.blue;
142 xcolor.flags = DoRed | DoGreen | DoBlue;
143 XAllocColor(display_->display(), colormap_, &xcolor);
144 XSetForeground(display_->display(), context_, xcolor.pixel);
zijiehe49c01d72016-08-16 17:33:55 -0700145 XFillRectangle(display_->display(), window_, context_, rect.left(),
146 rect.top(), rect.width(), rect.height());
147 XFlush(display_->display());
148}
149
150void ScreenDrawerLinux::Clear() {
zijiehe0f49dac2016-09-07 11:52:25 -0700151 DrawRectangle(rect_, RgbaColor(0, 0, 0));
152}
153
154// TODO(zijiehe): Find the right signal from X11 to indicate the finish of all
155// pending paintings.
156void ScreenDrawerLinux::WaitForPendingDraws() {
157 SleepMs(50);
zijiehe49c01d72016-08-16 17:33:55 -0700158}
159
zijiehe6a4607e2016-10-18 18:22:18 -0700160bool ScreenDrawerLinux::MayDrawIncompleteShapes() {
161 return true;
162}
163
164void ScreenDrawerLinux::BringToFront() {
165 Atom state_above = XInternAtom(display_->display(), "_NET_WM_STATE_ABOVE", 1);
166 Atom window_state = XInternAtom(display_->display(), "_NET_WM_STATE", 1);
167 if (state_above == None || window_state == None) {
168 // Fallback to use XRaiseWindow, it's not reliable if two windows are both
169 // raise itself to the top.
170 XRaiseWindow(display_->display(), window_);
171 return;
172 }
173
174 XEvent event;
175 memset(&event, 0, sizeof(event));
176 event.type = ClientMessage;
177 event.xclient.window = window_;
178 event.xclient.message_type = window_state;
179 event.xclient.format = 32;
180 event.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
181 event.xclient.data.l[1] = state_above;
182 XSendEvent(display_->display(), RootWindow(display_->display(), screen_num_),
183 False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
184}
185
zijiehe49c01d72016-08-16 17:33:55 -0700186} // namespace
187
188// static
zijiehe6a4607e2016-10-18 18:22:18 -0700189std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
190 return std::unique_ptr<ScreenDrawerLock>(new ScreenDrawerLockLinux());
191}
192
193// static
zijiehe49c01d72016-08-16 17:33:55 -0700194std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
zijiehe0f49dac2016-09-07 11:52:25 -0700195 if (SharedXDisplay::CreateDefault().get()) {
196 return std::unique_ptr<ScreenDrawer>(new ScreenDrawerLinux());
197 }
198 return nullptr;
zijiehe49c01d72016-08-16 17:33:55 -0700199}
200
201} // namespace webrtc