1af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org/*
2af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org *
4af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org *  Use of this source code is governed by a BSD-style license
5af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org *  that can be found in the LICENSE file in the root of the source
6af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org *  tree. An additional intellectual property rights grant can be found
7af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org *  in the file PATENTS.  All contributing project authors may
8af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org *  be found in the AUTHORS file in the root of the source tree.
9af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org */
10af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org
11af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
12af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org
132873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org#include <assert.h>
142873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org#include <ApplicationServices/ApplicationServices.h>
152873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org#include <Cocoa/Cocoa.h>
162873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org#include <CoreFoundation/CoreFoundation.h>
172873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
18678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org#include "webrtc/base/macutils.h"
195e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
202873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_frame.h"
211d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
225e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
23678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
242873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org#include "webrtc/modules/desktop_capture/mouse_cursor.h"
251d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org#include "webrtc/system_wrappers/interface/logging.h"
262873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org#include "webrtc/system_wrappers/interface/scoped_ptr.h"
275e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org#include "webrtc/system_wrappers/interface/scoped_refptr.h"
28af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org
29af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.orgnamespace webrtc {
30af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org
312873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.orgclass MouseCursorMonitorMac : public MouseCursorMonitor {
322873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org public:
335e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org  MouseCursorMonitorMac(const DesktopCaptureOptions& options,
341d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org                        CGWindowID window_id,
351d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org                        ScreenId screen_id);
362873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  virtual ~MouseCursorMonitorMac();
372873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
382873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  virtual void Init(Callback* callback, Mode mode) OVERRIDE;
392873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  virtual void Capture() OVERRIDE;
402873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
412873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org private:
421d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
431d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org                                           CGDisplayChangeSummaryFlags flags,
441d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org                                           void *user_parameter);
451d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  void DisplaysReconfigured(CGDirectDisplayID display,
461d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org                            CGDisplayChangeSummaryFlags flags);
471d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org
482873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  void CaptureImage();
492873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
505e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org  scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
512873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CGWindowID window_id_;
521d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  ScreenId screen_id_;
532873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  Callback* callback_;
542873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  Mode mode_;
552873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  scoped_ptr<MouseCursor> last_cursor_;
56678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org  scoped_refptr<FullScreenChromeWindowDetector>
57678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org      full_screen_chrome_window_detector_;
582873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org};
592873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
605e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.orgMouseCursorMonitorMac::MouseCursorMonitorMac(
615e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org    const DesktopCaptureOptions& options,
621d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    CGWindowID window_id,
631d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    ScreenId screen_id)
645e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org    : configuration_monitor_(options.configuration_monitor()),
655e9b73011bf23ffbf48d39d6704e65f037a9bdd7jiayl@webrtc.org      window_id_(window_id),
661d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      screen_id_(screen_id),
672873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      callback_(NULL),
68678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org      mode_(SHAPE_AND_POSITION),
69678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org      full_screen_chrome_window_detector_(
70678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org          options.full_screen_chrome_window_detector()) {
711d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  assert(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
72678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org  if (screen_id != kInvalidScreenId &&
73678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org      rtc::GetOSVersionName() < rtc::kMacOSLion) {
741d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    // Single screen capture is not supported on pre OS X 10.7.
751d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    screen_id_ = kFullDesktopScreenId;
761d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  }
772873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org}
782873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
792873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.orgMouseCursorMonitorMac::~MouseCursorMonitorMac() {}
802873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
812873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.orgvoid MouseCursorMonitorMac::Init(Callback* callback, Mode mode) {
822873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  assert(!callback_);
832873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  assert(callback);
842873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
852873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  callback_ = callback;
862873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  mode_ = mode;
872873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org}
882873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
892873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.orgvoid MouseCursorMonitorMac::Capture() {
902873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  assert(callback_);
912873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
922873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CaptureImage();
932873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
942873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  if (mode_ != SHAPE_AND_POSITION)
952873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    return;
962873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
972873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CursorState state = INSIDE;
982873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
992873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CGEventRef event = CGEventCreate(NULL);
1002873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CGPoint gc_position = CGEventGetLocation(event);
1012873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CFRelease(event);
1022873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
1032873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  DesktopVector position(gc_position.x, gc_position.y);
1042873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
1051d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  configuration_monitor_->Lock();
1061d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  MacDesktopConfiguration configuration =
1071d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      configuration_monitor_->desktop_configuration();
1081d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  configuration_monitor_->Unlock();
1091d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  float scale = 1.0f;
1101d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org
1111d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  // Find the dpi to physical pixel scale for the screen where the mouse cursor
1121d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  // is.
1131d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  for (MacDisplayConfigurations::iterator it = configuration.displays.begin();
1141d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      it != configuration.displays.end(); ++it) {
1151d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    if (it->bounds.Contains(position)) {
1161d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      scale = it->dip_to_pixel_scale;
1171d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      break;
1181d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    }
1191d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  }
1202873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  // If we are capturing cursor for a specific window then we need to figure out
1212873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  // if the current mouse position is covered by another window and also adjust
1222873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  // |position| to make it relative to the window origin.
1232873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  if (window_id_ != kCGNullWindowID) {
124678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org    CGWindowID on_screen_window = window_id_;
125678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org    if (full_screen_chrome_window_detector_) {
126678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org      CGWindowID full_screen_window =
127678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org          full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_);
128678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org
129678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org      if (full_screen_window != kCGNullWindowID)
130678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org        on_screen_window = full_screen_window;
131678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org    }
132678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org
133678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org    // Get list of windows that may be covering parts of |on_screen_window|.
1342873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    // CGWindowListCopyWindowInfo() returns windows in order from front to back,
135678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org    // so |on_screen_window| is expected to be the last in the list.
1362873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    CFArrayRef window_array =
1372873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly |
1382873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org                                       kCGWindowListOptionOnScreenAboveWindow |
1392873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org                                       kCGWindowListOptionIncludingWindow,
140678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org                                   on_screen_window);
1412873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    bool found_window = false;
1422873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    if (window_array) {
1432873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      CFIndex count = CFArrayGetCount(window_array);
1442873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      for (CFIndex i = 0; i < count; ++i) {
1452873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
1462873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            CFArrayGetValueAtIndex(window_array, i));
1472873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
1482873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        // Skip the Dock window. Dock window covers the whole screen, but it is
1492873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        // transparent.
1502873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        CFStringRef window_name = reinterpret_cast<CFStringRef>(
1512873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            CFDictionaryGetValue(window, kCGWindowName));
1522873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        if (window_name && CFStringCompare(window_name, CFSTR("Dock"), 0) == 0)
1532873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          continue;
1542873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
1552873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        CFDictionaryRef window_bounds = reinterpret_cast<CFDictionaryRef>(
1562873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            CFDictionaryGetValue(window, kCGWindowBounds));
1572873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        CFNumberRef window_number = reinterpret_cast<CFNumberRef>(
1582873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            CFDictionaryGetValue(window, kCGWindowNumber));
1592873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
1602873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        if (window_bounds && window_number) {
1612873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          CGRect gc_window_rect;
1622873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          if (!CGRectMakeWithDictionaryRepresentation(window_bounds,
1632873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org                                                      &gc_window_rect)) {
1642873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            continue;
1652873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          }
1662873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          DesktopRect window_rect =
1672873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org              DesktopRect::MakeXYWH(gc_window_rect.origin.x,
1682873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org                                    gc_window_rect.origin.y,
1692873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org                                    gc_window_rect.size.width,
1702873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org                                    gc_window_rect.size.height);
1712873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
1722873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          CGWindowID window_id;
1732873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          if (!CFNumberGetValue(window_number, kCFNumberIntType, &window_id))
1742873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            continue;
1752873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
176678f1909fc6918687aedd02949bcdf0f0e1ac682jiayl@webrtc.org          if (window_id == on_screen_window) {
1772873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            found_window = true;
1782873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            if (!window_rect.Contains(position))
1792873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org              state = OUTSIDE;
1802873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            position = position.subtract(window_rect.top_left());
1812873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
1822873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            assert(i == count - 1);
1832873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            break;
1842873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          } else if (window_rect.Contains(position)) {
1852873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            state = OUTSIDE;
1862873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            position.set(-1, -1);
1872873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org            break;
1882873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          }
1892873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org        }
1902873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      }
1912873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      CFRelease(window_array);
1922873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    }
1932873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    if (!found_window) {
1942873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      // If we failed to get list of windows or the window wasn't in the list
1952873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      // pretend that the cursor is outside the window. This can happen, e.g. if
1962873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      // the window was closed.
1972873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      state = OUTSIDE;
1982873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      position.set(-1, -1);
1992873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    }
2001d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  } else {
2011d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    assert(screen_id_ >= kFullDesktopScreenId);
2021d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    if (screen_id_ != kFullDesktopScreenId) {
2031d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      // For single screen capturing, convert the position to relative to the
2041d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      // target screen.
2051d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      const MacDisplayConfiguration* config =
2061d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org          configuration.FindDisplayConfigurationById(
2071d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org              static_cast<CGDirectDisplayID>(screen_id_));
2081d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      if (config) {
2091d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org        if (!config->pixel_bounds.Contains(position))
2101d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org          state = OUTSIDE;
2111d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org        position = position.subtract(config->bounds.top_left());
2121d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      } else {
2131d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org        // The target screen is no longer valid.
2141d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org        state = OUTSIDE;
2151d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org        position.set(-1, -1);
2161d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org      }
2171d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    } else {
218998d063211cf856d91181e16d6eb303a1a9fd229sergeyu@chromium.org      position.subtract(configuration.bounds.top_left());
2191d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    }
2201d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  }
2211d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  if (state == INSIDE) {
2221d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    // Convert Density Independent Pixel to physical pixel.
2231d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org    position = DesktopVector(round(position.x() * scale),
2241d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org                             round(position.y() * scale));
2252873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  }
2262873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  callback_->OnMouseCursorPosition(state, position);
2272873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org}
2282873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2292873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.orgvoid MouseCursorMonitorMac::CaptureImage() {
2302873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  NSCursor* nscursor = [NSCursor currentSystemCursor];
2312873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2322873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  NSImage* nsimage = [nscursor image];
2332873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  NSSize nssize = [nsimage size];
2342873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  DesktopSize size(nssize.width, nssize.height);
2352873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  NSPoint nshotspot = [nscursor hotSpot];
2362873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  DesktopVector hotspot(
23759fdf2d7bed0c080fa35677170ec9f02df30c2cbsergeyu@chromium.org      std::max(0, std::min(size.width(), static_cast<int>(nshotspot.x))),
23859fdf2d7bed0c080fa35677170ec9f02df30c2cbsergeyu@chromium.org      std::max(0, std::min(size.height(), static_cast<int>(nshotspot.y))));
2392873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CGImageRef cg_image =
2402873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      [nsimage CGImageForProposedRect:NULL context:nil hints:nil];
2412873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  if (!cg_image)
2422873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    return;
2432873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2442873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 ||
2452873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      CGImageGetBytesPerRow(cg_image) !=
2462873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org          static_cast<size_t>(DesktopFrame::kBytesPerPixel * size.width()) ||
2472873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      CGImageGetBitsPerComponent(cg_image) != 8) {
2482873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    return;
2492873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  }
2502873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2512873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CGDataProviderRef provider = CGImageGetDataProvider(cg_image);
2522873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CFDataRef image_data_ref = CGDataProviderCopyData(provider);
2532873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  if (image_data_ref == NULL)
2542873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    return;
2552873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2562873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  const uint8_t* src_data =
2572873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(image_data_ref));
2582873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2592873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  // Compare the cursor with the previous one.
2602873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  if (last_cursor_.get() &&
261eb9ce11063d614e6f3ac6d7dfb81eceb79329bc1sergeyu@chromium.org      last_cursor_->image()->size().equals(size) &&
2622873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      last_cursor_->hotspot().equals(hotspot) &&
263eb9ce11063d614e6f3ac6d7dfb81eceb79329bc1sergeyu@chromium.org      memcmp(last_cursor_->image()->data(), src_data,
264eb9ce11063d614e6f3ac6d7dfb81eceb79329bc1sergeyu@chromium.org             last_cursor_->image()->stride() * size.height()) == 0) {
2655f5cbf2dae26ba0eaf13158d831691d2356dda90sergeyu@chromium.org    CFRelease(image_data_ref);
2662873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org    return;
2672873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  }
2682873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2692873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  // Create a MouseCursor that describes the cursor and pass it to
2702873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  // the client.
2712873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  scoped_ptr<DesktopFrame> image(
2722873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org      new BasicDesktopFrame(DesktopSize(size.width(), size.height())));
2732873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  memcpy(image->data(), src_data,
2742873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org         size.width() * size.height() * DesktopFrame::kBytesPerPixel);
2752873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2762873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  CFRelease(image_data_ref);
2772873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2782873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  scoped_ptr<MouseCursor> cursor(new MouseCursor(image.release(), hotspot));
2792873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  last_cursor_.reset(MouseCursor::CopyOf(*cursor));
2802873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
2812873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org  callback_->OnMouseCursor(cursor.release());
2822873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org}
2832873c4c23513c84d6c822562c07c5e31fc3aa7bbsergeyu@chromium.org
284af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.orgMouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
285af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org    const DesktopCaptureOptions& options, WindowId window) {
2861d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  return new MouseCursorMonitorMac(options, window, kInvalidScreenId);
287af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org}
288af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org
289af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.orgMouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
290a2c26549012b8024442fe0920d0325aa853d7890jiayl@webrtc.org    const DesktopCaptureOptions& options,
291a2c26549012b8024442fe0920d0325aa853d7890jiayl@webrtc.org    ScreenId screen) {
2921d23d5eec85f4b124b4941f7d583e52a85ad5deejiayl@webrtc.org  return new MouseCursorMonitorMac(options, kCGNullWindowID, screen);
293af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org}
294af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org
295af54d4b3a12f79ebcfe10695a8ec2b1da80ab1f4sergeyu@chromium.org}  // namespace webrtc
296