2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
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.
11 #include "webrtc/modules/desktop_capture/window_capturer.h"
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"
24 typedef HRESULT (WINAPI *DwmIsCompositionEnabledFunc)(BOOL* enabled);
26 // Coverts a zero-terminated UTF-16 string to UTF-8. Returns an empty string if
28 std::string Utf16ToUtf8(const WCHAR* str) {
29 int len_utf8 = WideCharToMultiByte(CP_UTF8, 0, str, -1,
33 std::string result(len_utf8, '\0');
34 int rv = WideCharToMultiByte(CP_UTF8, 0, str, -1,
35 &*(result.begin()), len_utf8, NULL, NULL);
42 BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) {
43 WindowCapturer::WindowList* list =
44 reinterpret_cast<WindowCapturer::WindowList*>(param);
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))) {
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)
66 WindowCapturer::Window window;
67 window.id = reinterpret_cast<WindowCapturer::WindowId>(hwnd);
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);
75 // Skip windows when we failed to convert the title or it is empty.
76 if (window.title.empty())
79 list->push_back(window);
84 class WindowCapturerWin : public WindowCapturer {
87 virtual ~WindowCapturerWin();
89 // WindowCapturer interface.
90 virtual bool GetWindowList(WindowList* windows) OVERRIDE;
91 virtual bool SelectWindow(WindowId id) OVERRIDE;
92 virtual bool BringSelectedWindowToFront() OVERRIDE;
94 // DesktopCapturer interface.
95 virtual void Start(Callback* callback) OVERRIDE;
96 virtual void Capture(const DesktopRegion& region) OVERRIDE;
103 // HWND and HDC for the currently selected window or NULL if window is not
107 // dwmapi.dll is used to determine if desktop compositing is enabled.
108 HMODULE dwmapi_library_;
109 DwmIsCompositionEnabledFunc is_composition_enabled_func_;
111 DesktopSize previous_size_;
113 DISALLOW_COPY_AND_ASSIGN(WindowCapturerWin);
116 WindowCapturerWin::WindowCapturerWin()
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_);
127 is_composition_enabled_func_ = NULL;
131 WindowCapturerWin::~WindowCapturerWin() {
133 FreeLibrary(dwmapi_library_);
136 bool WindowCapturerWin::IsAeroEnabled() {
138 if (is_composition_enabled_func_)
139 is_composition_enabled_func_(&result);
140 return result != FALSE;
143 bool WindowCapturerWin::GetWindowList(WindowList* windows) {
145 LPARAM param = reinterpret_cast<LPARAM>(&result);
146 if (!EnumWindows(&WindowsEnumerationHandler, param))
148 windows->swap(result);
152 bool WindowCapturerWin::SelectWindow(WindowId id) {
153 HWND window = reinterpret_cast<HWND>(id);
154 if (!IsWindow(window) || !IsWindowVisible(window) || IsIconic(window))
157 previous_size_.set(0, 0);
161 bool WindowCapturerWin::BringSelectedWindowToFront() {
165 if (!IsWindow(window_) || !IsWindowVisible(window_) || IsIconic(window_))
168 return SetForegroundWindow(window_) != 0;
171 void WindowCapturerWin::Start(Callback* callback) {
175 callback_ = callback;
178 void WindowCapturerWin::Capture(const DesktopRegion& region) {
180 LOG(LS_ERROR) << "Window hasn't been selected: " << GetLastError();
181 callback_->OnCaptureCompleted(NULL);
185 // Stop capturing if the window has been minimized or hidden.
186 if (IsIconic(window_) || !IsWindowVisible(window_)) {
187 callback_->OnCaptureCompleted(NULL);
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);
199 HDC window_dc = GetWindowDC(window_);
201 LOG(LS_WARNING) << "Failed to get window DC: " << GetLastError();
202 callback_->OnCaptureCompleted(NULL);
206 scoped_ptr<DesktopFrameWin> frame(DesktopFrameWin::Create(
207 cropped_rect.size(), NULL, window_dc));
209 ReleaseDC(window_, window_dc);
210 callback_->OnCaptureCompleted(NULL);
214 HDC mem_dc = CreateCompatibleDC(window_dc);
215 HGDIOBJ previous_object = SelectObject(mem_dc, frame->bitmap());
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.
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.
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
235 if (!IsAeroEnabled() || !previous_size_.equals(frame->size())) {
236 result = PrintWindow(window_, mem_dc, 0);
239 // Aero is enabled or PrintWindow() failed, use BitBlt.
241 result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(),
243 cropped_rect.left() - original_rect.left(),
244 cropped_rect.top() - original_rect.top(),
248 SelectObject(mem_dc, previous_object);
250 ReleaseDC(window_, window_dc);
252 previous_size_ = frame->size();
255 LOG(LS_ERROR) << "Both PrintWindow() and BitBlt() failed.";
259 callback_->OnCaptureCompleted(frame.release());
265 WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
266 return new WindowCapturerWin();
269 } // namespace webrtc