Use WindowFinder in WindowCapturer to detect IsOccluded()

In this change,
bool DesktopCapturer::IsOccluded(const DesktopVector& pos);
is added to DesktopCapturer interface. This function returns true if the |pos|
is hidden by other elements on the display.

The function expects to return false for ScreenCapturer implementations:
everything is visible on the screen.

In WindowCapturer implementations, WindowFinder is expected to be used to help
detect the WindowId under |pos|. If the window_id_ equals to the window id
returned by WindowFinder, this function returns false.

To ensure the correct coordinate is used, a comment is also added to
WindowFinder::GetWindowUnderPoint() function. Considering it's used only in
desktop_capture module, using system coordinate is simpler.

Bug: webrtc:7950
Change-Id: I8ac4fbd5f4a612388f1593907805cfb2c359f70f
Reviewed-on: https://chromium-review.googlesource.com/636784
Reviewed-by: Jamie Walch <jamiewalch@chromium.org>
Commit-Queue: Zijie He <zijiehe@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#19590}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 12827113a1c1467065b124886dd658e6274a5ba5
diff --git a/modules/desktop_capture/desktop_capture_options.h b/modules/desktop_capture/desktop_capture_options.h
index e10cab7..8956275 100644
--- a/modules/desktop_capture/desktop_capture_options.h
+++ b/modules/desktop_capture/desktop_capture_options.h
@@ -49,14 +49,22 @@
 #endif
 
 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+  // TODO(zijiehe): Remove both DesktopConfigurationMonitor and
+  // FullScreenChromeWindowDetector out of DesktopCaptureOptions. It's not
+  // reasonable for external consumers to set these two parameters.
   DesktopConfigurationMonitor* configuration_monitor() const {
     return configuration_monitor_;
   }
+  // If nullptr is set, ScreenCapturer won't work and WindowCapturer may return
+  // inaccurate result from IsOccluded() function.
   void set_configuration_monitor(
       rtc::scoped_refptr<DesktopConfigurationMonitor> m) {
     configuration_monitor_ = m;
   }
 
+  // TODO(zijiehe): Instead of FullScreenChromeWindowDetector, provide a
+  // FullScreenWindowDetector for external consumers to detect the target
+  // fullscreen window.
   FullScreenChromeWindowDetector* full_screen_chrome_window_detector() const {
     return full_screen_window_detector_;
   }
@@ -83,8 +91,6 @@
   // Flag that should be set if the consumer uses updated_region() and the
   // capturer should try to provide correct updated_region() for the frames it
   // generates (e.g. by comparing each frame with the previous one).
-  // TODO(zijiehe): WindowCapturer ignores this opinion until we merge
-  // ScreenCapturer and WindowCapturer interfaces.
   bool detect_updated_region() const { return detect_updated_region_; }
   void set_detect_updated_region(bool detect_updated_region) {
     detect_updated_region_ = detect_updated_region;
diff --git a/modules/desktop_capture/desktop_capturer.cc b/modules/desktop_capture/desktop_capturer.cc
index e655474..67d3520 100644
--- a/modules/desktop_capture/desktop_capturer.cc
+++ b/modules/desktop_capture/desktop_capturer.cc
@@ -34,6 +34,10 @@
   return false;
 }
 
+bool DesktopCapturer::IsOccluded(const DesktopVector& pos) {
+  return false;
+}
+
 // static
 std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateWindowCapturer(
     const DesktopCaptureOptions& options) {
diff --git a/modules/desktop_capture/desktop_capturer.h b/modules/desktop_capture/desktop_capturer.h
index 5af69b8..4f44ce1 100644
--- a/modules/desktop_capture/desktop_capturer.h
+++ b/modules/desktop_capture/desktop_capturer.h
@@ -119,6 +119,13 @@
   // implementation does not support this functionality.
   virtual bool FocusOnSelectedSource();
 
+  // Returns true if the |pos| on the selected source is covered by other
+  // elements on the display, and is not visible to the users.
+  // |pos| is in full desktop coordinates, i.e. the top-left monitor always
+  // starts from (0, 0).
+  // The return value if |pos| is out of the scope of the source is undefined.
+  virtual bool IsOccluded(const DesktopVector& pos);
+
   // Creates a DesktopCapturer instance which targets to capture windows.
   static std::unique_ptr<DesktopCapturer> CreateWindowCapturer(
       const DesktopCaptureOptions& options);
diff --git a/modules/desktop_capture/mac/window_list_utils.h b/modules/desktop_capture/mac/window_list_utils.h
index a3cd507..7147e89 100644
--- a/modules/desktop_capture/mac/window_list_utils.h
+++ b/modules/desktop_capture/mac/window_list_utils.h
@@ -51,7 +51,9 @@
 WindowId GetWindowId(CFDictionaryRef window);
 
 // Returns the bounds of |window|. If |window| is not a window or the bounds
-// cannot be retrieved, this function returns an empty DesktopRect.
+// cannot be retrieved, this function returns an empty DesktopRect. The returned
+// DesktopRect is in system coordinate, i.e. the primary monitor always starts
+// from (0, 0).
 DesktopRect GetWindowBounds(CFDictionaryRef window);
 
 }  // namespace webrtc
diff --git a/modules/desktop_capture/window_capturer_mac.mm b/modules/desktop_capture/window_capturer_mac.mm
index c3a6d00..4f1c191 100644
--- a/modules/desktop_capture/window_capturer_mac.mm
+++ b/modules/desktop_capture/window_capturer_mac.mm
@@ -13,10 +13,14 @@
 #include <Cocoa/Cocoa.h>
 #include <CoreFoundation/CoreFoundation.h>
 
+#include <utility>
+
 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_capturer.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/window_finder_mac.h"
 #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
+#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
 #include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
 #include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
 #include "webrtc/rtc_base/constructormagic.h"
@@ -44,7 +48,9 @@
 class WindowCapturerMac : public DesktopCapturer {
  public:
   explicit WindowCapturerMac(rtc::scoped_refptr<FullScreenChromeWindowDetector>
-                                 full_screen_chrome_window_detector);
+                                 full_screen_chrome_window_detector,
+                             rtc::scoped_refptr<DesktopConfigurationMonitor>
+                                 configuration_monitor);
   ~WindowCapturerMac() override;
 
   // DesktopCapturer interface.
@@ -53,6 +59,7 @@
   bool GetSourceList(SourceList* sources) override;
   bool SelectSource(SourceId id) override;
   bool FocusOnSelectedSource() override;
+  bool IsOccluded(const DesktopVector& pos) override;
 
  private:
   Callback* callback_ = nullptr;
@@ -60,16 +67,23 @@
   // The window being captured.
   CGWindowID window_id_ = 0;
 
-  rtc::scoped_refptr<FullScreenChromeWindowDetector>
+  const rtc::scoped_refptr<FullScreenChromeWindowDetector>
       full_screen_chrome_window_detector_;
 
+  const rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
+
+  WindowFinderMac window_finder_;
+
   RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
 };
 
 WindowCapturerMac::WindowCapturerMac(
     rtc::scoped_refptr<FullScreenChromeWindowDetector>
-        full_screen_chrome_window_detector)
-    : full_screen_chrome_window_detector_(full_screen_chrome_window_detector) {}
+        full_screen_chrome_window_detector,
+    rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor)
+    : full_screen_chrome_window_detector_(
+          std::move(full_screen_chrome_window_detector)),
+      configuration_monitor_(std::move(configuration_monitor)) {}
 
 WindowCapturerMac::~WindowCapturerMac() {}
 
@@ -121,6 +135,17 @@
   return result;
 }
 
+bool WindowCapturerMac::IsOccluded(const DesktopVector& pos) {
+  DesktopVector sys_pos = pos;
+  if (configuration_monitor_) {
+    configuration_monitor_->Lock();
+    auto configuration = configuration_monitor_->desktop_configuration();
+    configuration_monitor_->Unlock();
+    sys_pos = pos.add(configuration.bounds.top_left());
+  }
+  return window_finder_.GetWindowUnderPoint(sys_pos) != window_id_;
+}
+
 void WindowCapturerMac::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
@@ -192,7 +217,8 @@
 std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
     const DesktopCaptureOptions& options) {
   return std::unique_ptr<DesktopCapturer>(
-      new WindowCapturerMac(options.full_screen_chrome_window_detector()));
+      new WindowCapturerMac(options.full_screen_chrome_window_detector(),
+                            options.configuration_monitor()));
 }
 
 }  // namespace webrtc
diff --git a/modules/desktop_capture/window_capturer_win.cc b/modules/desktop_capture/window_capturer_win.cc
index dde91aa..880a719 100644
--- a/modules/desktop_capture/window_capturer_win.cc
+++ b/modules/desktop_capture/window_capturer_win.cc
@@ -14,6 +14,8 @@
 
 #include "webrtc/modules/desktop_capture/desktop_capturer.h"
 #include "webrtc/modules/desktop_capture/desktop_frame_win.h"
+#include "webrtc/modules/desktop_capture/window_finder_win.h"
+#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
 #include "webrtc/modules/desktop_capture/win/window_capture_utils.h"
 #include "webrtc/rtc_base/checks.h"
 #include "webrtc/rtc_base/constructormagic.h"
@@ -90,6 +92,7 @@
   bool GetSourceList(SourceList* sources) override;
   bool SelectSource(SourceId id) override;
   bool FocusOnSelectedSource() override;
+  bool IsOccluded(const DesktopVector& pos) override;
 
  private:
   Callback* callback_ = nullptr;
@@ -106,6 +109,8 @@
   // are interleaved with Capture() calls.
   std::map<HWND, DesktopSize> window_size_map_;
 
+  WindowFinderWin window_finder_;
+
   RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerWin);
 };
 
@@ -152,6 +157,12 @@
          SetForegroundWindow(window_) != FALSE;
 }
 
+bool WindowCapturerWin::IsOccluded(const DesktopVector& pos) {
+  DesktopVector sys_pos = pos.add(GetFullscreenRect().top_left());
+  return reinterpret_cast<HWND>(window_finder_.GetWindowUnderPoint(sys_pos))
+      != window_;
+}
+
 void WindowCapturerWin::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
diff --git a/modules/desktop_capture/window_capturer_x11.cc b/modules/desktop_capture/window_capturer_x11.cc
index 3ea69a1..5d01a15 100644
--- a/modules/desktop_capture/window_capturer_x11.cc
+++ b/modules/desktop_capture/window_capturer_x11.cc
@@ -17,6 +17,7 @@
 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_capturer.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/window_finder_x11.h"
 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
 #include "webrtc/modules/desktop_capture/x11/window_list_utils.h"
 #include "webrtc/modules/desktop_capture/x11/x_atom_cache.h"
@@ -42,6 +43,7 @@
   bool GetSourceList(SourceList* sources) override;
   bool SelectSource(SourceId id) override;
   bool FocusOnSelectedSource() override;
+  bool IsOccluded(const DesktopVector& pos) override;
 
   // SharedXDisplay::XEventHandler interface.
   bool HandleXEvent(const XEvent& event) override;
@@ -61,13 +63,15 @@
   ::Window selected_window_ = 0;
   XServerPixelBuffer x_server_pixel_buffer_;
   XAtomCache atom_cache_;
+  WindowFinderX11 window_finder_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
 };
 
 WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options)
     : x_display_(options.x_display()),
-      atom_cache_(display()) {
+      atom_cache_(display()),
+      window_finder_(&atom_cache_) {
   int event_base, error_base, major_version, minor_version;
   if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
       XCompositeQueryVersion(display(), &major_version, &minor_version) &&
@@ -216,6 +220,11 @@
   callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
 }
 
+bool WindowCapturerLinux::IsOccluded(const DesktopVector& pos) {
+  return window_finder_.GetWindowUnderPoint(pos) !=
+      static_cast<WindowId>(selected_window_);
+}
+
 bool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
   if (event.type == ConfigureNotify) {
     XConfigureEvent xce = event.xconfigure;
diff --git a/modules/desktop_capture/window_finder.h b/modules/desktop_capture/window_finder.h
index 4c46c48..90f3513 100644
--- a/modules/desktop_capture/window_finder.h
+++ b/modules/desktop_capture/window_finder.h
@@ -25,6 +25,8 @@
   // Returns the id of the visible window under |point|. This function returns
   // kNullWindowId if no window is under |point| and the platform does not have
   // "root window" concept, i.e. the visible area under |point| is the desktop.
+  // |point| is always in system coordinate, i.e. the primary monitor always
+  // starts from (0, 0).
   virtual WindowId GetWindowUnderPoint(DesktopVector point) = 0;
 };
 
diff --git a/modules/desktop_capture/window_finder_mac.h b/modules/desktop_capture/window_finder_mac.h
index 4b01600..9f872ea 100644
--- a/modules/desktop_capture/window_finder_mac.h
+++ b/modules/desktop_capture/window_finder_mac.h
@@ -15,7 +15,7 @@
 
 namespace webrtc {
 
-// The implementation of WindowFinder for Windows.
+// The implementation of WindowFinder for Mac OSX.
 class WindowFinderMac final : public WindowFinder {
  public:
   WindowFinderMac();
diff --git a/modules/desktop_capture/window_finder_x11.h b/modules/desktop_capture/window_finder_x11.h
index 9a4e020..6dec89b 100644
--- a/modules/desktop_capture/window_finder_x11.h
+++ b/modules/desktop_capture/window_finder_x11.h
@@ -17,7 +17,7 @@
 
 class XAtomCache;
 
-// The implementation of WindowFinder for Windows.
+// The implementation of WindowFinder for X11.
 class WindowFinderX11 final : public WindowFinder {
  public:
   explicit WindowFinderX11(XAtomCache* cache);