diff options
author | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-12 02:29:54 +0000 |
---|---|---|
committer | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-12 02:29:54 +0000 |
commit | ccfc03ae9539ab77944d91a6c02b4693bb7bcf4b (patch) | |
tree | 46d35fe86d85dd2463072e41d43a9a84c5117b7c /remoting | |
parent | f500eea61c29addbda7631032b3118cd19721613 (diff) | |
download | chromium_src-ccfc03ae9539ab77944d91a6c02b4693bb7bcf4b.zip chromium_src-ccfc03ae9539ab77944d91a6c02b4693bb7bcf4b.tar.gz chromium_src-ccfc03ae9539ab77944d91a6c02b4693bb7bcf4b.tar.bz2 |
Support remoting of all monitors on multi-monitor Windows systems
BUG=118109
TEST=Manually verify that all monitors are remoted when connecting to a multi-monitor Windows host, even if one or more monitors are configured to the left or above the primary monitor.
Review URL: https://chromiumcodereview.appspot.com/10699046
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146272 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/capturer_win.cc | 180 |
1 files changed, 63 insertions, 117 deletions
diff --git a/remoting/host/capturer_win.cc b/remoting/host/capturer_win.cc index cae0326..db48a5d 100644 --- a/remoting/host/capturer_win.cc +++ b/remoting/host/capturer_win.cc @@ -12,6 +12,7 @@ #include "base/scoped_native_library.h" #include "base/utf_string_conversions.h" #include "base/win/scoped_gdi_object.h" +#include "base/win/scoped_hdc.h" #include "remoting/base/capture_data.h" #include "remoting/host/capturer_helper.h" #include "remoting/host/desktop_win.h" @@ -75,20 +76,21 @@ class CapturerGdi : public Capturer { SkISize size; int bytes_per_pixel; int bytes_per_row; + int resource_generation; }; - // Make sure that the current buffer has the same size as the screen. - void UpdateBufferCapture(const SkISize& size); + // Make sure that the device contexts and the current bufffer match the screen + // configuration. + void PrepareCaptureResources(); - // Allocate memory for a buffer of a given size, freeing any memory previously - // allocated for that buffer. - void ReallocateBuffer(int buffer_index, const SkISize& size); + // Allocates the specified capture buffer using the current device contexts + // and desktop dimensions, releasing any pre-existing buffer. + void AllocateBuffer(int buffer_index); void CalculateInvalidRegion(); void CaptureRegion(const SkRegion& region, const CaptureCompletedCallback& callback); - void ReleaseBuffers(); // Generates an image in the current buffer. void CaptureImage(); @@ -99,9 +101,6 @@ class CapturerGdi : public Capturer { // Capture the current cursor shape. void CaptureCursor(); - // Gets the screen size. - SkISize GetScreenSize(); - // A thread-safe list of invalid rectangles, and the size of the most // recently captured screen. CapturerHelper helper_; @@ -121,15 +120,14 @@ class CapturerGdi : public Capturer { ScopedThreadDesktopWin desktop_; - // Gdi specific information about screen. - HWND desktop_window_; - HDC desktop_dc_; - HDC memory_dc_; - HBITMAP target_bitmap_[kNumBuffers]; + // GDI resources used for screen capture. + scoped_ptr<base::win::ScopedGetDC> desktop_dc_; + base::win::ScopedCreateDC memory_dc_; + base::win::ScopedBitmap target_bitmap_[kNumBuffers]; + int resource_generation_; - // The screen size attached to the device contexts through which the screen - // is captured. - SkISize dc_size_; + // Rectangle describing the bounds of the desktop device context. + SkIRect desktop_dc_rect_; // The current buffer with valid data for reading. int current_buffer_; @@ -153,20 +151,15 @@ static const int kBytesPerPixel = 4; CapturerGdi::CapturerGdi() : last_cursor_size_(SkISize::Make(0, 0)), - desktop_window_(NULL), - desktop_dc_(NULL), - memory_dc_(NULL), - dc_size_(SkISize::Make(0, 0)), + desktop_dc_rect_(SkIRect::MakeEmpty()), + resource_generation_(0), current_buffer_(0), pixel_format_(media::VideoFrame::RGB32), composition_func_(NULL) { - memset(target_bitmap_, 0, sizeof(target_bitmap_)); - memset(buffers_, 0, sizeof(buffers_)); ScreenConfigurationChanged(); } CapturerGdi::~CapturerGdi() { - ReleaseBuffers(); } media::VideoFrame::Format CapturerGdi::pixel_format() const { @@ -208,30 +201,6 @@ const SkISize& CapturerGdi::size_most_recent() const { return helper_.size_most_recent(); } -void CapturerGdi::ReleaseBuffers() { - for (int i = kNumBuffers - 1; i >= 0; i--) { - if (target_bitmap_[i]) { - DeleteObject(target_bitmap_[i]); - target_bitmap_[i] = NULL; - } - if (buffers_[i].data) { - DeleteObject(buffers_[i].data); - buffers_[i].data = NULL; - } - } - - if (desktop_dc_) { - ReleaseDC(desktop_window_, desktop_dc_); - desktop_window_ = NULL; - desktop_dc_ = NULL; - } - - if (memory_dc_) { - DeleteDC(memory_dc_); - memory_dc_ = NULL; - } -} - void CapturerGdi::Start( const CursorShapeChangedCallback& callback) { cursor_shape_changed_callback_ = callback; @@ -266,97 +235,78 @@ void CapturerGdi::ScreenConfigurationChanged() { // We poll for screen configuration changes, so ignore notifications. } -void CapturerGdi::UpdateBufferCapture(const SkISize& size) { +void CapturerGdi::PrepareCaptureResources() { // Switch to the desktop receiving user input if different from the current // one. scoped_ptr<DesktopWin> input_desktop = DesktopWin::GetInputDesktop(); if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { // Release GDI resources otherwise SetThreadDesktop will fail. - if (desktop_dc_) { - ReleaseDC(desktop_window_, desktop_dc_); - desktop_window_ = NULL; - desktop_dc_ = NULL; - } - - if (memory_dc_) { - DeleteDC(memory_dc_); - memory_dc_ = NULL; - } - - ReleaseBuffers(); + desktop_dc_.reset(); + memory_dc_.Set(NULL); // If SetThreadDesktop() fails, the thread is still assigned a desktop. - // So we can continue capture screen bits, just from a diffented desktop. + // So we can continue capture screen bits, just from the wrong desktop. desktop_.SetThreadDesktop(input_desktop.Pass()); } - // Make sure the DCs have the correct dimensions. - if (size != dc_size_) { - // TODO(simonmorris): screen dimensions changing isn't equivalent to needing - // a new DC, but it's good enough for now. - if (desktop_dc_) { - ReleaseDC(desktop_window_, desktop_dc_); - desktop_window_ = NULL; - desktop_dc_ = NULL; - } - - if (memory_dc_) { - DeleteDC(memory_dc_); - memory_dc_ = NULL; - } + // If the display bounds have changed then recreate GDI resources. + // TODO(wez): Also check for pixel format changes. + SkIRect screen_rect(SkIRect::MakeXYWH( + GetSystemMetrics(SM_XVIRTUALSCREEN), + GetSystemMetrics(SM_YVIRTUALSCREEN), + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN))); + if (screen_rect != desktop_dc_rect_) { + desktop_dc_.reset(); + memory_dc_.Set(NULL); + desktop_dc_rect_.setEmpty(); } - if (desktop_dc_ == NULL) { - DCHECK(desktop_window_ == NULL); - DCHECK(memory_dc_ == NULL); + // Create GDI device contexts to capture from the desktop into memory, and + // allocate buffers to capture into. + if (desktop_dc_.get() == NULL) { + DCHECK(memory_dc_.Get() == NULL); + + desktop_dc_.reset(new base::win::ScopedGetDC(NULL)); + memory_dc_.Set(CreateCompatibleDC(*desktop_dc_)); + desktop_dc_rect_ = screen_rect; - desktop_window_ = GetDesktopWindow(); - desktop_dc_ = GetDC(desktop_window_); - memory_dc_ = CreateCompatibleDC(desktop_dc_); - dc_size_ = size; + ++resource_generation_; } - // Make sure the current bitmap has the correct dimensions. - if (buffers_[current_buffer_].data == NULL || - size != buffers_[current_buffer_].size) { - ReallocateBuffer(current_buffer_, size); + // If the current buffer is from an older generation then allocate a new one. + // Note that we can't reallocate other buffers at this point, since the caller + // may still be reading from them. + if (resource_generation_ != buffers_[current_buffer_].resource_generation) { + AllocateBuffer(current_buffer_); InvalidateFullScreen(); } } -void CapturerGdi::ReallocateBuffer(int buffer_index, const SkISize& size) { - // Delete any previously constructed bitmap. - if (target_bitmap_[buffer_index]) { - DeleteObject(target_bitmap_[buffer_index]); - target_bitmap_[buffer_index] = NULL; - } - if (buffers_[buffer_index].data) { - DeleteObject(buffers_[buffer_index].data); - buffers_[buffer_index].data = NULL; - } - - // Create a bitmap to keep the desktop image. - int rounded_width = (size.width() + 3) & (~3); +void CapturerGdi::AllocateBuffer(int buffer_index) { + DCHECK(desktop_dc_.get() != NULL); + DCHECK(memory_dc_.Get() != NULL); - // Dimensions of screen. - pixel_format_ = media::VideoFrame::RGB32; - int bytes_per_row = rounded_width * kBytesPerPixel; + // Windows requires DIB sections' rows to start DWORD-aligned, which is + // implicit when working with RGB32 pixels. + DCHECK_EQ(pixel_format_, media::VideoFrame::RGB32); - // Create a device independent bitmap (DIB) that is the same size. + // Describe a device independent bitmap (DIB) that is the size of the desktop. BITMAPINFO bmi; memset(&bmi, 0, sizeof(bmi)); - bmi.bmiHeader.biHeight = -size.height(); - bmi.bmiHeader.biWidth = rounded_width; + bmi.bmiHeader.biHeight = -desktop_dc_rect_.height(); + bmi.bmiHeader.biWidth = desktop_dc_rect_.width(); bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = kBytesPerPixel * 8; bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biSizeImage = bytes_per_row * size.height(); + int bytes_per_row = desktop_dc_rect_.width() * kBytesPerPixel; + bmi.bmiHeader.biSizeImage = bytes_per_row * desktop_dc_rect_.height(); bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter; bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter; - // Create memory for the buffers. + // Create the DIB, and store a pointer to its pixel buffer. target_bitmap_[buffer_index] = - CreateDIBSection(desktop_dc_, &bmi, DIB_RGB_COLORS, + CreateDIBSection(*desktop_dc_, &bmi, DIB_RGB_COLORS, static_cast<void**>(&buffers_[buffer_index].data), NULL, 0); buffers_[buffer_index].size = SkISize::Make(bmi.bmiHeader.biWidth, @@ -423,15 +373,16 @@ void CapturerGdi::CaptureRegion(const SkRegion& region, } void CapturerGdi::CaptureImage() { - // Make sure the structures we use to capture the image have the correct size. - UpdateBufferCapture(GetScreenSize()); + // Make sure the GDI capture resources are up-to-date. + PrepareCaptureResources(); // Select the target bitmap into the memory dc. SelectObject(memory_dc_, target_bitmap_[current_buffer_]); // And then copy the rect from desktop to memory. BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(), - buffers_[current_buffer_].size.height(), desktop_dc_, 0, 0, + buffers_[current_buffer_].size.height(), *desktop_dc_, + desktop_dc_rect_.x(), desktop_dc_rect_.y(), SRCCOPY | CAPTUREBLT); } @@ -608,11 +559,6 @@ void CapturerGdi::CaptureCursor() { cursor_shape_changed_callback_.Run(cursor_proto.Pass()); } -SkISize CapturerGdi::GetScreenSize() { - return SkISize::Make(GetSystemMetrics(SM_CXSCREEN), - GetSystemMetrics(SM_CYSCREEN)); -} - } // namespace // static |