zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 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 | |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 11 | #include <fcntl.h> |
| 12 | #include <sys/stat.h> |
| 13 | #include <semaphore.h> |
| 14 | #include <string.h> |
zijiehe | e083909 | 2016-11-08 12:47:14 -0800 | [diff] [blame] | 15 | #include <X11/Xlib.h> |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 16 | |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 17 | #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" |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 22 | #include "webrtc/system_wrappers/include/sleep.h" |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 23 | |
| 24 | namespace webrtc { |
| 25 | |
| 26 | namespace { |
| 27 | |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 28 | static constexpr char kSemaphoreName[] = |
| 29 | "/global-screen-drawer-linux-54fe5552-8047-11e6-a725-3f429a5b4fb4"; |
| 30 | |
| 31 | class ScreenDrawerLockLinux : public ScreenDrawerLock { |
| 32 | public: |
| 33 | ScreenDrawerLockLinux(); |
zijiehe | 6be0a65 | 2016-10-27 16:50:35 -0700 | [diff] [blame] | 34 | ~ScreenDrawerLockLinux() override; |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 35 | |
| 36 | private: |
| 37 | sem_t* semaphore_; |
| 38 | }; |
| 39 | |
| 40 | ScreenDrawerLockLinux::ScreenDrawerLockLinux() { |
| 41 | semaphore_ = |
| 42 | sem_open(kSemaphoreName, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 1); |
| 43 | sem_wait(semaphore_); |
| 44 | } |
| 45 | |
| 46 | ScreenDrawerLockLinux::~ScreenDrawerLockLinux() { |
| 47 | sem_post(semaphore_); |
| 48 | sem_close(semaphore_); |
| 49 | // sem_unlink(kSemaphoreName); |
| 50 | } |
| 51 | |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 52 | // A ScreenDrawer implementation for X11. |
| 53 | class ScreenDrawerLinux : public ScreenDrawer { |
| 54 | public: |
| 55 | ScreenDrawerLinux(); |
| 56 | ~ScreenDrawerLinux() override; |
| 57 | |
| 58 | // ScreenDrawer interface. |
| 59 | DesktopRect DrawableRegion() override; |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 60 | void DrawRectangle(DesktopRect rect, RgbaColor color) override; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 61 | void Clear() override; |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 62 | void WaitForPendingDraws() override; |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 63 | bool MayDrawIncompleteShapes() override; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 64 | |
| 65 | private: |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 66 | // Bring the window to the front, this can help to avoid the impact from other |
| 67 | // windows or shadow effect. |
| 68 | void BringToFront(); |
| 69 | |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 70 | rtc::scoped_refptr<SharedXDisplay> display_; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 71 | int screen_num_; |
| 72 | DesktopRect rect_; |
| 73 | Window window_; |
| 74 | GC context_; |
| 75 | Colormap colormap_; |
| 76 | }; |
| 77 | |
| 78 | ScreenDrawerLinux::ScreenDrawerLinux() { |
| 79 | display_ = SharedXDisplay::CreateDefault(); |
| 80 | RTC_CHECK(display_.get()); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 81 | screen_num_ = DefaultScreen(display_->display()); |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 82 | 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_)); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 93 | 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()); |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 103 | 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); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 116 | context_ = DefaultGC(display_->display(), screen_num_); |
| 117 | colormap_ = DefaultColormap(display_->display(), screen_num_); |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 118 | BringToFront(); |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 119 | // Wait for window animations. |
| 120 | SleepMs(200); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | ScreenDrawerLinux::~ScreenDrawerLinux() { |
| 124 | XUnmapWindow(display_->display(), window_); |
| 125 | XDestroyWindow(display_->display(), window_); |
| 126 | } |
| 127 | |
| 128 | DesktopRect ScreenDrawerLinux::DrawableRegion() { |
| 129 | return rect_; |
| 130 | } |
| 131 | |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 132 | void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) { |
| 133 | rect.Translate(-rect_.left(), -rect_.top()); |
| 134 | XColor xcolor; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 135 | // X11 does not support Alpha. |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 136 | // 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); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 145 | XFillRectangle(display_->display(), window_, context_, rect.left(), |
| 146 | rect.top(), rect.width(), rect.height()); |
| 147 | XFlush(display_->display()); |
| 148 | } |
| 149 | |
| 150 | void ScreenDrawerLinux::Clear() { |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 151 | 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. |
| 156 | void ScreenDrawerLinux::WaitForPendingDraws() { |
| 157 | SleepMs(50); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 158 | } |
| 159 | |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 160 | bool ScreenDrawerLinux::MayDrawIncompleteShapes() { |
| 161 | return true; |
| 162 | } |
| 163 | |
| 164 | void 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 | |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 186 | } // namespace |
| 187 | |
| 188 | // static |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 189 | std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() { |
| 190 | return std::unique_ptr<ScreenDrawerLock>(new ScreenDrawerLockLinux()); |
| 191 | } |
| 192 | |
| 193 | // static |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 194 | std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() { |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 195 | if (SharedXDisplay::CreateDefault().get()) { |
| 196 | return std::unique_ptr<ScreenDrawer>(new ScreenDrawerLinux()); |
| 197 | } |
| 198 | return nullptr; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 199 | } |
| 200 | |
| 201 | } // namespace webrtc |