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 <string.h> |
zijiehe | e083909 | 2016-11-08 12:47:14 -0800 | [diff] [blame] | 12 | #include <X11/Xlib.h> |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 13 | |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 14 | #include <memory> |
| 15 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 16 | #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" |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 22 | |
| 23 | namespace webrtc { |
| 24 | |
| 25 | namespace { |
| 26 | |
| 27 | // A ScreenDrawer implementation for X11. |
| 28 | class ScreenDrawerLinux : public ScreenDrawer { |
| 29 | public: |
| 30 | ScreenDrawerLinux(); |
| 31 | ~ScreenDrawerLinux() override; |
| 32 | |
| 33 | // ScreenDrawer interface. |
| 34 | DesktopRect DrawableRegion() override; |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 35 | void DrawRectangle(DesktopRect rect, RgbaColor color) override; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 36 | void Clear() override; |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 37 | void WaitForPendingDraws() override; |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 38 | bool MayDrawIncompleteShapes() override; |
Zijie He | 77b7a1d | 2017-09-01 15:51:14 -0700 | [diff] [blame] | 39 | WindowId window_id() const override; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 40 | |
| 41 | private: |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 42 | // Bring the window to the front, this can help to avoid the impact from other |
| 43 | // windows or shadow effect. |
| 44 | void BringToFront(); |
| 45 | |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 46 | rtc::scoped_refptr<SharedXDisplay> display_; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 47 | int screen_num_; |
| 48 | DesktopRect rect_; |
| 49 | Window window_; |
| 50 | GC context_; |
| 51 | Colormap colormap_; |
| 52 | }; |
| 53 | |
| 54 | ScreenDrawerLinux::ScreenDrawerLinux() { |
| 55 | display_ = SharedXDisplay::CreateDefault(); |
| 56 | RTC_CHECK(display_.get()); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 57 | screen_num_ = DefaultScreen(display_->display()); |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 58 | XWindowAttributes root_attributes; |
| 59 | if (!XGetWindowAttributes(display_->display(), |
| 60 | RootWindow(display_->display(), screen_num_), |
| 61 | &root_attributes)) { |
nisse | eb4ca4e | 2017-01-12 02:24:27 -0800 | [diff] [blame] | 62 | RTC_NOTREACHED() << "Failed to get root window size."; |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 63 | } |
| 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_)); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 69 | 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()); |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 79 | Window child; |
| 80 | int x, y; |
| 81 | if (!XTranslateCoordinates(display_->display(), window_, |
| 82 | RootWindow(display_->display(), screen_num_), 0, 0, |
| 83 | &x, &y, &child)) { |
nisse | eb4ca4e | 2017-01-12 02:24:27 -0800 | [diff] [blame] | 84 | RTC_NOTREACHED() << "Failed to get window position."; |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 85 | } |
| 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); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 92 | context_ = DefaultGC(display_->display(), screen_num_); |
| 93 | colormap_ = DefaultColormap(display_->display(), screen_num_); |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 94 | BringToFront(); |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 95 | // Wait for window animations. |
| 96 | SleepMs(200); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | ScreenDrawerLinux::~ScreenDrawerLinux() { |
| 100 | XUnmapWindow(display_->display(), window_); |
| 101 | XDestroyWindow(display_->display(), window_); |
| 102 | } |
| 103 | |
| 104 | DesktopRect ScreenDrawerLinux::DrawableRegion() { |
| 105 | return rect_; |
| 106 | } |
| 107 | |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 108 | void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) { |
| 109 | rect.Translate(-rect_.left(), -rect_.top()); |
| 110 | XColor xcolor; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 111 | // X11 does not support Alpha. |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 112 | // 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); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 121 | XFillRectangle(display_->display(), window_, context_, rect.left(), |
| 122 | rect.top(), rect.width(), rect.height()); |
| 123 | XFlush(display_->display()); |
| 124 | } |
| 125 | |
| 126 | void ScreenDrawerLinux::Clear() { |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 127 | 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. |
| 132 | void ScreenDrawerLinux::WaitForPendingDraws() { |
| 133 | SleepMs(50); |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 134 | } |
| 135 | |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 136 | bool ScreenDrawerLinux::MayDrawIncompleteShapes() { |
| 137 | return true; |
| 138 | } |
| 139 | |
Zijie He | 77b7a1d | 2017-09-01 15:51:14 -0700 | [diff] [blame] | 140 | WindowId ScreenDrawerLinux::window_id() const { |
| 141 | return window_; |
| 142 | } |
| 143 | |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 144 | void 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 | |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 166 | } // namespace |
| 167 | |
| 168 | // static |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 169 | std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() { |
Zijie He | 825f65e | 2017-08-16 14:56:42 -0700 | [diff] [blame] | 170 | return rtc::MakeUnique<ScreenDrawerLockPosix>(); |
zijiehe | 6a4607e | 2016-10-18 18:22:18 -0700 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | // static |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 174 | std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() { |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 175 | if (SharedXDisplay::CreateDefault().get()) { |
Zijie He | 825f65e | 2017-08-16 14:56:42 -0700 | [diff] [blame] | 176 | return rtc::MakeUnique<ScreenDrawerLinux>(); |
zijiehe | 0f49dac | 2016-09-07 11:52:25 -0700 | [diff] [blame] | 177 | } |
| 178 | return nullptr; |
zijiehe | 49c01d7 | 2016-08-16 17:33:55 -0700 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | } // namespace webrtc |