Home | History | Annotate | Download | only in host
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/desktop_shape_tracker.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/win/scoped_gdi_object.h"
     11 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
     12 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
     13 
     14 namespace remoting {
     15 
     16 namespace {
     17 
     18 struct EnumDesktopShapeData {
     19   EnumDesktopShapeData()
     20     : window_region(CreateRectRgn(0, 0, 0, 0)),
     21       desktop_region(CreateRectRgn(0, 0, 0, 0)) {
     22   }
     23   base::win::ScopedRegion window_region;
     24   base::win::ScopedRegion desktop_region;
     25 };
     26 
     27 class DesktopShapeTrackerWin : public DesktopShapeTracker {
     28  public:
     29   DesktopShapeTrackerWin();
     30   virtual ~DesktopShapeTrackerWin();
     31 
     32   virtual void RefreshDesktopShape();
     33   virtual const webrtc::DesktopRegion& desktop_shape();
     34 
     35  private:
     36   // Callback passed to EnumWindows() to enumerate windows.
     37   static BOOL CALLBACK EnumWindowsCallback(HWND window, LPARAM lparam);
     38 
     39   // The most recently calculated desktop region.
     40   webrtc::DesktopRegion desktop_shape_;
     41 
     42   // Stored to compare with newly calculated desktop shapes, to avoid converting
     43   // to an DesktopRegion unless the shape has actually changed.
     44   base::win::ScopedRegion old_desktop_region_;
     45 
     46   DISALLOW_COPY_AND_ASSIGN(DesktopShapeTrackerWin);
     47 };
     48 
     49 DesktopShapeTrackerWin::DesktopShapeTrackerWin()
     50   : old_desktop_region_(CreateRectRgn(0, 0, 0, 0)) {
     51 }
     52 
     53 DesktopShapeTrackerWin::~DesktopShapeTrackerWin() {
     54 }
     55 
     56 void DesktopShapeTrackerWin::RefreshDesktopShape() {
     57   // Accumulate a new desktop shape from current window positions.
     58   scoped_ptr<EnumDesktopShapeData> shape_data(new EnumDesktopShapeData);
     59   if (!EnumWindows(EnumWindowsCallback, (LPARAM)shape_data.get())) {
     60     PLOG(ERROR) << "Failed to enumerate windows";
     61     desktop_shape_.Clear();
     62     return;
     63   }
     64 
     65   // If the shape has changed, refresh |desktop_shape_|.
     66   if (!EqualRgn(shape_data->desktop_region, old_desktop_region_)) {
     67     old_desktop_region_.Set(shape_data->desktop_region.release());
     68 
     69     // Determine the size of output buffer required to receive the region.
     70     DWORD bytes_size = GetRegionData(old_desktop_region_, 0, NULL);
     71     CHECK(bytes_size != 0);
     72 
     73     // Fetch the Windows RECTs that comprise the region.
     74     std::vector<char> buffer(bytes_size);
     75     LPRGNDATA region_data = reinterpret_cast<LPRGNDATA>(buffer.data());
     76     DWORD result = GetRegionData(old_desktop_region_, bytes_size, region_data);
     77     CHECK(result == bytes_size);
     78     const LPRECT rects = reinterpret_cast<LPRECT>(&region_data->Buffer[0]);
     79 
     80     // Reset |desktop_shape_| and add new rectangles into it.
     81     desktop_shape_.Clear();
     82     for (size_t i = 0; i < region_data->rdh.nCount; ++i) {
     83       desktop_shape_.AddRect(webrtc::DesktopRect::MakeLTRB(
     84           rects[i].left, rects[i].top, rects[i].right, rects[i].bottom));
     85     }
     86   }
     87 }
     88 
     89 const webrtc::DesktopRegion& DesktopShapeTrackerWin::desktop_shape() {
     90   return desktop_shape_;
     91 }
     92 
     93 // static
     94 BOOL DesktopShapeTrackerWin::EnumWindowsCallback(HWND window, LPARAM lparam) {
     95   EnumDesktopShapeData* data = reinterpret_cast<EnumDesktopShapeData*>(lparam);
     96   HRGN desktop_region = data->desktop_region.Get();
     97   HRGN window_region = data->window_region.Get();
     98 
     99   // Is the window visible?
    100   if (!IsWindow(window) || !IsWindowVisible(window) || IsIconic(window))
    101     return TRUE;
    102 
    103   // Find the desktop position of the window (including non-client-area).
    104   RECT window_rect;
    105   if (!GetWindowRect(window, &window_rect))
    106     return TRUE;
    107 
    108   // Find the shape of the window, in window coords.
    109   // GetWindowRgn will overwrite the current contents of |window_region|.
    110   if (GetWindowRgn(window, window_region) != ERROR) {
    111     // Translate the window region into desktop coordinates.
    112     OffsetRgn(window_region, window_rect.left, window_rect.top);
    113   } else {
    114     // Window has no shape, or an error occurred, so assume it's rectangular.
    115     SetRectRgn(window_region, window_rect.left, window_rect.top,
    116                window_rect.right, window_rect.bottom);
    117   }
    118 
    119   // TODO(wez): If the window is maximized then we should clip it to the
    120   // display on which it is maximized.
    121   //  if (IsZoomed(window))
    122   //    CombineRgn(window_region, window_region, screen_region, RGN_AND);
    123 
    124   // Merge the window region into the accumulated desktop region.  Window
    125   // regions are combined together before converting the result to
    126   // DesktopRegion. It assumed that this approach is more efficient than
    127   // converting each window region individually.
    128   CombineRgn(desktop_region, desktop_region, window_region, RGN_OR);
    129 
    130   return TRUE;
    131 }
    132 
    133 } // namespace
    134 
    135 // static
    136 scoped_ptr<DesktopShapeTracker> DesktopShapeTracker::Create(
    137     webrtc::DesktopCaptureOptions options) {
    138   return scoped_ptr<DesktopShapeTracker>(new DesktopShapeTrackerWin());
    139 }
    140 
    141 }  // namespace remoting
    142