blob: a50743cc6e6fbce078ab10cc291103fcabf0e436 [file] [log] [blame]
/*
* 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 <string.h>
#include <memory>
#include "modules/desktop_capture/desktop_capture_types.h"
#include "modules/desktop_capture/desktop_frame.h"
#include "modules/desktop_capture/desktop_geometry.h"
#include "modules/desktop_capture/mouse_cursor.h"
#include "modules/desktop_capture/win/cursor.h"
#include "modules/desktop_capture/win/screen_capture_utils.h"
#include "modules/desktop_capture/win/window_capture_utils.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
bool IsSameCursorShape(const CURSORINFO& left, const CURSORINFO& right) {
// If the cursors are not showing, we do not care the hCursor handle.
return left.flags == right.flags &&
(left.flags != CURSOR_SHOWING ||
left.hCursor == right.hCursor);
}
} // namespace
class MouseCursorMonitorWin : public MouseCursorMonitor {
public:
explicit MouseCursorMonitorWin(HWND window);
explicit MouseCursorMonitorWin(ScreenId screen);
~MouseCursorMonitorWin() override;
void Init(Callback* callback, Mode mode) override;
void Capture() override;
private:
// Get the rect of the currently selected screen, relative to the primary
// display's top-left. If the screen is disabled or disconnected, or any error
// happens, an empty rect is returned.
DesktopRect GetScreenRect();
HWND window_;
ScreenId screen_;
Callback* callback_;
Mode mode_;
HDC desktop_dc_;
// The last CURSORINFO (converted to MouseCursor) we have sent to the client.
CURSORINFO last_cursor_;
};
MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window)
: window_(window),
screen_(kInvalidScreenId),
callback_(NULL),
mode_(SHAPE_AND_POSITION),
desktop_dc_(NULL) {
memset(&last_cursor_, 0, sizeof(CURSORINFO));
}
MouseCursorMonitorWin::MouseCursorMonitorWin(ScreenId screen)
: window_(NULL),
screen_(screen),
callback_(NULL),
mode_(SHAPE_AND_POSITION),
desktop_dc_(NULL) {
assert(screen >= kFullDesktopScreenId);
memset(&last_cursor_, 0, sizeof(CURSORINFO));
}
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)) {
RTC_LOG_F(LS_ERROR) << "Unable to get cursor info. Error = "
<< GetLastError();
return;
}
if (!IsSameCursorShape(cursor_info, last_cursor_)) {
if (cursor_info.flags == CURSOR_SUPPRESSED) {
// The cursor is intentionally hidden now, send an empty bitmap.
last_cursor_ = cursor_info;
callback_->OnMouseCursor(new MouseCursor(
new BasicDesktopFrame(DesktopSize()), DesktopVector()));
} else {
// According to MSDN https://goo.gl/u6gyuC, HCURSOR instances returned by
// functions other than CreateCursor do not need to be actively destroyed.
// And CloseHandle function (https://goo.gl/ja5ycW) does not close a
// cursor, so assume a HCURSOR does not need to be closed.
if (cursor_info.flags == 0) {
// Host machine does not have a hardware mouse attached, we will send a
// default one instead.
// Note, Windows automatically caches cursor resource, so we do not need
// to cache the result of LoadCursor.
cursor_info.hCursor = LoadCursor(nullptr, IDC_ARROW);
}
std::unique_ptr<MouseCursor> cursor(
CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
if (cursor) {
last_cursor_ = cursor_info;
callback_->OnMouseCursor(cursor.release());
}
}
}
if (mode_ != SHAPE_AND_POSITION)
return;
// CURSORINFO::ptScreenPos is in full desktop coordinate.
DesktopVector position(cursor_info.ptScreenPos.x, cursor_info.ptScreenPos.y);
bool inside = cursor_info.flags == CURSOR_SHOWING;
if (window_) {
DesktopRect original_rect;
DesktopRect cropped_rect;
if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) {
position.set(0, 0);
inside = false;
} else {
if (inside) {
HWND windowUnderCursor = WindowFromPoint(cursor_info.ptScreenPos);
inside = windowUnderCursor ?
(window_ == GetAncestor(windowUnderCursor, GA_ROOT)) : false;
}
position = position.subtract(cropped_rect.top_left());
}
} else {
assert(screen_ != kInvalidScreenId);
DesktopRect rect = GetScreenRect();
if (inside)
inside = rect.Contains(position);
position = position.subtract(rect.top_left());
}
// TODO(zijiehe): Remove this overload.
callback_->OnMouseCursorPosition(inside ? INSIDE : OUTSIDE, position);
callback_->OnMouseCursorPosition(position);
}
DesktopRect MouseCursorMonitorWin::GetScreenRect() {
assert(screen_ != kInvalidScreenId);
if (screen_ == kFullDesktopScreenId) {
return DesktopRect::MakeXYWH(
GetSystemMetrics(SM_XVIRTUALSCREEN),
GetSystemMetrics(SM_YVIRTUALSCREEN),
GetSystemMetrics(SM_CXVIRTUALSCREEN),
GetSystemMetrics(SM_CYVIRTUALSCREEN));
}
DISPLAY_DEVICE device;
device.cb = sizeof(device);
BOOL result = EnumDisplayDevices(NULL, screen_, &device, 0);
if (!result)
return DesktopRect();
DEVMODE device_mode;
device_mode.dmSize = sizeof(device_mode);
device_mode.dmDriverExtra = 0;
result = EnumDisplaySettingsEx(
device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
if (!result)
return DesktopRect();
return DesktopRect::MakeXYWH(device_mode.dmPosition.x,
device_mode.dmPosition.y,
device_mode.dmPelsWidth,
device_mode.dmPelsHeight);
}
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
const DesktopCaptureOptions& options, WindowId window) {
return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
}
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
const DesktopCaptureOptions& options,
ScreenId screen) {
return new MouseCursorMonitorWin(screen);
}
std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
const DesktopCaptureOptions& options) {
return std::unique_ptr<MouseCursorMonitor>(
CreateForScreen(options, kFullDesktopScreenId));
}
} // namespace webrtc