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