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.
5 #include "remoting/host/desktop_shape_tracker.h"
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"
18 struct EnumDesktopShapeData
{
19 EnumDesktopShapeData()
20 : window_region(CreateRectRgn(0, 0, 0, 0)),
21 desktop_region(CreateRectRgn(0, 0, 0, 0)) {
23 base::win::ScopedRegion window_region
;
24 base::win::ScopedRegion desktop_region
;
27 class DesktopShapeTrackerWin
: public DesktopShapeTracker
{
29 DesktopShapeTrackerWin();
30 ~DesktopShapeTrackerWin() override
;
32 void RefreshDesktopShape() override
;
33 const webrtc::DesktopRegion
& desktop_shape() override
;
36 // Callback passed to EnumWindows() to enumerate windows.
37 static BOOL CALLBACK
EnumWindowsCallback(HWND window
, LPARAM lparam
);
39 // The most recently calculated desktop region.
40 webrtc::DesktopRegion desktop_shape_
;
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_
;
46 DISALLOW_COPY_AND_ASSIGN(DesktopShapeTrackerWin
);
49 DesktopShapeTrackerWin::DesktopShapeTrackerWin()
50 : old_desktop_region_(CreateRectRgn(0, 0, 0, 0)) {
53 DesktopShapeTrackerWin::~DesktopShapeTrackerWin() {
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();
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());
69 // Determine the size of output buffer required to receive the region.
70 DWORD bytes_size
= GetRegionData(old_desktop_region_
, 0, nullptr);
71 CHECK(bytes_size
!= 0);
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]);
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
));
89 const webrtc::DesktopRegion
& DesktopShapeTrackerWin::desktop_shape() {
90 return desktop_shape_
;
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();
99 // Is the window visible?
100 if (!IsWindow(window
) || !IsWindowVisible(window
) || IsIconic(window
))
103 // Find the desktop position of the window (including non-client-area).
105 if (!GetWindowRect(window
, &window_rect
))
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
);
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
);
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);
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
);
136 scoped_ptr
<DesktopShapeTracker
> DesktopShapeTracker::Create(
137 webrtc::DesktopCaptureOptions options
) {
138 return make_scoped_ptr(new DesktopShapeTrackerWin());
141 } // namespace remoting