mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-19 08:37:54 +01:00

This is a reland of 89653d5db4
Original change's description:
> [desktopCapture] Unify the position info in DIP coordinates on Mac.
>
> On OSX, the logical(DIP) and physical coordinates are used mixingly.
> For example, the captured image has its size in physical pixels(2x) and
> location in logical(DIP) pixels. Same to the cursor position. This
> causes trouble when we check the relative position of image and cursor
> when there are multiple monitors with different DIP setting connected.
>
> This cl proposed a solution to use DIP pixel for any location info,
> i.e. top-left of a frame and cursor position. Also propose a method to
> get the current scale factor of a window across multiple monitors. And
> save the current scale factor in DPI of the capture frame.
> Then we can check relative position of cursor and frame correctly
> in DIP pixel and compose them in physical pixel.
>
> Bug: webrtc:9178
> Change-Id: I3c076aeac2d6f2c1f63d000d7fff03500aa375ac
> Reviewed-on: https://webrtc-review.googlesource.com/71621
> Reviewed-by: Jamie Walch <jamiewalch@chromium.org>
> Reviewed-by: Zijie He <zijiehe@chromium.org>
> Commit-Queue: Brave Yao <braveyao@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#23263}
Bug: webrtc:9178
Change-Id: I97d9150f7b9a4ed6671733b75613ea9c315d5c1d
Reviewed-on: https://webrtc-review.googlesource.com/77481
Reviewed-by: Zijie He <zijiehe@chromium.org>
Commit-Queue: Brave Yao <braveyao@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23289}
223 lines
7.6 KiB
Text
223 lines
7.6 KiB
Text
/*
|
|
* Copyright (c) 2013 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 "modules/desktop_capture/mouse_cursor_monitor.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <ApplicationServices/ApplicationServices.h>
|
|
#include <Cocoa/Cocoa.h>
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
#include "modules/desktop_capture/desktop_capture_options.h"
|
|
#include "modules/desktop_capture/desktop_capture_types.h"
|
|
#include "modules/desktop_capture/desktop_frame.h"
|
|
#include "modules/desktop_capture/mac/desktop_configuration.h"
|
|
#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
|
|
#include "modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
|
|
#include "modules/desktop_capture/mac/window_list_utils.h"
|
|
#include "modules/desktop_capture/mouse_cursor.h"
|
|
#include "rtc_base/macutils.h"
|
|
#include "rtc_base/scoped_ref_ptr.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
CGImageRef CreateScaledCGImage(CGImageRef image, int width, int height) {
|
|
// Create context, keeping original image properties.
|
|
CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
|
|
CGContextRef context = CGBitmapContextCreate(nullptr,
|
|
width,
|
|
height,
|
|
CGImageGetBitsPerComponent(image),
|
|
width * DesktopFrame::kBytesPerPixel,
|
|
colorspace,
|
|
CGImageGetBitmapInfo(image));
|
|
|
|
if (!context) return nil;
|
|
|
|
// Draw image to context, resizing it.
|
|
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
|
|
// Extract resulting image from context.
|
|
CGImageRef imgRef = CGBitmapContextCreateImage(context);
|
|
CGContextRelease(context);
|
|
|
|
return imgRef;
|
|
}
|
|
} // namespace
|
|
|
|
class MouseCursorMonitorMac : public MouseCursorMonitor {
|
|
public:
|
|
MouseCursorMonitorMac(const DesktopCaptureOptions& options,
|
|
CGWindowID window_id,
|
|
ScreenId screen_id);
|
|
~MouseCursorMonitorMac() override;
|
|
|
|
void Init(Callback* callback, Mode mode) override;
|
|
void Capture() override;
|
|
|
|
private:
|
|
static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
|
|
CGDisplayChangeSummaryFlags flags,
|
|
void *user_parameter);
|
|
void DisplaysReconfigured(CGDirectDisplayID display,
|
|
CGDisplayChangeSummaryFlags flags);
|
|
|
|
void CaptureImage(float scale);
|
|
|
|
rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
|
|
CGWindowID window_id_;
|
|
ScreenId screen_id_;
|
|
Callback* callback_;
|
|
Mode mode_;
|
|
__strong NSImage* last_cursor_;
|
|
rtc::scoped_refptr<FullScreenChromeWindowDetector>
|
|
full_screen_chrome_window_detector_;
|
|
};
|
|
|
|
MouseCursorMonitorMac::MouseCursorMonitorMac(
|
|
const DesktopCaptureOptions& options,
|
|
CGWindowID window_id,
|
|
ScreenId screen_id)
|
|
: configuration_monitor_(options.configuration_monitor()),
|
|
window_id_(window_id),
|
|
screen_id_(screen_id),
|
|
callback_(NULL),
|
|
mode_(SHAPE_AND_POSITION),
|
|
full_screen_chrome_window_detector_(
|
|
options.full_screen_chrome_window_detector()) {
|
|
assert(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
|
|
}
|
|
|
|
MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
|
|
|
|
void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) {
|
|
assert(!callback_);
|
|
assert(callback);
|
|
|
|
callback_ = callback;
|
|
mode_ = mode;
|
|
}
|
|
|
|
void MouseCursorMonitorMac::Capture() {
|
|
assert(callback_);
|
|
|
|
CGEventRef event = CGEventCreate(NULL);
|
|
CGPoint gc_position = CGEventGetLocation(event);
|
|
CFRelease(event);
|
|
|
|
DesktopVector position(gc_position.x, gc_position.y);
|
|
|
|
configuration_monitor_->Lock();
|
|
MacDesktopConfiguration configuration =
|
|
configuration_monitor_->desktop_configuration();
|
|
configuration_monitor_->Unlock();
|
|
float scale = GetScaleFactorAtPosition(configuration, position);
|
|
|
|
CaptureImage(scale);
|
|
|
|
if (mode_ != SHAPE_AND_POSITION)
|
|
return;
|
|
|
|
// Always report cursor position in DIP pixel.
|
|
callback_->OnMouseCursorPosition(
|
|
position.subtract(configuration.bounds.top_left()));
|
|
}
|
|
|
|
void MouseCursorMonitorMac::CaptureImage(float scale) {
|
|
NSCursor* nscursor = [NSCursor currentSystemCursor];
|
|
|
|
NSImage* nsimage = [nscursor image];
|
|
if (nsimage == nil || !nsimage.isValid) {
|
|
return;
|
|
}
|
|
NSSize nssize = [nsimage size]; // DIP size
|
|
|
|
// No need to caputre cursor image if it's unchanged since last capture.
|
|
if ([[nsimage TIFFRepresentation] isEqual:[last_cursor_ TIFFRepresentation]]) return;
|
|
last_cursor_ = nsimage;
|
|
|
|
DesktopSize size(round(nssize.width * scale),
|
|
round(nssize.height * scale)); // Pixel size
|
|
NSPoint nshotspot = [nscursor hotSpot];
|
|
DesktopVector hotspot(
|
|
std::max(0,
|
|
std::min(size.width(), static_cast<int>(nshotspot.x * scale))),
|
|
std::max(0,
|
|
std::min(size.height(), static_cast<int>(nshotspot.y * scale))));
|
|
CGImageRef cg_image =
|
|
[nsimage CGImageForProposedRect:NULL context:nil hints:nil];
|
|
if (!cg_image)
|
|
return;
|
|
|
|
// Before 10.12, OSX may report 1X cursor on Retina screen. (See
|
|
// crbug.com/632995.) After 10.12, OSX may report 2X cursor on non-Retina
|
|
// screen. (See crbug.com/671436.) So scaling the cursor if needed.
|
|
CGImageRef scaled_cg_image = nil;
|
|
if (CGImageGetWidth(cg_image) != static_cast<size_t>(size.width())) {
|
|
scaled_cg_image = CreateScaledCGImage(cg_image, size.width(), size.height());
|
|
if (scaled_cg_image != nil) {
|
|
cg_image = scaled_cg_image;
|
|
}
|
|
}
|
|
if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 ||
|
|
CGImageGetWidth(cg_image) != static_cast<size_t>(size.width()) ||
|
|
CGImageGetBitsPerComponent(cg_image) != 8) {
|
|
if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
|
|
return;
|
|
}
|
|
|
|
CGDataProviderRef provider = CGImageGetDataProvider(cg_image);
|
|
CFDataRef image_data_ref = CGDataProviderCopyData(provider);
|
|
if (image_data_ref == NULL) {
|
|
if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
|
|
return;
|
|
}
|
|
|
|
const uint8_t* src_data =
|
|
reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(image_data_ref));
|
|
|
|
// Create a MouseCursor that describes the cursor and pass it to
|
|
// the client.
|
|
std::unique_ptr<DesktopFrame> image(
|
|
new BasicDesktopFrame(DesktopSize(size.width(), size.height())));
|
|
|
|
int src_stride = CGImageGetBytesPerRow(cg_image);
|
|
image->CopyPixelsFrom(src_data, src_stride, DesktopRect::MakeSize(size));
|
|
|
|
CFRelease(image_data_ref);
|
|
if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
|
|
|
|
std::unique_ptr<MouseCursor> cursor(
|
|
new MouseCursor(image.release(), hotspot));
|
|
|
|
callback_->OnMouseCursor(cursor.release());
|
|
}
|
|
|
|
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
|
const DesktopCaptureOptions& options, WindowId window) {
|
|
return new MouseCursorMonitorMac(options, window, kInvalidScreenId);
|
|
}
|
|
|
|
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
|
const DesktopCaptureOptions& options,
|
|
ScreenId screen) {
|
|
return new MouseCursorMonitorMac(options, kCGNullWindowID, screen);
|
|
}
|
|
|
|
std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
|
|
const DesktopCaptureOptions& options) {
|
|
return std::unique_ptr<MouseCursorMonitor>(
|
|
CreateForScreen(options, kFullDesktopScreenId));
|
|
}
|
|
|
|
} // namespace webrtc
|