OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include <assert.h> | 11 #include <assert.h> |
12 #include <string.h> | 12 #include <string.h> |
13 #include <X11/Xatom.h> | 13 #include <X11/Xatom.h> |
14 #include <X11/extensions/Xcomposite.h> | 14 #include <X11/extensions/Xcomposite.h> |
15 #include <X11/extensions/Xrender.h> | 15 #include <X11/extensions/Xrender.h> |
16 #include <X11/Xutil.h> | 16 #include <X11/Xutil.h> |
17 | 17 |
18 #include <algorithm> | 18 #include <algorithm> |
19 | 19 |
20 #include "webrtc/base/constructormagic.h" | 20 #include "webrtc/base/constructormagic.h" |
21 #include "webrtc/base/scoped_ref_ptr.h" | 21 #include "webrtc/base/scoped_ref_ptr.h" |
22 #include "webrtc/modules/desktop_capture/desktop_capturer.h" | 22 #include "webrtc/modules/desktop_capture/desktop_capturer.h" |
23 #include "webrtc/modules/desktop_capture/desktop_capture_options.h" | 23 #include "webrtc/modules/desktop_capture/desktop_capture_options.h" |
24 #include "webrtc/modules/desktop_capture/desktop_frame.h" | 24 #include "webrtc/modules/desktop_capture/desktop_frame.h" |
25 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h" | 25 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h" |
26 #include "webrtc/modules/desktop_capture/x11/x_error_trap.h" | 26 #include "webrtc/modules/desktop_capture/x11/x_error_trap.h" |
27 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" | 27 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" |
28 #include "webrtc/system_wrappers/include/logging.h" | |
29 | 28 |
30 namespace webrtc { | 29 namespace webrtc { |
31 | 30 |
32 namespace { | 31 namespace { |
33 | 32 |
34 // Convenience wrapper for XGetWindowProperty() results. | 33 // Convenience wrapper for XGetWindowProperty() results. |
35 template <class PropertyType> | 34 template <class PropertyType> |
36 class XWindowProperty { | 35 class XWindowProperty { |
37 public: | 36 public: |
38 XWindowProperty(Display* display, Window window, Atom property) { | 37 XWindowProperty(Display* display, Window window, Atom property) { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 normal_window_type_atom_ = XInternAtom( | 133 normal_window_type_atom_ = XInternAtom( |
135 display(), "_NET_WM_WINDOW_TYPE_NORMAL", True); | 134 display(), "_NET_WM_WINDOW_TYPE_NORMAL", True); |
136 | 135 |
137 int event_base, error_base, major_version, minor_version; | 136 int event_base, error_base, major_version, minor_version; |
138 if (XCompositeQueryExtension(display(), &event_base, &error_base) && | 137 if (XCompositeQueryExtension(display(), &event_base, &error_base) && |
139 XCompositeQueryVersion(display(), &major_version, &minor_version) && | 138 XCompositeQueryVersion(display(), &major_version, &minor_version) && |
140 // XCompositeNameWindowPixmap() requires version 0.2 | 139 // XCompositeNameWindowPixmap() requires version 0.2 |
141 (major_version > 0 || minor_version >= 2)) { | 140 (major_version > 0 || minor_version >= 2)) { |
142 has_composite_extension_ = true; | 141 has_composite_extension_ = true; |
143 } else { | 142 } else { |
144 LOG(LS_INFO) << "Xcomposite extension not available or too old."; | |
145 } | 143 } |
146 | 144 |
147 x_display_->AddEventHandler(ConfigureNotify, this); | 145 x_display_->AddEventHandler(ConfigureNotify, this); |
148 } | 146 } |
149 | 147 |
150 WindowCapturerLinux::~WindowCapturerLinux() { | 148 WindowCapturerLinux::~WindowCapturerLinux() { |
151 x_display_->RemoveEventHandler(ConfigureNotify, this); | 149 x_display_->RemoveEventHandler(ConfigureNotify, this); |
152 } | 150 } |
153 | 151 |
154 bool WindowCapturerLinux::GetSourceList(SourceList* sources) { | 152 bool WindowCapturerLinux::GetSourceList(SourceList* sources) { |
155 SourceList result; | 153 SourceList result; |
156 | 154 |
157 XErrorTrap error_trap(display()); | 155 XErrorTrap error_trap(display()); |
158 | 156 |
159 int num_screens = XScreenCount(display()); | 157 int num_screens = XScreenCount(display()); |
160 for (int screen = 0; screen < num_screens; ++screen) { | 158 for (int screen = 0; screen < num_screens; ++screen) { |
161 ::Window root_window = XRootWindow(display(), screen); | 159 ::Window root_window = XRootWindow(display(), screen); |
162 ::Window parent; | 160 ::Window parent; |
163 ::Window *children; | 161 ::Window *children; |
164 unsigned int num_children; | 162 unsigned int num_children; |
165 int status = XQueryTree(display(), root_window, &root_window, &parent, | 163 int status = XQueryTree(display(), root_window, &root_window, &parent, |
166 &children, &num_children); | 164 &children, &num_children); |
167 if (status == 0) { | 165 if (status == 0) { |
168 LOG(LS_ERROR) << "Failed to query for child windows for screen " | |
169 << screen; | |
170 continue; | 166 continue; |
171 } | 167 } |
172 | 168 |
173 for (unsigned int i = 0; i < num_children; ++i) { | 169 for (unsigned int i = 0; i < num_children; ++i) { |
174 // Iterate in reverse order to return windows from front to back. | 170 // Iterate in reverse order to return windows from front to back. |
175 ::Window app_window = | 171 ::Window app_window = |
176 GetApplicationWindow(children[num_children - 1 - i]); | 172 GetApplicationWindow(children[num_children - 1 - i]); |
177 if (app_window && !IsDesktopElement(app_window)) { | 173 if (app_window && !IsDesktopElement(app_window)) { |
178 Source w; | 174 Source w; |
179 w.id = app_window; | 175 w.id = app_window; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 return false; | 213 return false; |
218 | 214 |
219 unsigned int num_children; | 215 unsigned int num_children; |
220 ::Window* children; | 216 ::Window* children; |
221 ::Window parent; | 217 ::Window parent; |
222 ::Window root; | 218 ::Window root; |
223 // Find the root window to pass event to. | 219 // Find the root window to pass event to. |
224 int status = XQueryTree( | 220 int status = XQueryTree( |
225 display(), selected_window_, &root, &parent, &children, &num_children); | 221 display(), selected_window_, &root, &parent, &children, &num_children); |
226 if (status == 0) { | 222 if (status == 0) { |
227 LOG(LS_ERROR) << "Failed to query for the root window."; | |
228 return false; | 223 return false; |
229 } | 224 } |
230 | 225 |
231 if (children) | 226 if (children) |
232 XFree(children); | 227 XFree(children); |
233 | 228 |
234 XRaiseWindow(display(), selected_window_); | 229 XRaiseWindow(display(), selected_window_); |
235 | 230 |
236 // Some window managers (e.g., metacity in GNOME) consider it illegal to | 231 // Some window managers (e.g., metacity in GNOME) consider it illegal to |
237 // raise a window without also giving it input focus with | 232 // raise a window without also giving it input focus with |
(...skipping 25 matching lines...) Expand all Loading... |
263 | 258 |
264 void WindowCapturerLinux::Start(Callback* callback) { | 259 void WindowCapturerLinux::Start(Callback* callback) { |
265 assert(!callback_); | 260 assert(!callback_); |
266 assert(callback); | 261 assert(callback); |
267 | 262 |
268 callback_ = callback; | 263 callback_ = callback; |
269 } | 264 } |
270 | 265 |
271 void WindowCapturerLinux::CaptureFrame() { | 266 void WindowCapturerLinux::CaptureFrame() { |
272 if (!x_server_pixel_buffer_.IsWindowValid()) { | 267 if (!x_server_pixel_buffer_.IsWindowValid()) { |
273 LOG(LS_INFO) << "The window is no longer valid."; | |
274 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); | 268 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); |
275 return; | 269 return; |
276 } | 270 } |
277 | 271 |
278 x_display_->ProcessPendingXEvents(); | 272 x_display_->ProcessPendingXEvents(); |
279 | 273 |
280 if (!has_composite_extension_) { | 274 if (!has_composite_extension_) { |
281 // Without the Xcomposite extension we capture when the whole window is | 275 // Without the Xcomposite extension we capture when the whole window is |
282 // visible on screen and not covered by any other window. This is not | 276 // visible on screen and not covered by any other window. This is not |
283 // something we want so instead, just bail out. | 277 // something we want so instead, just bail out. |
284 LOG(LS_INFO) << "No Xcomposite extension detected."; | |
285 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); | 278 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); |
286 return; | 279 return; |
287 } | 280 } |
288 | 281 |
289 std::unique_ptr<DesktopFrame> frame( | 282 std::unique_ptr<DesktopFrame> frame( |
290 new BasicDesktopFrame(x_server_pixel_buffer_.window_size())); | 283 new BasicDesktopFrame(x_server_pixel_buffer_.window_size())); |
291 | 284 |
292 x_server_pixel_buffer_.Synchronize(); | 285 x_server_pixel_buffer_.Synchronize(); |
293 if (!x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()), | 286 if (!x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()), |
294 frame.get())) { | 287 frame.get())) { |
295 callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); | 288 callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); |
296 return; | 289 return; |
297 } | 290 } |
298 | 291 |
299 frame->mutable_updated_region()->SetRect( | 292 frame->mutable_updated_region()->SetRect( |
300 DesktopRect::MakeSize(frame->size())); | 293 DesktopRect::MakeSize(frame->size())); |
301 | 294 |
302 callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); | 295 callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); |
303 } | 296 } |
304 | 297 |
305 bool WindowCapturerLinux::HandleXEvent(const XEvent& event) { | 298 bool WindowCapturerLinux::HandleXEvent(const XEvent& event) { |
306 if (event.type == ConfigureNotify) { | 299 if (event.type == ConfigureNotify) { |
307 XConfigureEvent xce = event.xconfigure; | 300 XConfigureEvent xce = event.xconfigure; |
308 if (!DesktopSize(xce.width, xce.height).equals( | 301 if (!DesktopSize(xce.width, xce.height).equals( |
309 x_server_pixel_buffer_.window_size())) { | 302 x_server_pixel_buffer_.window_size())) { |
310 if (!x_server_pixel_buffer_.Init(display(), selected_window_)) { | 303 if (!x_server_pixel_buffer_.Init(display(), selected_window_)) { |
311 LOG(LS_ERROR) << "Failed to initialize pixel buffer after resizing."; | |
312 } | 304 } |
313 return true; | 305 return true; |
314 } | 306 } |
315 } | 307 } |
316 return false; | 308 return false; |
317 } | 309 } |
318 | 310 |
319 ::Window WindowCapturerLinux::GetApplicationWindow(::Window window) { | 311 ::Window WindowCapturerLinux::GetApplicationWindow(::Window window) { |
320 // Get WM_STATE property of the window. | 312 // Get WM_STATE property of the window. |
321 XWindowProperty<uint32_t> window_state(display(), window, wm_state_atom_); | 313 XWindowProperty<uint32_t> window_state(display(), window, wm_state_atom_); |
322 | 314 |
323 // WM_STATE is considered to be set to WithdrawnState when it missing. | 315 // WM_STATE is considered to be set to WithdrawnState when it missing. |
324 int32_t state = window_state.is_valid() ? | 316 int32_t state = window_state.is_valid() ? |
325 *window_state.data() : WithdrawnState; | 317 *window_state.data() : WithdrawnState; |
326 | 318 |
327 if (state == NormalState) { | 319 if (state == NormalState) { |
328 // Window has WM_STATE==NormalState. Return it. | 320 // Window has WM_STATE==NormalState. Return it. |
329 return window; | 321 return window; |
330 } else if (state == IconicState) { | 322 } else if (state == IconicState) { |
331 // Window is in minimized. Skip it. | 323 // Window is in minimized. Skip it. |
332 return 0; | 324 return 0; |
333 } | 325 } |
334 | 326 |
335 // If the window is in WithdrawnState then look at all of its children. | 327 // If the window is in WithdrawnState then look at all of its children. |
336 ::Window root, parent; | 328 ::Window root, parent; |
337 ::Window *children; | 329 ::Window *children; |
338 unsigned int num_children; | 330 unsigned int num_children; |
339 if (!XQueryTree(display(), window, &root, &parent, &children, | 331 if (!XQueryTree(display(), window, &root, &parent, &children, |
340 &num_children)) { | 332 &num_children)) { |
341 LOG(LS_ERROR) << "Failed to query for child windows although window" | |
342 << "does not have a valid WM_STATE."; | |
343 return 0; | 333 return 0; |
344 } | 334 } |
345 ::Window app_window = 0; | 335 ::Window app_window = 0; |
346 for (unsigned int i = 0; i < num_children; ++i) { | 336 for (unsigned int i = 0; i < num_children; ++i) { |
347 app_window = GetApplicationWindow(children[i]); | 337 app_window = GetApplicationWindow(children[i]); |
348 if (app_window) | 338 if (app_window) |
349 break; | 339 break; |
350 } | 340 } |
351 | 341 |
352 if (children) | 342 if (children) |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 window_name.value = nullptr; | 386 window_name.value = nullptr; |
397 if (window) { | 387 if (window) { |
398 status = XGetWMName(display(), window, &window_name); | 388 status = XGetWMName(display(), window, &window_name); |
399 if (status && window_name.value && window_name.nitems) { | 389 if (status && window_name.value && window_name.nitems) { |
400 int cnt; | 390 int cnt; |
401 char** list = nullptr; | 391 char** list = nullptr; |
402 status = Xutf8TextPropertyToTextList(display(), &window_name, &list, | 392 status = Xutf8TextPropertyToTextList(display(), &window_name, &list, |
403 &cnt); | 393 &cnt); |
404 if (status >= Success && cnt && *list) { | 394 if (status >= Success && cnt && *list) { |
405 if (cnt > 1) { | 395 if (cnt > 1) { |
406 LOG(LS_INFO) << "Window has " << cnt | |
407 << " text properties, only using the first one."; | |
408 } | 396 } |
409 *title = *list; | 397 *title = *list; |
410 result = true; | 398 result = true; |
411 } | 399 } |
412 if (list) | 400 if (list) |
413 XFreeStringList(list); | 401 XFreeStringList(list); |
414 } | 402 } |
415 if (window_name.value) | 403 if (window_name.value) |
416 XFree(window_name.value); | 404 XFree(window_name.value); |
417 } | 405 } |
418 return result; | 406 return result; |
419 } | 407 } |
420 | 408 |
421 } // namespace | 409 } // namespace |
422 | 410 |
423 // static | 411 // static |
424 std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer( | 412 std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer( |
425 const DesktopCaptureOptions& options) { | 413 const DesktopCaptureOptions& options) { |
426 if (!options.x_display()) | 414 if (!options.x_display()) |
427 return nullptr; | 415 return nullptr; |
428 return std::unique_ptr<DesktopCapturer>(new WindowCapturerLinux(options)); | 416 return std::unique_ptr<DesktopCapturer>(new WindowCapturerLinux(options)); |
429 } | 417 } |
430 | 418 |
431 } // namespace webrtc | 419 } // namespace webrtc |
OLD | NEW |