blob: 804cee9656b9bcb60b3df62770fe359e6f041087 [file] [log] [blame]
/*
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/desktop_capture/win/window_capture_utils.h"
#include <winuser.h>
#include <algorithm>
#include <memory>
#include <mutex>
#include "modules/desktop_capture/desktop_capturer.h"
#include "rtc_base/thread.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
const char kWindowThreadName[] = "window_capture_utils_test_thread";
const WCHAR kWindowClass[] = L"WindowCaptureUtilsTestClass";
const WCHAR kWindowTitle[] = L"Window Capture Utils Test";
const int kWindowWidth = 300;
const int kWindowHeight = 200;
struct WindowInfo {
HWND hwnd;
HINSTANCE window_instance;
ATOM window_class;
};
WindowInfo CreateTestWindow(const WCHAR* window_title) {
WindowInfo info;
::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&::DefWindowProc),
&info.window_instance);
WNDCLASSEXW wcex;
memset(&wcex, 0, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.hInstance = info.window_instance;
wcex.lpfnWndProc = &::DefWindowProc;
wcex.lpszClassName = kWindowClass;
info.window_class = ::RegisterClassExW(&wcex);
info.hwnd = ::CreateWindowW(kWindowClass, window_title, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, kWindowWidth,
kWindowHeight, /*parent_window=*/nullptr,
/*menu_bar=*/nullptr, info.window_instance,
/*additional_params=*/nullptr);
::ShowWindow(info.hwnd, SW_SHOWNORMAL);
::UpdateWindow(info.hwnd);
return info;
}
void DestroyTestWindow(WindowInfo info) {
::DestroyWindow(info.hwnd);
::UnregisterClass(MAKEINTATOM(info.window_class), info.window_instance);
}
std::unique_ptr<rtc::Thread> SetUpUnresponsiveWindow(std::mutex& mtx,
WindowInfo& info) {
std::unique_ptr<rtc::Thread> window_thread;
window_thread = rtc::Thread::Create();
window_thread->SetName(kWindowThreadName, nullptr);
window_thread->Start();
window_thread->Invoke<void>(
RTC_FROM_HERE, [&info]() { info = CreateTestWindow(kWindowTitle); });
// Intentionally create a deadlock to cause the window to become unresponsive.
mtx.lock();
window_thread->PostTask(RTC_FROM_HERE, [&mtx]() {
mtx.lock();
mtx.unlock();
});
return window_thread;
}
} // namespace
TEST(WindowCaptureUtilsTest, GetWindowList) {
WindowInfo info = CreateTestWindow(kWindowTitle);
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
EXPECT_GT(window_list.size(), 0ULL);
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
DestroyTestWindow(info);
}
TEST(WindowCaptureUtilsTest, IncludeUnresponsiveWindows) {
std::mutex mtx;
WindowInfo info;
std::unique_ptr<rtc::Thread> window_thread =
SetUpUnresponsiveWindow(mtx, info);
EXPECT_FALSE(IsWindowResponding(info.hwnd));
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
EXPECT_GT(window_list.size(), 0ULL);
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
mtx.unlock();
window_thread->Invoke<void>(RTC_FROM_HERE,
[&info]() { DestroyTestWindow(info); });
window_thread->Stop();
}
TEST(WindowCaptureUtilsTest, IgnoreUnresponsiveWindows) {
std::mutex mtx;
WindowInfo info;
std::unique_ptr<rtc::Thread> window_thread =
SetUpUnresponsiveWindow(mtx, info);
EXPECT_FALSE(IsWindowResponding(info.hwnd));
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(
GetWindowList(GetWindowListFlags::kIgnoreUnresponsive, &window_list));
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
mtx.unlock();
window_thread->Invoke<void>(RTC_FROM_HERE,
[&info]() { DestroyTestWindow(info); });
window_thread->Stop();
}
TEST(WindowCaptureUtilsTest, IncludeUntitledWindows) {
WindowInfo info = CreateTestWindow(L"");
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
EXPECT_GT(window_list.size(), 0ULL);
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
DestroyTestWindow(info);
}
TEST(WindowCaptureUtilsTest, IgnoreUntitledWindows) {
WindowInfo info = CreateTestWindow(L"");
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreUntitled, &window_list));
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
DestroyTestWindow(info);
}
} // namespace webrtc