MouseCursorMonitor implementation for OSX and Windows.
BUG=crbug.com/173265 R=wez@chromium.org Review URL: https://webrtc-codereview.appspot.com/2388004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4994 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
6b426ba5c5
commit
2df89c0c8b
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
DesktopFrame::DesktopFrame(DesktopSize size,
|
DesktopFrame::DesktopFrame(DesktopSize size,
|
||||||
@ -35,6 +37,20 @@ BasicDesktopFrame::~BasicDesktopFrame() {
|
|||||||
delete[] data_;
|
delete[] data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DesktopFrame* BasicDesktopFrame::CopyOf(const DesktopFrame& frame) {
|
||||||
|
DesktopFrame* result = new BasicDesktopFrame(frame.size());
|
||||||
|
for (int y = 0; y < frame.size().height(); ++y) {
|
||||||
|
memcpy(result->data() + y * result->stride(),
|
||||||
|
frame.data() + y * frame.stride(),
|
||||||
|
frame.size().width() * kBytesPerPixel);
|
||||||
|
}
|
||||||
|
result->set_dpi(frame.dpi());
|
||||||
|
result->set_capture_time_ms(frame.capture_time_ms());
|
||||||
|
*result->mutable_updated_region() = frame.updated_region();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(
|
SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(
|
||||||
DesktopSize size,
|
DesktopSize size,
|
||||||
int stride,
|
int stride,
|
||||||
|
@ -83,6 +83,9 @@ class BasicDesktopFrame : public DesktopFrame {
|
|||||||
explicit BasicDesktopFrame(DesktopSize size);
|
explicit BasicDesktopFrame(DesktopSize size);
|
||||||
virtual ~BasicDesktopFrame();
|
virtual ~BasicDesktopFrame();
|
||||||
|
|
||||||
|
// Creates a BasicDesktopFrame that contains copy of |frame|.
|
||||||
|
static DesktopFrame* CopyOf(const DesktopFrame& frame);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(BasicDesktopFrame);
|
DISALLOW_COPY_AND_ASSIGN(BasicDesktopFrame);
|
||||||
};
|
};
|
||||||
|
@ -23,4 +23,10 @@ MouseCursor::MouseCursor(DesktopFrame* image, const DesktopVector& hotspot)
|
|||||||
|
|
||||||
MouseCursor::~MouseCursor() {}
|
MouseCursor::~MouseCursor() {}
|
||||||
|
|
||||||
|
// static
|
||||||
|
MouseCursor* MouseCursor::CopyOf(const MouseCursor& cursor) {
|
||||||
|
return new MouseCursor(BasicDesktopFrame::CopyOf(cursor.image()),
|
||||||
|
cursor.hotspot());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -25,8 +25,10 @@ class MouseCursor {
|
|||||||
MouseCursor(DesktopFrame* image, const DesktopVector& hotspot);
|
MouseCursor(DesktopFrame* image, const DesktopVector& hotspot);
|
||||||
~MouseCursor();
|
~MouseCursor();
|
||||||
|
|
||||||
const DesktopFrame& image() { return *image_; }
|
static MouseCursor* CopyOf(const MouseCursor& cursor);
|
||||||
const DesktopVector& hotspot() { return hotspot_; }
|
|
||||||
|
const DesktopFrame& image() const { return *image_; }
|
||||||
|
const DesktopVector& hotspot() const { return hotspot_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
scoped_ptr<DesktopFrame> image_;
|
scoped_ptr<DesktopFrame> image_;
|
||||||
|
@ -10,19 +10,209 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
|
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <assert.h>
|
||||||
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
#include <Cocoa/Cocoa.h>
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||||
|
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
// TODO(sergeyu): Implement MouseCursorMonitor for Mac.
|
class MouseCursorMonitorMac : public MouseCursorMonitor {
|
||||||
|
public:
|
||||||
|
MouseCursorMonitorMac(CGWindowID window_id);
|
||||||
|
virtual ~MouseCursorMonitorMac();
|
||||||
|
|
||||||
|
virtual void Init(Callback* callback, Mode mode) OVERRIDE;
|
||||||
|
virtual void Capture() OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CaptureImage();
|
||||||
|
|
||||||
|
CGWindowID window_id_;
|
||||||
|
|
||||||
|
Callback* callback_;
|
||||||
|
Mode mode_;
|
||||||
|
|
||||||
|
scoped_ptr<MouseCursor> last_cursor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
MouseCursorMonitorMac::MouseCursorMonitorMac(CGWindowID window_id)
|
||||||
|
: window_id_(window_id),
|
||||||
|
callback_(NULL),
|
||||||
|
mode_(SHAPE_AND_POSITION) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
|
||||||
|
|
||||||
|
void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) {
|
||||||
|
assert(!callback_);
|
||||||
|
assert(callback);
|
||||||
|
|
||||||
|
callback_ = callback;
|
||||||
|
mode_ = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MouseCursorMonitorMac::Capture() {
|
||||||
|
assert(callback_);
|
||||||
|
|
||||||
|
CaptureImage();
|
||||||
|
|
||||||
|
if (mode_ != SHAPE_AND_POSITION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CursorState state = INSIDE;
|
||||||
|
|
||||||
|
CGEventRef event = CGEventCreate(NULL);
|
||||||
|
CGPoint gc_position = CGEventGetLocation(event);
|
||||||
|
CFRelease(event);
|
||||||
|
|
||||||
|
DesktopVector position(gc_position.x, gc_position.y);
|
||||||
|
|
||||||
|
// If we are capturing cursor for a specific window then we need to figure out
|
||||||
|
// if the current mouse position is covered by another window and also adjust
|
||||||
|
// |position| to make it relative to the window origin.
|
||||||
|
if (window_id_ != kCGNullWindowID) {
|
||||||
|
// Get list of windows that may be covering parts of |window_id_|.
|
||||||
|
// CGWindowListCopyWindowInfo() returns windows in order from front to back,
|
||||||
|
// so |window_id_| is expected to be the last in the list.
|
||||||
|
CFArrayRef window_array =
|
||||||
|
CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly |
|
||||||
|
kCGWindowListOptionOnScreenAboveWindow |
|
||||||
|
kCGWindowListOptionIncludingWindow,
|
||||||
|
window_id_);
|
||||||
|
bool found_window = false;
|
||||||
|
if (window_array) {
|
||||||
|
CFIndex count = CFArrayGetCount(window_array);
|
||||||
|
for (CFIndex i = 0; i < count; ++i) {
|
||||||
|
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
|
||||||
|
CFArrayGetValueAtIndex(window_array, i));
|
||||||
|
|
||||||
|
// Skip the Dock window. Dock window covers the whole screen, but it is
|
||||||
|
// transparent.
|
||||||
|
CFStringRef window_name = reinterpret_cast<CFStringRef>(
|
||||||
|
CFDictionaryGetValue(window, kCGWindowName));
|
||||||
|
if (window_name && CFStringCompare(window_name, CFSTR("Dock"), 0) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CFDictionaryRef window_bounds = reinterpret_cast<CFDictionaryRef>(
|
||||||
|
CFDictionaryGetValue(window, kCGWindowBounds));
|
||||||
|
CFNumberRef window_number = reinterpret_cast<CFNumberRef>(
|
||||||
|
CFDictionaryGetValue(window, kCGWindowNumber));
|
||||||
|
|
||||||
|
if (window_bounds && window_number) {
|
||||||
|
CGRect gc_window_rect;
|
||||||
|
if (!CGRectMakeWithDictionaryRepresentation(window_bounds,
|
||||||
|
&gc_window_rect)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DesktopRect window_rect =
|
||||||
|
DesktopRect::MakeXYWH(gc_window_rect.origin.x,
|
||||||
|
gc_window_rect.origin.y,
|
||||||
|
gc_window_rect.size.width,
|
||||||
|
gc_window_rect.size.height);
|
||||||
|
|
||||||
|
CGWindowID window_id;
|
||||||
|
if (!CFNumberGetValue(window_number, kCFNumberIntType, &window_id))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (window_id == window_id_) {
|
||||||
|
found_window = true;
|
||||||
|
if (!window_rect.Contains(position))
|
||||||
|
state = OUTSIDE;
|
||||||
|
position = position.subtract(window_rect.top_left());
|
||||||
|
|
||||||
|
assert(i == count - 1);
|
||||||
|
break;
|
||||||
|
} else if (window_rect.Contains(position)) {
|
||||||
|
state = OUTSIDE;
|
||||||
|
position.set(-1, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(window_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_window) {
|
||||||
|
// If we failed to get list of windows or the window wasn't in the list
|
||||||
|
// pretend that the cursor is outside the window. This can happen, e.g. if
|
||||||
|
// the window was closed.
|
||||||
|
state = OUTSIDE;
|
||||||
|
position.set(-1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_->OnMouseCursorPosition(state, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MouseCursorMonitorMac::CaptureImage() {
|
||||||
|
NSCursor* nscursor = [NSCursor currentSystemCursor];
|
||||||
|
|
||||||
|
NSImage* nsimage = [nscursor image];
|
||||||
|
NSSize nssize = [nsimage size];
|
||||||
|
DesktopSize size(nssize.width, nssize.height);
|
||||||
|
NSPoint nshotspot = [nscursor hotSpot];
|
||||||
|
DesktopVector hotspot(
|
||||||
|
std::min(0, std::max(size.width(), static_cast<int>(nshotspot.x))),
|
||||||
|
std::min(0, std::max(size.height(), static_cast<int>(nshotspot.y))));
|
||||||
|
CGImageRef cg_image =
|
||||||
|
[nsimage CGImageForProposedRect:NULL context:nil hints:nil];
|
||||||
|
if (!cg_image)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 ||
|
||||||
|
CGImageGetBytesPerRow(cg_image) !=
|
||||||
|
static_cast<size_t>(DesktopFrame::kBytesPerPixel * size.width()) ||
|
||||||
|
CGImageGetBitsPerComponent(cg_image) != 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGDataProviderRef provider = CGImageGetDataProvider(cg_image);
|
||||||
|
CFDataRef image_data_ref = CGDataProviderCopyData(provider);
|
||||||
|
if (image_data_ref == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint8_t* src_data =
|
||||||
|
reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(image_data_ref));
|
||||||
|
|
||||||
|
// Compare the cursor with the previous one.
|
||||||
|
if (last_cursor_.get() &&
|
||||||
|
last_cursor_->image().size().equals(size) &&
|
||||||
|
last_cursor_->hotspot().equals(hotspot) &&
|
||||||
|
memcmp(last_cursor_->image().data(), src_data,
|
||||||
|
last_cursor_->image().stride() * size.height()) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a MouseCursor that describes the cursor and pass it to
|
||||||
|
// the client.
|
||||||
|
scoped_ptr<DesktopFrame> image(
|
||||||
|
new BasicDesktopFrame(DesktopSize(size.width(), size.height())));
|
||||||
|
memcpy(image->data(), src_data,
|
||||||
|
size.width() * size.height() * DesktopFrame::kBytesPerPixel);
|
||||||
|
|
||||||
|
CFRelease(image_data_ref);
|
||||||
|
|
||||||
|
scoped_ptr<MouseCursor> cursor(new MouseCursor(image.release(), hotspot));
|
||||||
|
last_cursor_.reset(MouseCursor::CopyOf(*cursor));
|
||||||
|
|
||||||
|
callback_->OnMouseCursor(cursor.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
||||||
const DesktopCaptureOptions& options, WindowId window) {
|
const DesktopCaptureOptions& options, WindowId window) {
|
||||||
return NULL;
|
return new MouseCursorMonitorMac(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
||||||
const DesktopCaptureOptions& options) {
|
const DesktopCaptureOptions& options) {
|
||||||
return NULL;
|
return new MouseCursorMonitorMac(kCGNullWindowID);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -46,8 +46,11 @@ class MouseCursorMonitorTest : public testing::Test,
|
|||||||
bool position_received_;
|
bool position_received_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(sergeyu): Enable tests on all platforms.
|
// TODO(sergeyu): On Mac we need to initialize NSApplication before running the
|
||||||
#if defined(USE_X11)
|
// tests. Figure out how to do that without breaking other tests in
|
||||||
|
// modules_unittests and enable these tests on Mac.
|
||||||
|
// https://code.google.com/p/webrtc/issues/detail?id=2532
|
||||||
|
#if !defined(WEBRTC_MAC)
|
||||||
#define MAYBE(x) x
|
#define MAYBE(x) x
|
||||||
#else
|
#else
|
||||||
#define MAYBE(x) DISABLED_##x
|
#define MAYBE(x) DISABLED_##x
|
||||||
|
@ -10,19 +10,103 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
|
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/cursor.h"
|
||||||
|
#include "webrtc/system_wrappers/interface/logging.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
// TODO(sergeyu): Implement MouseCursorMonitor for Windows.
|
class MouseCursorMonitorWin : public MouseCursorMonitor {
|
||||||
|
public:
|
||||||
|
explicit MouseCursorMonitorWin(HWND window);
|
||||||
|
virtual ~MouseCursorMonitorWin();
|
||||||
|
|
||||||
|
virtual void Init(Callback* callback, Mode mode) OVERRIDE;
|
||||||
|
virtual void Capture() OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
HWND window_;
|
||||||
|
|
||||||
|
Callback* callback_;
|
||||||
|
Mode mode_;
|
||||||
|
|
||||||
|
HDC desktop_dc_;
|
||||||
|
|
||||||
|
HCURSOR last_cursor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window)
|
||||||
|
: window_(window),
|
||||||
|
callback_(NULL),
|
||||||
|
mode_(SHAPE_AND_POSITION),
|
||||||
|
desktop_dc_(NULL),
|
||||||
|
last_cursor_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseCursorMonitorWin::~MouseCursorMonitorWin() {
|
||||||
|
if (desktop_dc_)
|
||||||
|
ReleaseDC(NULL, desktop_dc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) {
|
||||||
|
assert(!callback_);
|
||||||
|
assert(callback);
|
||||||
|
|
||||||
|
callback_ = callback;
|
||||||
|
mode_ = mode;
|
||||||
|
|
||||||
|
desktop_dc_ = GetDC(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MouseCursorMonitorWin::Capture() {
|
||||||
|
assert(callback_);
|
||||||
|
|
||||||
|
CURSORINFO cursor_info;
|
||||||
|
cursor_info.cbSize = sizeof(CURSORINFO);
|
||||||
|
if (!GetCursorInfo(&cursor_info)) {
|
||||||
|
LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_cursor_ != cursor_info.hCursor) {
|
||||||
|
last_cursor_ = cursor_info.hCursor;
|
||||||
|
// Note that |cursor_info.hCursor| does not need to be freed.
|
||||||
|
scoped_ptr<MouseCursor> cursor(
|
||||||
|
CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
|
||||||
|
if (cursor.get())
|
||||||
|
callback_->OnMouseCursor(cursor.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode_ != SHAPE_AND_POSITION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DesktopVector position(cursor_info.ptScreenPos.x, cursor_info.ptScreenPos.y);
|
||||||
|
bool inside = cursor_info.flags == CURSOR_SHOWING;
|
||||||
|
|
||||||
|
if (window_) {
|
||||||
|
RECT rect;
|
||||||
|
if (!GetWindowRect(window_, &rect)) {
|
||||||
|
position.set(0, 0);
|
||||||
|
inside = false;
|
||||||
|
} else {
|
||||||
|
position = position.subtract(DesktopVector(rect.left, rect.top));
|
||||||
|
if (inside)
|
||||||
|
inside = (window_ == WindowFromPoint(cursor_info.ptScreenPos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_->OnMouseCursorPosition(inside ? INSIDE : OUTSIDE, position);
|
||||||
|
}
|
||||||
|
|
||||||
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
||||||
const DesktopCaptureOptions& options, WindowId window) {
|
const DesktopCaptureOptions& options, WindowId window) {
|
||||||
return NULL;
|
return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
||||||
const DesktopCaptureOptions& options) {
|
const DesktopCaptureOptions& options) {
|
||||||
return NULL;
|
return new MouseCursorMonitorWin(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
|
#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
#include "webrtc/modules/desktop_capture/differ.h"
|
#include "webrtc/modules/desktop_capture/differ.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||||
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.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_capture_frame_queue.h"
|
||||||
#include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
|
#include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
|
||||||
@ -328,11 +329,19 @@ void ScreenCapturerWin::CaptureCursor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Note that |cursor_info.hCursor| does not need to be freed.
|
// Note that |cursor_info.hCursor| does not need to be freed.
|
||||||
scoped_ptr<MouseCursorShape> cursor(
|
scoped_ptr<MouseCursor> cursor_image(
|
||||||
CreateMouseCursorShapeFromCursor(desktop_dc_, cursor_info.hCursor));
|
CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
|
||||||
if (!cursor.get())
|
if (!cursor_image.get())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape);
|
||||||
|
cursor->hotspot = cursor_image->hotspot();
|
||||||
|
cursor->size = cursor_image->image().size();
|
||||||
|
cursor->data.assign(
|
||||||
|
cursor_image->image().data(),
|
||||||
|
cursor_image->image().data() +
|
||||||
|
cursor_image->image().stride() * DesktopFrame::kBytesPerPixel);
|
||||||
|
|
||||||
// Compare the current cursor with the last one we sent to the client. If
|
// Compare the current cursor with the last one we sent to the client. If
|
||||||
// they're the same, then don't bother sending the cursor again.
|
// they're the same, then don't bother sending the cursor again.
|
||||||
if (last_cursor_.size.equals(cursor->size) &&
|
if (last_cursor_.size.equals(cursor->size) &&
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
|
#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||||
#include "webrtc/system_wrappers/interface/compile_assert.h"
|
#include "webrtc/system_wrappers/interface/compile_assert.h"
|
||||||
#include "webrtc/system_wrappers/interface/logging.h"
|
#include "webrtc/system_wrappers/interface/logging.h"
|
||||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||||
@ -92,29 +93,24 @@ void AlphaMul(uint32_t* data, int width, int height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scans a 32bpp bitmap looking for any pixels with non-zero alpha component.
|
// Scans a 32bpp bitmap looking for any pixels with non-zero alpha component.
|
||||||
// |*has_alpha| is set to true if non-zero alpha is found. |stride| is expressed
|
// Returns true if non-zero alpha is found. |stride| is expressed in pixels.
|
||||||
// in pixels.
|
bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height) {
|
||||||
bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height,
|
|
||||||
bool* has_alpha) {
|
|
||||||
const RGBQUAD* plane = reinterpret_cast<const RGBQUAD*>(data);
|
const RGBQUAD* plane = reinterpret_cast<const RGBQUAD*>(data);
|
||||||
for (int y = 0; y < height; ++y) {
|
for (int y = 0; y < height; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
if (plane->rgbReserved != 0) {
|
if (plane->rgbReserved != 0)
|
||||||
*has_alpha = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
plane += 1;
|
plane += 1;
|
||||||
}
|
}
|
||||||
plane += stride - width;
|
plane += stride - width;
|
||||||
}
|
}
|
||||||
|
|
||||||
*has_alpha = false;
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor) {
|
||||||
ICONINFO iinfo;
|
ICONINFO iinfo;
|
||||||
if (!GetIconInfo(cursor, &iinfo)) {
|
if (!GetIconInfo(cursor, &iinfo)) {
|
||||||
LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = "
|
LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = "
|
||||||
@ -167,20 +163,18 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t* mask_plane = mask_data.get();
|
uint32_t* mask_plane = mask_data.get();
|
||||||
|
scoped_ptr<DesktopFrame> image(
|
||||||
scoped_array<uint32_t> color_data;
|
new BasicDesktopFrame(DesktopSize(width, height)));
|
||||||
uint32_t* color_plane = NULL;
|
|
||||||
int color_stride = 0;
|
|
||||||
bool has_alpha = false;
|
bool has_alpha = false;
|
||||||
|
|
||||||
if (is_color) {
|
if (is_color) {
|
||||||
|
image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
|
||||||
// Get the pixels from the color bitmap.
|
// Get the pixels from the color bitmap.
|
||||||
color_data.reset(new uint32_t[width * height]);
|
|
||||||
if (!GetDIBits(dc,
|
if (!GetDIBits(dc,
|
||||||
scoped_color,
|
scoped_color,
|
||||||
0,
|
0,
|
||||||
height,
|
height,
|
||||||
color_data.get(),
|
image->data(),
|
||||||
reinterpret_cast<BITMAPINFO*>(&bmi),
|
reinterpret_cast<BITMAPINFO*>(&bmi),
|
||||||
DIB_RGB_COLORS)) {
|
DIB_RGB_COLORS)) {
|
||||||
LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
|
LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
|
||||||
@ -188,30 +182,28 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
color_plane = color_data.get();
|
|
||||||
color_stride = width;
|
|
||||||
|
|
||||||
// GetDIBits() does not provide any indication whether the bitmap has alpha
|
// GetDIBits() does not provide any indication whether the bitmap has alpha
|
||||||
// channel, so we use HasAlphaChannel() below to find it out.
|
// channel, so we use HasAlphaChannel() below to find it out.
|
||||||
if (!HasAlphaChannel(color_plane, color_stride, width, height, &has_alpha))
|
has_alpha = HasAlphaChannel(reinterpret_cast<uint32_t*>(image->data()),
|
||||||
return NULL;
|
width, width, height);
|
||||||
} else {
|
} else {
|
||||||
// For non-color cursors, the mask contains both an AND and an XOR mask and
|
// For non-color cursors, the mask contains both an AND and an XOR mask and
|
||||||
// the height includes both. Thus, the width is correct, but we need to
|
// the height includes both. Thus, the width is correct, but we need to
|
||||||
// divide by 2 to get the correct mask height.
|
// divide by 2 to get the correct mask height.
|
||||||
height /= 2;
|
height /= 2;
|
||||||
|
|
||||||
|
image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
|
||||||
|
|
||||||
// The XOR mask becomes the color bitmap.
|
// The XOR mask becomes the color bitmap.
|
||||||
color_plane = mask_plane + (width * height);
|
memcpy(
|
||||||
color_stride = width;
|
image->data(), mask_plane + (width * height), image->stride() * width);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruct transparency from the mask if the color image does not has
|
// Reconstruct transparency from the mask if the color image does not has
|
||||||
// alpha channel.
|
// alpha channel.
|
||||||
if (!has_alpha) {
|
if (!has_alpha) {
|
||||||
bool add_outline = false;
|
bool add_outline = false;
|
||||||
uint32_t* color = color_plane;
|
uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
|
||||||
uint32_t* dst = color_plane;
|
|
||||||
uint32_t* mask = mask_plane;
|
uint32_t* mask = mask_plane;
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
@ -226,36 +218,32 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
|||||||
// with black. In this case, we also add an outline around the cursor
|
// with black. In this case, we also add an outline around the cursor
|
||||||
// so that it is visible against a dark background.
|
// so that it is visible against a dark background.
|
||||||
if (*mask == kPixelRgbWhite) {
|
if (*mask == kPixelRgbWhite) {
|
||||||
if (*color != 0) {
|
if (*dst != 0) {
|
||||||
add_outline = true;
|
add_outline = true;
|
||||||
*dst = kPixelRgbaBlack;
|
*dst = kPixelRgbaBlack;
|
||||||
} else {
|
} else {
|
||||||
*dst = kPixelRgbaTransparent;
|
*dst = kPixelRgbaTransparent;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*dst = kPixelRgbaBlack ^ *color;
|
*dst = kPixelRgbaBlack ^ *dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
++color;
|
|
||||||
++dst;
|
++dst;
|
||||||
++mask;
|
++mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (add_outline) {
|
if (add_outline) {
|
||||||
AddCursorOutline(width, height, color_plane);
|
AddCursorOutline(
|
||||||
|
width, height, reinterpret_cast<uint32_t*>(image->data()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-multiply the resulting pixels since MouseCursorShape uses premultiplied
|
// Pre-multiply the resulting pixels since MouseCursor uses premultiplied
|
||||||
// images.
|
// images.
|
||||||
AlphaMul(color_plane, width, height);
|
AlphaMul(reinterpret_cast<uint32_t*>(image->data()), width, height);
|
||||||
|
|
||||||
scoped_ptr<MouseCursorShape> result(new MouseCursorShape());
|
return new MouseCursor(
|
||||||
result->data.assign(reinterpret_cast<char*>(color_plane),
|
image.release(), DesktopVector(hotspot_x, hotspot_y));
|
||||||
height * width * kBytesPerPixel);
|
|
||||||
result->size.set(width, height);
|
|
||||||
result->hotspot.set(hotspot_x, hotspot_y);
|
|
||||||
return result.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -13,13 +13,12 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
// Converts a cursor into a |MouseCursorShape| instance.
|
class MouseCursor;
|
||||||
MouseCursorShape* CreateMouseCursorShapeFromCursor(
|
|
||||||
HDC dc, HCURSOR cursor);
|
// Converts an HCURSOR into a |MouseCursor| instance.
|
||||||
|
MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor);
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "testing/gmock/include/gmock/gmock.h"
|
#include "testing/gmock/include/gmock/gmock.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||||
#include "webrtc/modules/desktop_capture/win/cursor.h"
|
#include "webrtc/modules/desktop_capture/win/cursor.h"
|
||||||
#include "webrtc/modules/desktop_capture/win/cursor_unittest_resources.h"
|
#include "webrtc/modules/desktop_capture/win/cursor_unittest_resources.h"
|
||||||
#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
|
#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
|
||||||
@ -19,9 +21,9 @@ namespace webrtc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Loads |left| from resources, converts it to a |MouseCursorShape| instance
|
// Loads |left| from resources, converts it to a |MouseCursor| instance and
|
||||||
// and compares pixels with |right|. Returns true of MouseCursorShape bits
|
// compares pixels with |right|. Returns true of MouseCursor bits match |right|.
|
||||||
// match |right|. |right| must be a 32bpp cursor with alpha channel.
|
// |right| must be a 32bpp cursor with alpha channel.
|
||||||
bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
||||||
HMODULE instance = GetModuleHandle(NULL);
|
HMODULE instance = GetModuleHandle(NULL);
|
||||||
|
|
||||||
@ -32,8 +34,8 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
|||||||
|
|
||||||
// Convert |cursor| to |mouse_shape|.
|
// Convert |cursor| to |mouse_shape|.
|
||||||
HDC dc = GetDC(NULL);
|
HDC dc = GetDC(NULL);
|
||||||
scoped_ptr<MouseCursorShape> mouse_shape(
|
scoped_ptr<MouseCursor> mouse_shape(
|
||||||
CreateMouseCursorShapeFromCursor(dc, cursor));
|
CreateMouseCursorFromHCursor(dc, cursor));
|
||||||
ReleaseDC(NULL, dc);
|
ReleaseDC(NULL, dc);
|
||||||
|
|
||||||
EXPECT_TRUE(mouse_shape.get());
|
EXPECT_TRUE(mouse_shape.get());
|
||||||
@ -56,7 +58,7 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
|||||||
|
|
||||||
int width = bitmap_info.bmWidth;
|
int width = bitmap_info.bmWidth;
|
||||||
int height = bitmap_info.bmHeight;
|
int height = bitmap_info.bmHeight;
|
||||||
EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->size));
|
EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->image().size()));
|
||||||
|
|
||||||
// Get the pixels from |scoped_color|.
|
// Get the pixels from |scoped_color|.
|
||||||
int size = width * height;
|
int size = width * height;
|
||||||
@ -64,13 +66,13 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
|||||||
EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get()));
|
EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get()));
|
||||||
|
|
||||||
// Compare the 32bpp image in |mouse_shape| with the one loaded from |right|.
|
// Compare the 32bpp image in |mouse_shape| with the one loaded from |right|.
|
||||||
return memcmp(data.get(), mouse_shape->data.data(),
|
return memcmp(data.get(), mouse_shape->image().data(),
|
||||||
size * sizeof(uint32_t)) == 0;
|
size * sizeof(uint32_t)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(MouseCursorShapeTest, MatchCursors) {
|
TEST(MouseCursorTest, MatchCursors) {
|
||||||
EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP,
|
EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP,
|
||||||
IDD_CURSOR1_32BPP));
|
IDD_CURSOR1_32BPP));
|
||||||
|
|
||||||
|
@ -41,29 +41,6 @@ bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DesktopFrame that stores data in CFData.
|
|
||||||
class CFDataDesktopFrame : public DesktopFrame {
|
|
||||||
public:
|
|
||||||
// Consumes |cf_data| reference.
|
|
||||||
//
|
|
||||||
// TODO(sergeyu): Here we const_cast<> the buffer used in CFDataRef. CFDataRef
|
|
||||||
// buffer is immutable, but DesktopFrame is always mutable. This shouldn't be
|
|
||||||
// a problem because frames generated by WindowCapturers are normally not
|
|
||||||
// mutated. To avoid this hack consider making DesktopFrame immutable and add
|
|
||||||
// MutableDesktopFrame.
|
|
||||||
CFDataDesktopFrame(DesktopSize size, int stride, CFDataRef cf_data)
|
|
||||||
: DesktopFrame(size, stride,
|
|
||||||
const_cast<uint8_t*>(CFDataGetBytePtr(cf_data)), NULL),
|
|
||||||
cf_data_(cf_data) {
|
|
||||||
}
|
|
||||||
virtual ~CFDataDesktopFrame() {
|
|
||||||
CFRelease(cf_data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
CFDataRef cf_data_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class WindowCapturerMac : public WindowCapturer {
|
class WindowCapturerMac : public WindowCapturer {
|
||||||
public:
|
public:
|
||||||
WindowCapturerMac();
|
WindowCapturerMac();
|
||||||
@ -185,9 +162,18 @@ void WindowCapturerMac::Capture(const DesktopRegion& region) {
|
|||||||
int width = CGImageGetWidth(window_image);
|
int width = CGImageGetWidth(window_image);
|
||||||
int height = CGImageGetHeight(window_image);
|
int height = CGImageGetHeight(window_image);
|
||||||
CGDataProviderRef provider = CGImageGetDataProvider(window_image);
|
CGDataProviderRef provider = CGImageGetDataProvider(window_image);
|
||||||
DesktopFrame* frame = new CFDataDesktopFrame(
|
CFDataRef cf_data = CGDataProviderCopyData(provider);
|
||||||
DesktopSize(width, height), CGImageGetBytesPerRow(window_image),
|
DesktopFrame* frame = new BasicDesktopFrame(
|
||||||
CGDataProviderCopyData(provider));
|
DesktopSize(width, height));
|
||||||
|
|
||||||
|
int src_stride = CGImageGetBytesPerRow(window_image);
|
||||||
|
const uint8_t* src_data = CFDataGetBytePtr(cf_data);
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
|
||||||
|
DesktopFrame::kBytesPerPixel * width);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(cf_data);
|
||||||
CFRelease(window_image);
|
CFRelease(window_image);
|
||||||
|
|
||||||
callback_->OnCaptureCompleted(frame);
|
callback_->OnCaptureCompleted(frame);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user