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>(®ion_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