Moves the display reconfiguration callback into a separate class,
so that it can be shared with the cursor monitor when single monitor capturing
is added (https://webrtc-codereview.appspot.com/4679005/).
This Cl should have no functionality change.

BUG=2253
R=henrike@webrtc.org, sergeyu@chromium.org

Review URL: https://webrtc-codereview.appspot.com/7599004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5461 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi
index eb3bc9a..7834d21 100644
--- a/webrtc/modules/desktop_capture/desktop_capture.gypi
+++ b/webrtc/modules/desktop_capture/desktop_capture.gypi
@@ -36,6 +36,8 @@
         "differ_block.h",
         "mac/desktop_configuration.h",
         "mac/desktop_configuration.mm",
+        "mac/desktop_configuration_monitor.h",
+        "mac/desktop_configuration_monitor.cc",
         "mac/scoped_pixel_buffer_object.cc",
         "mac/scoped_pixel_buffer_object.h",
         "mouse_cursor.cc",
diff --git a/webrtc/modules/desktop_capture/desktop_capture_options.cc b/webrtc/modules/desktop_capture/desktop_capture_options.cc
index a4fa025..26044e1 100644
--- a/webrtc/modules/desktop_capture/desktop_capture_options.cc
+++ b/webrtc/modules/desktop_capture/desktop_capture_options.cc
@@ -29,6 +29,9 @@
 #if defined(USE_X11)
   result.set_x_display(SharedXDisplay::CreateDefault());
 #endif
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+  result.set_configuration_monitor(new DesktopConfigurationMonitor());
+#endif
   return result;
 }
 
diff --git a/webrtc/modules/desktop_capture/desktop_capture_options.h b/webrtc/modules/desktop_capture/desktop_capture_options.h
index f0c76b1..2a188a0 100644
--- a/webrtc/modules/desktop_capture/desktop_capture_options.h
+++ b/webrtc/modules/desktop_capture/desktop_capture_options.h
@@ -11,11 +11,16 @@
 #define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_
 
 #include "webrtc/system_wrappers/interface/constructor_magic.h"
+#include "webrtc/system_wrappers/interface/scoped_refptr.h"
 
 #if defined(USE_X11)
 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
 #endif
 
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#endif
+
 namespace webrtc {
 
 // An object that stores initialization parameters for screen and window
@@ -38,6 +43,15 @@
   }
 #endif
 
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+  DesktopConfigurationMonitor* configuration_monitor() const {
+    return configuration_monitor_;
+  }
+  void set_configuration_monitor(scoped_refptr<DesktopConfigurationMonitor> m) {
+    configuration_monitor_ = m;
+  }
+#endif
+
   // Flag indicating that the capturer should use screen change notifications.
   // Enables/disables use of XDAMAGE in the X11 capturer.
   bool use_update_notifications() const { return use_update_notifications_; }
@@ -56,6 +70,10 @@
 #if defined(USE_X11)
   scoped_refptr<SharedXDisplay> x_display_;
 #endif
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+  scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
+#endif
   bool use_update_notifications_;
   bool disable_effects_;
 };
diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration.h b/webrtc/modules/desktop_capture/mac/desktop_configuration.h
index 433040a..80a155d 100644
--- a/webrtc/modules/desktop_capture/mac/desktop_configuration.h
+++ b/webrtc/modules/desktop_capture/mac/desktop_configuration.h
@@ -52,6 +52,9 @@
   // increase as you move up the screen) or Carbon-style "top-down" coordinates.
   static MacDesktopConfiguration GetCurrent(Origin origin);
 
+  // Returns true if the given desktop configuration equals this one.
+  bool Equals(const MacDesktopConfiguration& other);
+
   // Bounds of the desktop in Density-Independent Pixels (DIPs).
   DesktopRect bounds;
 
diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration.mm b/webrtc/modules/desktop_capture/mac/desktop_configuration.mm
index a917b5d..5dac409 100644
--- a/webrtc/modules/desktop_capture/mac/desktop_configuration.mm
+++ b/webrtc/modules/desktop_capture/mac/desktop_configuration.mm
@@ -143,4 +143,22 @@
   return desktop_config;
 }
 
+
+// For convenience of comparing MacDisplayConfigurations in
+// MacDesktopConfiguration::Equals.
+bool operator==(const MacDisplayConfiguration& left,
+                const MacDisplayConfiguration& right) {
+  return left.id == right.id &&
+      left.bounds.equals(right.bounds) &&
+      left.pixel_bounds.equals(right.pixel_bounds) &&
+      left.dip_to_pixel_scale == right.dip_to_pixel_scale;
+}
+
+bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) {
+  return bounds.equals(other.bounds) &&
+      pixel_bounds.equals(other.pixel_bounds) &&
+      dip_to_pixel_scale == other.dip_to_pixel_scale &&
+      displays == other.displays;
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc
new file mode 100644
index 0000000..f0d5c34
--- /dev/null
+++ b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc
@@ -0,0 +1,91 @@
+/*
+ *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
+
+#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
+#include "webrtc/system_wrappers/interface/event_wrapper.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+
+namespace webrtc {
+
+// The amount of time allowed for displays to reconfigure.
+static const int64_t kDisplayConfigurationEventTimeoutMs = 10 * 1000;
+
+DesktopConfigurationMonitor::DesktopConfigurationMonitor()
+    : ref_count_(0),
+      display_configuration_capture_event_(EventWrapper::Create()) {
+  CGError err = CGDisplayRegisterReconfigurationCallback(
+      DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
+  if (err != kCGErrorSuccess) {
+    LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
+    abort();
+  }
+  display_configuration_capture_event_->Set();
+
+  desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
+      MacDesktopConfiguration::TopLeftOrigin);
+}
+
+DesktopConfigurationMonitor::~DesktopConfigurationMonitor() {
+  CGError err = CGDisplayRemoveReconfigurationCallback(
+      DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
+  if (err != kCGErrorSuccess)
+    LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err;
+}
+
+void DesktopConfigurationMonitor::Lock() {
+  if (!display_configuration_capture_event_->Wait(
+              kDisplayConfigurationEventTimeoutMs)) {
+    LOG_F(LS_ERROR) << "Event wait timed out.";
+    abort();
+  }
+}
+
+void DesktopConfigurationMonitor::Unlock() {
+  display_configuration_capture_event_->Set();
+}
+
+// static
+void DesktopConfigurationMonitor::DisplaysReconfiguredCallback(
+    CGDirectDisplayID display,
+    CGDisplayChangeSummaryFlags flags,
+    void *user_parameter) {
+  DesktopConfigurationMonitor* monitor =
+      reinterpret_cast<DesktopConfigurationMonitor*>(user_parameter);
+  monitor->DisplaysReconfigured(display, flags);
+}
+
+void DesktopConfigurationMonitor::DisplaysReconfigured(
+    CGDirectDisplayID display,
+    CGDisplayChangeSummaryFlags flags) {
+  if (flags & kCGDisplayBeginConfigurationFlag) {
+    if (reconfiguring_displays_.empty()) {
+      // If this is the first display to start reconfiguring then wait on
+      // |display_configuration_capture_event_| to block the capture thread
+      // from accessing display memory until the reconfiguration completes.
+      if (!display_configuration_capture_event_->Wait(
+              kDisplayConfigurationEventTimeoutMs)) {
+        LOG_F(LS_ERROR) << "Event wait timed out.";
+        abort();
+      }
+    }
+    reconfiguring_displays_.insert(display);
+  } else {
+    reconfiguring_displays_.erase(display);
+    if (reconfiguring_displays_.empty()) {
+      desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
+          MacDesktopConfiguration::TopLeftOrigin);
+      display_configuration_capture_event_->Set();
+    }
+  }
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h
new file mode 100644
index 0000000..27143a8
--- /dev/null
+++ b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <set>
+
+#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
+#include "webrtc/system_wrappers/interface/atomic32.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+class EventWrapper;
+
+// The class provides functions to synchronize capturing and display
+// reconfiguring across threads, and the up-to-date MacDesktopConfiguration.
+class DesktopConfigurationMonitor {
+ public:
+  DesktopConfigurationMonitor();
+  // Acquires a lock on the current configuration.
+  void Lock();
+  // Releases the lock previously acquired.
+  void Unlock();
+  // Returns the current desktop configuration. Should only be called when the
+  // lock has been acquired.
+  const MacDesktopConfiguration& desktop_configuration() {
+    return desktop_configuration_;
+  }
+
+  void AddRef() { ++ref_count_; }
+  void Release() {
+    if (--ref_count_ == 0)
+      delete this;
+  }
+
+ private:
+  static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
+                                           CGDisplayChangeSummaryFlags flags,
+                                           void *user_parameter);
+  ~DesktopConfigurationMonitor();
+
+  void DisplaysReconfigured(CGDirectDisplayID display,
+                            CGDisplayChangeSummaryFlags flags);
+
+  Atomic32 ref_count_;
+  std::set<CGDirectDisplayID> reconfiguring_displays_;
+  MacDesktopConfiguration desktop_configuration_;
+  scoped_ptr<EventWrapper> display_configuration_capture_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopConfigurationMonitor);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
diff --git a/webrtc/modules/desktop_capture/screen_capturer_mac.mm b/webrtc/modules/desktop_capture/screen_capturer_mac.mm
index 7aec06c..46118dd 100644
--- a/webrtc/modules/desktop_capture/screen_capturer_mac.mm
+++ b/webrtc/modules/desktop_capture/screen_capturer_mac.mm
@@ -26,11 +26,11 @@
 #include "webrtc/modules/desktop_capture/desktop_geometry.h"
 #include "webrtc/modules/desktop_capture/desktop_region.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/scoped_pixel_buffer_object.h"
 #include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
-#include "webrtc/system_wrappers/interface/event_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
 #include "webrtc/system_wrappers/interface/tick_util.h"
@@ -120,13 +120,11 @@
   return darwin_version >= 11;
 }
 
-// The amount of time allowed for displays to reconfigure.
-const int64_t kDisplayConfigurationEventTimeoutMs = 10 * 1000;
-
 // A class to perform video frame capturing for mac.
 class ScreenCapturerMac : public ScreenCapturer {
  public:
-  ScreenCapturerMac();
+  explicit ScreenCapturerMac(
+      scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor);
   virtual ~ScreenCapturerMac();
 
   bool Init();
@@ -160,8 +158,6 @@
   void ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
                         size_t count,
                         const CGRect *rect_array);
-  void DisplaysReconfigured(CGDirectDisplayID display,
-                            CGDisplayChangeSummaryFlags flags);
   static void ScreenRefreshCallback(CGRectCount count,
                                     const CGRect *rect_array,
                                     void *user_parameter);
@@ -169,10 +165,6 @@
                                        size_t count,
                                        const CGRect *rect_array,
                                        void *user_parameter);
-  static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
-                                           CGDisplayChangeSummaryFlags flags,
-                                           void *user_parameter);
-
   void ReleaseBuffers();
 
   Callback* callback_;
@@ -184,9 +176,6 @@
   // Queue of the frames buffers.
   ScreenCaptureFrameQueue queue_;
 
-  // Current display configuration.
-  MacDesktopConfiguration desktop_config_;
-
   // A thread-safe list of invalid rectangles, and the size of the most
   // recently captured screen.
   ScreenCapturerHelper helper_;
@@ -197,13 +186,12 @@
   // Contains an invalid region from the previous capture.
   DesktopRegion last_invalid_region_;
 
-  // Used to ensure that frame captures do not take place while displays
-  // are being reconfigured.
-  scoped_ptr<EventWrapper> display_configuration_capture_event_;
+  // Monitoring display reconfiguration.
+  scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
 
-  // Records the Ids of attached displays which are being reconfigured.
-  // Accessed on the thread on which we are notified of display events.
-  std::set<CGDirectDisplayID> reconfiguring_displays_;
+  // The desktop configuration obtained from desktop_config_monitor_ the last
+  // time of capturing.
+  MacDesktopConfiguration desktop_config_;
 
   // Power management assertion to prevent the screen from sleeping.
   IOPMAssertionID power_assertion_id_display_;
@@ -258,11 +246,12 @@
   return frame.release();
 }
 
-ScreenCapturerMac::ScreenCapturerMac()
+ScreenCapturerMac::ScreenCapturerMac(
+    scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
     : callback_(NULL),
       mouse_shape_observer_(NULL),
       cgl_context_(NULL),
-      display_configuration_capture_event_(EventWrapper::Create()),
+      desktop_config_monitor_(desktop_config_monitor),
       power_assertion_id_display_(kIOPMNullAssertionID),
       power_assertion_id_user_(kIOPMNullAssertionID),
       app_services_library_(NULL),
@@ -271,7 +260,6 @@
       cg_display_bits_per_pixel_(NULL),
       opengl_library_(NULL),
       cgl_set_full_screen_(NULL) {
-  display_configuration_capture_event_->Set();
 }
 
 ScreenCapturerMac::~ScreenCapturerMac() {
@@ -286,11 +274,6 @@
 
   ReleaseBuffers();
   UnregisterRefreshAndMoveHandlers();
-  CGError err = CGDisplayRemoveReconfigurationCallback(
-      ScreenCapturerMac::DisplaysReconfiguredCallback, this);
-  if (err != kCGErrorSuccess)
-    LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err;
-
   dlclose(app_services_library_);
   dlclose(opengl_library_);
 }
@@ -299,14 +282,6 @@
   if (!RegisterRefreshAndMoveHandlers()) {
     return false;
   }
-
-  CGError err = CGDisplayRegisterReconfigurationCallback(
-      ScreenCapturerMac::DisplaysReconfiguredCallback, this);
-  if (err != kCGErrorSuccess) {
-    LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
-    return false;
-  }
-
   ScreenConfigurationChanged();
   return true;
 }
@@ -351,14 +326,17 @@
 
   queue_.MoveToNextFrame();
 
-  // Wait until the display configuration is stable. If one or more displays
-  // are reconfiguring then |display_configuration_capture_event_| will not be
-  // set until the reconfiguration completes.
-  // TODO(wez): Replace this with an early-exit (See crbug.com/104542).
-  if (!display_configuration_capture_event_->Wait(
-          kDisplayConfigurationEventTimeoutMs)) {
-    LOG_F(LS_ERROR) << "Event wait timed out.";
-    abort();
+  desktop_config_monitor_->Lock();
+  MacDesktopConfiguration new_config =
+      desktop_config_monitor_->desktop_configuration();
+  if (!desktop_config_.Equals(new_config)) {
+    desktop_config_ = new_config;
+    // If the display configuraiton has changed then refresh capturer data
+    // structures. Occasionally, the refresh and move handlers are lost when
+    // the screen mode changes, so re-register them here.
+    UnregisterRefreshAndMoveHandlers();
+    RegisterRefreshAndMoveHandlers();
+    ScreenConfigurationChanged();
   }
 
   DesktopRegion region;
@@ -400,7 +378,7 @@
 
   // Signal that we are done capturing data from the display framebuffer,
   // and accessing display structures.
-  display_configuration_capture_event_->Set();
+  desktop_config_monitor_->Unlock();
 
   // Capture the current cursor shape and notify |callback_| if it has changed.
   CaptureCursor();
@@ -693,14 +671,8 @@
   // Clear the dirty region, in case the display is down-sizing.
   helper_.ClearInvalidRegion();
 
-  // Refresh the cached desktop configuration.
-  desktop_config_ = MacDesktopConfiguration::GetCurrent(
-      MacDesktopConfiguration::TopLeftOrigin);
-
   // Re-mark the entire desktop as dirty.
-  helper_.InvalidateScreen(
-      DesktopSize(desktop_config_.pixel_bounds.width(),
-                          desktop_config_.pixel_bounds.height()));
+  helper_.InvalidateScreen(desktop_config_.pixel_bounds.size());
 
   // Make sure the frame buffers will be reallocated.
   queue_.Reset();
@@ -847,39 +819,6 @@
   ScreenRefresh(count, refresh_rects);
 }
 
-void ScreenCapturerMac::DisplaysReconfigured(
-    CGDirectDisplayID display,
-    CGDisplayChangeSummaryFlags flags) {
-  if (flags & kCGDisplayBeginConfigurationFlag) {
-    if (reconfiguring_displays_.empty()) {
-      // If this is the first display to start reconfiguring then wait on
-      // |display_configuration_capture_event_| to block the capture thread
-      // from accessing display memory until the reconfiguration completes.
-      if (!display_configuration_capture_event_->Wait(
-              kDisplayConfigurationEventTimeoutMs)) {
-        LOG_F(LS_ERROR) << "Event wait timed out.";
-        abort();
-      }
-    }
-
-    reconfiguring_displays_.insert(display);
-  } else {
-    reconfiguring_displays_.erase(display);
-
-    if (reconfiguring_displays_.empty()) {
-      // If no other displays are reconfiguring then refresh capturer data
-      // structures and un-block the capturer thread. Occasionally, the
-      // refresh and move handlers are lost when the screen mode changes,
-      // so re-register them here (the same does not appear to be true for
-      // the reconfiguration handler itself).
-      UnregisterRefreshAndMoveHandlers();
-      RegisterRefreshAndMoveHandlers();
-      ScreenConfigurationChanged();
-      display_configuration_capture_event_->Set();
-    }
-  }
-}
-
 void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count,
                                               const CGRect* rect_array,
                                               void* user_parameter) {
@@ -900,20 +839,15 @@
   capturer->ScreenUpdateMove(delta, count, rect_array);
 }
 
-void ScreenCapturerMac::DisplaysReconfiguredCallback(
-    CGDirectDisplayID display,
-    CGDisplayChangeSummaryFlags flags,
-    void* user_parameter) {
-  ScreenCapturerMac* capturer =
-      reinterpret_cast<ScreenCapturerMac*>(user_parameter);
-  capturer->DisplaysReconfigured(display, flags);
-}
-
 }  // namespace
 
 // static
 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
-  scoped_ptr<ScreenCapturerMac> capturer(new ScreenCapturerMac());
+  if (!options.configuration_monitor())
+    return NULL;
+
+  scoped_ptr<ScreenCapturerMac> capturer(
+      new ScreenCapturerMac(options.configuration_monitor()));
   if (!capturer->Init())
     capturer.reset();
   return capturer.release();
diff --git a/webrtc/system_wrappers/interface/scoped_refptr.h b/webrtc/system_wrappers/interface/scoped_refptr.h
index a8a0074..b344d21 100644
--- a/webrtc/system_wrappers/interface/scoped_refptr.h
+++ b/webrtc/system_wrappers/interface/scoped_refptr.h
@@ -11,6 +11,8 @@
 #ifndef SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_
 #define SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_
 
+#include <stddef.h>
+
 namespace webrtc {
 
 // Extracted from Chromium's src/base/memory/ref_counted.h.