Backed out changeset e7acb4e12051 (bug 1893666) for causing xpcshell failures on...
[gecko.git] / third_party / libwebrtc / modules / desktop_capture / mouse_cursor_monitor_mac.mm
blob512103ab5e2874bba72f3e8db648d1aeacbeab22
1 /*
2  *  Copyright (c) 2013 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  */
11 #include "modules/desktop_capture/mouse_cursor_monitor.h"
14 #include <memory>
16 #include <ApplicationServices/ApplicationServices.h>
17 #include <Cocoa/Cocoa.h>
18 #include <CoreFoundation/CoreFoundation.h>
20 #include "api/scoped_refptr.h"
21 #include "modules/desktop_capture/desktop_capture_options.h"
22 #include "modules/desktop_capture/desktop_capture_types.h"
23 #include "modules/desktop_capture/desktop_frame.h"
24 #include "modules/desktop_capture/mac/desktop_configuration.h"
25 #include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
26 #include "modules/desktop_capture/mac/window_list_utils.h"
27 #include "modules/desktop_capture/mouse_cursor.h"
28 #include "rtc_base/checks.h"
30 namespace webrtc {
32 namespace {
33 CGImageRef CreateScaledCGImage(CGImageRef image, int width, int height) {
34   // Create context, keeping original image properties.
35   CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
36   CGContextRef context = CGBitmapContextCreate(nullptr,
37                                                width,
38                                                height,
39                                                CGImageGetBitsPerComponent(image),
40                                                width * DesktopFrame::kBytesPerPixel,
41                                                colorspace,
42                                                CGImageGetBitmapInfo(image));
44   if (!context) return nil;
46   // Draw image to context, resizing it.
47   CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
48   // Extract resulting image from context.
49   CGImageRef imgRef = CGBitmapContextCreateImage(context);
50   CGContextRelease(context);
52   return imgRef;
54 }  // namespace
56 class MouseCursorMonitorMac : public MouseCursorMonitor {
57  public:
58   MouseCursorMonitorMac(const DesktopCaptureOptions& options,
59                         CGWindowID window_id,
60                         ScreenId screen_id);
61   ~MouseCursorMonitorMac() override;
63   void Init(Callback* callback, Mode mode) override;
64   void Capture() override;
66  private:
67   static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
68                                            CGDisplayChangeSummaryFlags flags,
69                                            void *user_parameter);
70   void DisplaysReconfigured(CGDirectDisplayID display,
71                             CGDisplayChangeSummaryFlags flags);
73   void CaptureImage(float scale);
75   rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
76   CGWindowID window_id_;
77   ScreenId screen_id_;
78   Callback* callback_ = NULL;
79   Mode mode_;
80   __strong NSImage* last_cursor_ = NULL;
83 MouseCursorMonitorMac::MouseCursorMonitorMac(const DesktopCaptureOptions& options,
84                                              CGWindowID window_id,
85                                              ScreenId screen_id)
86     : configuration_monitor_(options.configuration_monitor()),
87       window_id_(window_id),
88       screen_id_(screen_id),
89       mode_(SHAPE_AND_POSITION) {
90   RTC_DCHECK(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
93 MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
95 void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) {
96   RTC_DCHECK(!callback_);
97   RTC_DCHECK(callback);
99   callback_ = callback;
100   mode_ = mode;
103 void MouseCursorMonitorMac::Capture() {
104   RTC_DCHECK(callback_);
106   CGEventRef event = CGEventCreate(NULL);
107   CGPoint gc_position = CGEventGetLocation(event);
108   CFRelease(event);
110   DesktopVector position(gc_position.x, gc_position.y);
112   MacDesktopConfiguration configuration =
113       configuration_monitor_->desktop_configuration();
114   float scale = GetScaleFactorAtPosition(configuration, position);
116   CaptureImage(scale);
118   if (mode_ != SHAPE_AND_POSITION)
119     return;
121   // Always report cursor position in DIP pixel.
122   callback_->OnMouseCursorPosition(
123       position.subtract(configuration.bounds.top_left()));
126 void MouseCursorMonitorMac::CaptureImage(float scale) {
127   NSCursor* nscursor = [NSCursor currentSystemCursor];
129   NSImage* nsimage = [nscursor image];
130   if (nsimage == nil || !nsimage.isValid) {
131     return;
132   }
133   NSSize nssize = [nsimage size];  // DIP size
135   // No need to caputre cursor image if it's unchanged since last capture.
136   if (last_cursor_ && [[nsimage TIFFRepresentation] isEqual:[last_cursor_ TIFFRepresentation]]) return;
137   last_cursor_ = nsimage;
139   DesktopSize size(round(nssize.width * scale),
140                    round(nssize.height * scale));  // Pixel size
141   NSPoint nshotspot = [nscursor hotSpot];
142   DesktopVector hotspot(
143       std::max(0,
144                std::min(size.width(), static_cast<int>(nshotspot.x * scale))),
145       std::max(0,
146                std::min(size.height(), static_cast<int>(nshotspot.y * scale))));
147   CGImageRef cg_image =
148       [nsimage CGImageForProposedRect:NULL context:nil hints:nil];
149   if (!cg_image)
150     return;
152   // Before 10.12, OSX may report 1X cursor on Retina screen. (See
153   // crbug.com/632995.) After 10.12, OSX may report 2X cursor on non-Retina
154   // screen. (See crbug.com/671436.) So scaling the cursor if needed.
155   CGImageRef scaled_cg_image = nil;
156   if (CGImageGetWidth(cg_image) != static_cast<size_t>(size.width())) {
157     scaled_cg_image = CreateScaledCGImage(cg_image, size.width(), size.height());
158     if (scaled_cg_image != nil) {
159       cg_image = scaled_cg_image;
160     }
161   }
162   if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 ||
163       CGImageGetWidth(cg_image) != static_cast<size_t>(size.width()) ||
164       CGImageGetBitsPerComponent(cg_image) != 8) {
165     if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
166     return;
167   }
169   CGDataProviderRef provider = CGImageGetDataProvider(cg_image);
170   CFDataRef image_data_ref = CGDataProviderCopyData(provider);
171   if (image_data_ref == NULL) {
172     if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
173     return;
174   }
176   const uint8_t* src_data =
177       reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(image_data_ref));
179   // Create a MouseCursor that describes the cursor and pass it to
180   // the client.
181   std::unique_ptr<DesktopFrame> image(
182       new BasicDesktopFrame(DesktopSize(size.width(), size.height())));
184   int src_stride = CGImageGetBytesPerRow(cg_image);
185   image->CopyPixelsFrom(src_data, src_stride, DesktopRect::MakeSize(size));
187   CFRelease(image_data_ref);
188   if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
190   std::unique_ptr<MouseCursor> cursor(
191       new MouseCursor(image.release(), hotspot));
193   callback_->OnMouseCursor(cursor.release());
196 MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
197     const DesktopCaptureOptions& options, WindowId window) {
198   return new MouseCursorMonitorMac(options, window, kInvalidScreenId);
201 MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
202     const DesktopCaptureOptions& options,
203     ScreenId screen) {
204   return new MouseCursorMonitorMac(options, kCGNullWindowID, screen);
207 std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
208     const DesktopCaptureOptions& options) {
209   return std::unique_ptr<MouseCursorMonitor>(
210       CreateForScreen(options, kFullDesktopScreenId));
213 }  // namespace webrtc