mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00

When a display uses a scale factor (different than 1.0) the previous cursor position is not properly cleared during a CRD connection on ChromeOS (see b/235191365). The issue was that the fix for crbug.com/1323241 does not take device scaling into account, so that fix would incorrectly not mark the previous location of the mouse cursor as modified. Adding proper boundary checks is hard and risky though, as the way the position of the mouse cursor is reported seems to be platform dependent (ChromeOS vs Linux vs ...). So because crbug.com/1323241 only solves a theoretical crash that is rarely if ever hit in the field, I decided to for now undo the fix for crbug.com/1323241. A proper boundary check can then later be introduced without any pressure from a looming release Bug: chromium:1323241 Bug: b/235191365 Fixed: b/235191365 Test: Manually deployed Change-Id: Ib09b6cc5e396bd52538332edfc4395ed80c6786e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265391 Reviewed-by: Alexander Cooper <alcooper@chromium.org> Reviewed-by: Joe Downing <joedow@google.com> Commit-Queue: Jeroen Dhollander <jeroendh@google.com> Cr-Commit-Position: refs/heads/main@{#37274}
260 lines
9.5 KiB
C++
260 lines
9.5 KiB
C++
/*
|
|
* 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/desktop_and_cursor_composer.h"
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "modules/desktop_capture/desktop_capturer.h"
|
|
#include "modules/desktop_capture/desktop_frame.h"
|
|
#include "modules/desktop_capture/mouse_cursor.h"
|
|
#include "modules/desktop_capture/mouse_cursor_monitor.h"
|
|
#include "rtc_base/checks.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
// Helper function that blends one image into another. Source image must be
|
|
// pre-multiplied with the alpha channel. Destination is assumed to be opaque.
|
|
void AlphaBlend(uint8_t* dest,
|
|
int dest_stride,
|
|
const uint8_t* src,
|
|
int src_stride,
|
|
const DesktopSize& size) {
|
|
for (int y = 0; y < size.height(); ++y) {
|
|
for (int x = 0; x < size.width(); ++x) {
|
|
uint32_t base_alpha = 255 - src[x * DesktopFrame::kBytesPerPixel + 3];
|
|
if (base_alpha == 255) {
|
|
continue;
|
|
} else if (base_alpha == 0) {
|
|
memcpy(dest + x * DesktopFrame::kBytesPerPixel,
|
|
src + x * DesktopFrame::kBytesPerPixel,
|
|
DesktopFrame::kBytesPerPixel);
|
|
} else {
|
|
dest[x * DesktopFrame::kBytesPerPixel] =
|
|
dest[x * DesktopFrame::kBytesPerPixel] * base_alpha / 255 +
|
|
src[x * DesktopFrame::kBytesPerPixel];
|
|
dest[x * DesktopFrame::kBytesPerPixel + 1] =
|
|
dest[x * DesktopFrame::kBytesPerPixel + 1] * base_alpha / 255 +
|
|
src[x * DesktopFrame::kBytesPerPixel + 1];
|
|
dest[x * DesktopFrame::kBytesPerPixel + 2] =
|
|
dest[x * DesktopFrame::kBytesPerPixel + 2] * base_alpha / 255 +
|
|
src[x * DesktopFrame::kBytesPerPixel + 2];
|
|
}
|
|
}
|
|
src += src_stride;
|
|
dest += dest_stride;
|
|
}
|
|
}
|
|
|
|
// DesktopFrame wrapper that draws mouse on a frame and restores original
|
|
// content before releasing the underlying frame.
|
|
class DesktopFrameWithCursor : public DesktopFrame {
|
|
public:
|
|
// Takes ownership of `frame`.
|
|
DesktopFrameWithCursor(std::unique_ptr<DesktopFrame> frame,
|
|
const MouseCursor& cursor,
|
|
const DesktopVector& position,
|
|
const DesktopRect& previous_cursor_rect,
|
|
bool cursor_changed);
|
|
~DesktopFrameWithCursor() override;
|
|
|
|
DesktopFrameWithCursor(const DesktopFrameWithCursor&) = delete;
|
|
DesktopFrameWithCursor& operator=(const DesktopFrameWithCursor&) = delete;
|
|
|
|
DesktopRect cursor_rect() const { return cursor_rect_; }
|
|
|
|
private:
|
|
const std::unique_ptr<DesktopFrame> original_frame_;
|
|
|
|
DesktopVector restore_position_;
|
|
std::unique_ptr<DesktopFrame> restore_frame_;
|
|
DesktopRect cursor_rect_;
|
|
};
|
|
|
|
DesktopFrameWithCursor::DesktopFrameWithCursor(
|
|
std::unique_ptr<DesktopFrame> frame,
|
|
const MouseCursor& cursor,
|
|
const DesktopVector& position,
|
|
const DesktopRect& previous_cursor_rect,
|
|
bool cursor_changed)
|
|
: DesktopFrame(frame->size(),
|
|
frame->stride(),
|
|
frame->data(),
|
|
frame->shared_memory()),
|
|
original_frame_(std::move(frame)) {
|
|
MoveFrameInfoFrom(original_frame_.get());
|
|
|
|
DesktopVector image_pos = position.subtract(cursor.hotspot());
|
|
cursor_rect_ = DesktopRect::MakeSize(cursor.image()->size());
|
|
cursor_rect_.Translate(image_pos);
|
|
DesktopVector cursor_origin = cursor_rect_.top_left();
|
|
cursor_rect_.IntersectWith(DesktopRect::MakeSize(size()));
|
|
|
|
if (!previous_cursor_rect.equals(cursor_rect_)) {
|
|
mutable_updated_region()->AddRect(cursor_rect_);
|
|
// TODO(crbug:1323241) Update this code to properly handle the case where
|
|
// |previous_cursor_rect| is outside of the boundaries of |frame|.
|
|
// Any boundary check has to take into account the fact that
|
|
// |previous_cursor_rect| can be in DPI or in pixels, based on the platform
|
|
// we're running on.
|
|
mutable_updated_region()->AddRect(previous_cursor_rect);
|
|
} else if (cursor_changed) {
|
|
mutable_updated_region()->AddRect(cursor_rect_);
|
|
}
|
|
|
|
if (cursor_rect_.is_empty())
|
|
return;
|
|
|
|
// Copy original screen content under cursor to `restore_frame_`.
|
|
restore_position_ = cursor_rect_.top_left();
|
|
restore_frame_.reset(new BasicDesktopFrame(cursor_rect_.size()));
|
|
restore_frame_->CopyPixelsFrom(*this, cursor_rect_.top_left(),
|
|
DesktopRect::MakeSize(restore_frame_->size()));
|
|
|
|
// Blit the cursor.
|
|
uint8_t* cursor_rect_data =
|
|
reinterpret_cast<uint8_t*>(data()) + cursor_rect_.top() * stride() +
|
|
cursor_rect_.left() * DesktopFrame::kBytesPerPixel;
|
|
DesktopVector origin_shift = cursor_rect_.top_left().subtract(cursor_origin);
|
|
AlphaBlend(cursor_rect_data, stride(),
|
|
cursor.image()->data() +
|
|
origin_shift.y() * cursor.image()->stride() +
|
|
origin_shift.x() * DesktopFrame::kBytesPerPixel,
|
|
cursor.image()->stride(), cursor_rect_.size());
|
|
}
|
|
|
|
DesktopFrameWithCursor::~DesktopFrameWithCursor() {
|
|
// Restore original content of the frame.
|
|
if (restore_frame_) {
|
|
DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
|
|
target_rect.Translate(restore_position_);
|
|
CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
|
|
target_rect);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
DesktopAndCursorComposer::DesktopAndCursorComposer(
|
|
std::unique_ptr<DesktopCapturer> desktop_capturer,
|
|
const DesktopCaptureOptions& options)
|
|
: DesktopAndCursorComposer(desktop_capturer.release(),
|
|
MouseCursorMonitor::Create(options).release()) {}
|
|
|
|
DesktopAndCursorComposer::DesktopAndCursorComposer(
|
|
DesktopCapturer* desktop_capturer,
|
|
MouseCursorMonitor* mouse_monitor)
|
|
: desktop_capturer_(desktop_capturer), mouse_monitor_(mouse_monitor) {
|
|
RTC_DCHECK(desktop_capturer_);
|
|
}
|
|
|
|
DesktopAndCursorComposer::~DesktopAndCursorComposer() = default;
|
|
|
|
std::unique_ptr<DesktopAndCursorComposer>
|
|
DesktopAndCursorComposer::CreateWithoutMouseCursorMonitor(
|
|
std::unique_ptr<DesktopCapturer> desktop_capturer) {
|
|
return std::unique_ptr<DesktopAndCursorComposer>(
|
|
new DesktopAndCursorComposer(desktop_capturer.release(), nullptr));
|
|
}
|
|
|
|
void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) {
|
|
callback_ = callback;
|
|
if (mouse_monitor_)
|
|
mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
|
|
desktop_capturer_->Start(this);
|
|
}
|
|
|
|
void DesktopAndCursorComposer::SetSharedMemoryFactory(
|
|
std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
|
|
desktop_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
|
|
}
|
|
|
|
void DesktopAndCursorComposer::CaptureFrame() {
|
|
if (mouse_monitor_)
|
|
mouse_monitor_->Capture();
|
|
desktop_capturer_->CaptureFrame();
|
|
}
|
|
|
|
void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) {
|
|
desktop_capturer_->SetExcludedWindow(window);
|
|
}
|
|
|
|
bool DesktopAndCursorComposer::GetSourceList(SourceList* sources) {
|
|
return desktop_capturer_->GetSourceList(sources);
|
|
}
|
|
|
|
bool DesktopAndCursorComposer::SelectSource(SourceId id) {
|
|
return desktop_capturer_->SelectSource(id);
|
|
}
|
|
|
|
bool DesktopAndCursorComposer::FocusOnSelectedSource() {
|
|
return desktop_capturer_->FocusOnSelectedSource();
|
|
}
|
|
|
|
bool DesktopAndCursorComposer::IsOccluded(const DesktopVector& pos) {
|
|
return desktop_capturer_->IsOccluded(pos);
|
|
}
|
|
|
|
#if defined(WEBRTC_USE_GIO)
|
|
DesktopCaptureMetadata DesktopAndCursorComposer::GetMetadata() {
|
|
return desktop_capturer_->GetMetadata();
|
|
}
|
|
#endif // defined(WEBRTC_USE_GIO)
|
|
|
|
void DesktopAndCursorComposer::OnCaptureResult(
|
|
DesktopCapturer::Result result,
|
|
std::unique_ptr<DesktopFrame> frame) {
|
|
if (frame && cursor_) {
|
|
if (!frame->may_contain_cursor() &&
|
|
frame->rect().Contains(cursor_position_) &&
|
|
!desktop_capturer_->IsOccluded(cursor_position_)) {
|
|
DesktopVector relative_position =
|
|
cursor_position_.subtract(frame->top_left());
|
|
#if defined(WEBRTC_MAC) || defined(CHROMEOS)
|
|
// On OSX, the logical(DIP) and physical coordinates are used mixingly.
|
|
// For example, the captured cursor has its size in physical pixels(2x)
|
|
// and location in logical(DIP) pixels on Retina monitor. This will cause
|
|
// problem when the desktop is mixed with Retina and non-Retina monitors.
|
|
// So we use DIP pixel for all location info and compensate with the scale
|
|
// factor of current frame to the `relative_position`.
|
|
const float scale = frame->scale_factor();
|
|
relative_position.set(relative_position.x() * scale,
|
|
relative_position.y() * scale);
|
|
#endif
|
|
auto frame_with_cursor = std::make_unique<DesktopFrameWithCursor>(
|
|
std::move(frame), *cursor_, relative_position, previous_cursor_rect_,
|
|
cursor_changed_);
|
|
previous_cursor_rect_ = frame_with_cursor->cursor_rect();
|
|
cursor_changed_ = false;
|
|
frame = std::move(frame_with_cursor);
|
|
frame->set_may_contain_cursor(true);
|
|
}
|
|
}
|
|
|
|
callback_->OnCaptureResult(result, std::move(frame));
|
|
}
|
|
|
|
void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) {
|
|
cursor_changed_ = true;
|
|
cursor_.reset(cursor);
|
|
}
|
|
|
|
void DesktopAndCursorComposer::OnMouseCursorPosition(
|
|
const DesktopVector& position) {
|
|
cursor_position_ = position;
|
|
}
|
|
|
|
} // namespace webrtc
|