Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / window_capturer_win.cc
1 /*
2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10
11 #include "webrtc/modules/desktop_capture/window_capturer.h"
12
13 #include <assert.h>
14
15 #include "webrtc/modules/desktop_capture/desktop_frame_win.h"
16 #include "webrtc/modules/desktop_capture/win/window_capture_utils.h"
17 #include "webrtc/system_wrappers/interface/logging.h"
18 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
19
20 namespace webrtc {
21
22 namespace {
23
24 typedef HRESULT (WINAPI *DwmIsCompositionEnabledFunc)(BOOL* enabled);
25
26 // Coverts a zero-terminated UTF-16 string to UTF-8. Returns an empty string if
27 // error occurs.
28 std::string Utf16ToUtf8(const WCHAR* str) {
29   int len_utf8 = WideCharToMultiByte(CP_UTF8, 0, str, -1,
30                                      NULL, 0, NULL, NULL);
31   if (len_utf8 <= 0)
32     return std::string();
33   std::string result(len_utf8, '\0');
34   int rv = WideCharToMultiByte(CP_UTF8, 0, str, -1,
35                                &*(result.begin()), len_utf8, NULL, NULL);
36   if (rv != len_utf8)
37     assert(false);
38
39   return result;
40 }
41
42 BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) {
43   WindowCapturer::WindowList* list =
44       reinterpret_cast<WindowCapturer::WindowList*>(param);
45
46   // Skip windows that are invisible, minimized, have no title, or are owned,
47   // unless they have the app window style set.
48   int len = GetWindowTextLength(hwnd);
49   HWND owner = GetWindow(hwnd, GW_OWNER);
50   LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
51   if (len == 0 || IsIconic(hwnd) || !IsWindowVisible(hwnd) ||
52       (owner && !(exstyle & WS_EX_APPWINDOW))) {
53     return TRUE;
54   }
55
56   // Skip the Program Manager window and the Start button.
57   const size_t kClassLength = 256;
58   WCHAR class_name[kClassLength];
59   GetClassName(hwnd, class_name, kClassLength);
60   // Skip Program Manager window and the Start button. This is the same logic
61   // that's used in Win32WindowPicker in libjingle. Consider filtering other
62   // windows as well (e.g. toolbars).
63   if (wcscmp(class_name, L"Progman") == 0 || wcscmp(class_name, L"Button") == 0)
64     return TRUE;
65
66   WindowCapturer::Window window;
67   window.id = reinterpret_cast<WindowCapturer::WindowId>(hwnd);
68
69   const size_t kTitleLength = 500;
70   WCHAR window_title[kTitleLength];
71   // Truncate the title if it's longer than kTitleLength.
72   GetWindowText(hwnd, window_title, kTitleLength);
73   window.title = Utf16ToUtf8(window_title);
74
75   // Skip windows when we failed to convert the title or it is empty.
76   if (window.title.empty())
77     return TRUE;
78
79   list->push_back(window);
80
81   return TRUE;
82 }
83
84 class WindowCapturerWin : public WindowCapturer {
85  public:
86   WindowCapturerWin();
87   virtual ~WindowCapturerWin();
88
89   // WindowCapturer interface.
90   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
91   virtual bool SelectWindow(WindowId id) OVERRIDE;
92   virtual bool BringSelectedWindowToFront() OVERRIDE;
93
94   // DesktopCapturer interface.
95   virtual void Start(Callback* callback) OVERRIDE;
96   virtual void Capture(const DesktopRegion& region) OVERRIDE;
97
98  private:
99   bool IsAeroEnabled();
100
101   Callback* callback_;
102
103   // HWND and HDC for the currently selected window or NULL if window is not
104   // selected.
105   HWND window_;
106
107   // dwmapi.dll is used to determine if desktop compositing is enabled.
108   HMODULE dwmapi_library_;
109   DwmIsCompositionEnabledFunc is_composition_enabled_func_;
110
111   DesktopSize previous_size_;
112
113   DISALLOW_COPY_AND_ASSIGN(WindowCapturerWin);
114 };
115
116 WindowCapturerWin::WindowCapturerWin()
117     : callback_(NULL),
118       window_(NULL) {
119   // Try to load dwmapi.dll dynamically since it is not available on XP.
120   dwmapi_library_ = LoadLibrary(L"dwmapi.dll");
121   if (dwmapi_library_) {
122     is_composition_enabled_func_ =
123         reinterpret_cast<DwmIsCompositionEnabledFunc>(
124             GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
125     assert(is_composition_enabled_func_);
126   } else {
127     is_composition_enabled_func_ = NULL;
128   }
129 }
130
131 WindowCapturerWin::~WindowCapturerWin() {
132   if (dwmapi_library_)
133     FreeLibrary(dwmapi_library_);
134 }
135
136 bool WindowCapturerWin::IsAeroEnabled() {
137   BOOL result = FALSE;
138   if (is_composition_enabled_func_)
139     is_composition_enabled_func_(&result);
140   return result != FALSE;
141 }
142
143 bool WindowCapturerWin::GetWindowList(WindowList* windows) {
144   WindowList result;
145   LPARAM param = reinterpret_cast<LPARAM>(&result);
146   if (!EnumWindows(&WindowsEnumerationHandler, param))
147     return false;
148   windows->swap(result);
149   return true;
150 }
151
152 bool WindowCapturerWin::SelectWindow(WindowId id) {
153   HWND window = reinterpret_cast<HWND>(id);
154   if (!IsWindow(window) || !IsWindowVisible(window) || IsIconic(window))
155     return false;
156   window_ = window;
157   previous_size_.set(0, 0);
158   return true;
159 }
160
161 bool WindowCapturerWin::BringSelectedWindowToFront() {
162   if (!window_)
163     return false;
164
165   if (!IsWindow(window_) || !IsWindowVisible(window_) || IsIconic(window_))
166     return false;
167
168   return SetForegroundWindow(window_) != 0;
169 }
170
171 void WindowCapturerWin::Start(Callback* callback) {
172   assert(!callback_);
173   assert(callback);
174
175   callback_ = callback;
176 }
177
178 void WindowCapturerWin::Capture(const DesktopRegion& region) {
179   if (!window_) {
180     LOG(LS_ERROR) << "Window hasn't been selected: " << GetLastError();
181     callback_->OnCaptureCompleted(NULL);
182     return;
183   }
184
185   // Stop capturing if the window has been minimized or hidden.
186   if (IsIconic(window_) || !IsWindowVisible(window_)) {
187     callback_->OnCaptureCompleted(NULL);
188     return;
189   }
190
191   DesktopRect original_rect;
192   DesktopRect cropped_rect;
193   if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) {
194     LOG(LS_WARNING) << "Failed to get window info: " << GetLastError();
195     callback_->OnCaptureCompleted(NULL);
196     return;
197   }
198
199   HDC window_dc = GetWindowDC(window_);
200   if (!window_dc) {
201     LOG(LS_WARNING) << "Failed to get window DC: " << GetLastError();
202     callback_->OnCaptureCompleted(NULL);
203     return;
204   }
205
206   scoped_ptr<DesktopFrameWin> frame(DesktopFrameWin::Create(
207       cropped_rect.size(), NULL, window_dc));
208   if (!frame.get()) {
209     ReleaseDC(window_, window_dc);
210     callback_->OnCaptureCompleted(NULL);
211     return;
212   }
213
214   HDC mem_dc = CreateCompatibleDC(window_dc);
215   HGDIOBJ previous_object = SelectObject(mem_dc, frame->bitmap());
216   BOOL result = FALSE;
217
218   // When desktop composition (Aero) is enabled each window is rendered to a
219   // private buffer allowing BitBlt() to get the window content even if the
220   // window is occluded. PrintWindow() is slower but lets rendering the window
221   // contents to an off-screen device context when Aero is not available.
222   // PrintWindow() is not supported by some applications.
223   //
224   // If Aero is enabled, we prefer BitBlt() because it's faster and avoids
225   // window flickering. Otherwise, we prefer PrintWindow() because BitBlt() may
226   // render occluding windows on top of the desired window.
227   //
228   // When composition is enabled the DC returned by GetWindowDC() doesn't always
229   // have window frame rendered correctly. Windows renders it only once and then
230   // caches the result between captures. We hack it around by calling
231   // PrintWindow() whenever window size changes, including the first time of
232   // capturing - it somehow affects what we get from BitBlt() on the subsequent
233   // captures.
234
235   if (!IsAeroEnabled() || !previous_size_.equals(frame->size())) {
236     result = PrintWindow(window_, mem_dc, 0);
237   }
238
239   // Aero is enabled or PrintWindow() failed, use BitBlt.
240   if (!result) {
241     result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(),
242                     window_dc,
243                     cropped_rect.left() - original_rect.left(),
244                     cropped_rect.top() - original_rect.top(),
245                     SRCCOPY);
246   }
247
248   SelectObject(mem_dc, previous_object);
249   DeleteDC(mem_dc);
250   ReleaseDC(window_, window_dc);
251
252   previous_size_ = frame->size();
253
254   if (!result) {
255     LOG(LS_ERROR) << "Both PrintWindow() and BitBlt() failed.";
256     frame.reset();
257   }
258
259   callback_->OnCaptureCompleted(frame.release());
260 }
261
262 }  // namespace
263
264 // static
265 WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
266   return new WindowCapturerWin();
267 }
268
269 }  // namespace webrtc