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