1 /*
2 * Copyright (c) 2017 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 "modules/desktop_capture/window_finder.h"
12
13 #include <stdint.h>
14
15 #include <memory>
16
17 #include "api/scoped_refptr.h"
18 #include "modules/desktop_capture/desktop_geometry.h"
19 #include "modules/desktop_capture/screen_drawer.h"
20 #include "rtc_base/logging.h"
21 #include "test/gtest.h"
22
23 #if defined(WEBRTC_USE_X11)
24 #include "modules/desktop_capture/linux/x11/shared_x_display.h"
25 #include "modules/desktop_capture/linux/x11/x_atom_cache.h"
26 #endif
27
28 #if defined(WEBRTC_WIN)
29 #include <windows.h>
30
31 #include "modules/desktop_capture/win/window_capture_utils.h"
32 #include "modules/desktop_capture/window_finder_win.h"
33 #endif
34
35 namespace webrtc {
36
37 namespace {
38
39 #if defined(WEBRTC_WIN)
40 // ScreenDrawerWin does not have a message loop, so it's unresponsive to user
41 // inputs. WindowFinderWin cannot detect this kind of unresponsive windows.
42 // Instead, console window is used to test WindowFinderWin.
TEST(WindowFinderTest,FindConsoleWindow)43 TEST(WindowFinderTest, FindConsoleWindow) {
44 // Creates a ScreenDrawer to avoid this test from conflicting with
45 // ScreenCapturerIntegrationTest: both tests require its window to be in
46 // foreground.
47 //
48 // In ScreenCapturer related tests, this is controlled by
49 // ScreenDrawer, which has a global lock to ensure only one ScreenDrawer
50 // window is active. So even we do not use ScreenDrawer for Windows test,
51 // creating an instance can block ScreenCapturer related tests until this test
52 // finishes.
53 //
54 // Usually the test framework should take care of this "isolated test"
55 // requirement, but unfortunately WebRTC trybots do not support this.
56 std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
57 const int kMaxSize = 10000;
58 // Enlarges current console window.
59 system("mode 1000,1000");
60 const HWND console_window = GetConsoleWindow();
61 // Ensures that current console window is visible.
62 ShowWindow(console_window, SW_MAXIMIZE);
63 // Moves the window to the top-left of the display.
64 MoveWindow(console_window, 0, 0, kMaxSize, kMaxSize, true);
65
66 bool should_restore_notopmost =
67 (GetWindowLong(console_window, GWL_EXSTYLE) & WS_EX_TOPMOST) == 0;
68
69 // Brings console window to top.
70 SetWindowPos(console_window, HWND_TOPMOST, 0, 0, 0, 0,
71 SWP_NOMOVE | SWP_NOSIZE);
72 BringWindowToTop(console_window);
73
74 bool success = false;
75 WindowFinderWin finder;
76 for (int i = 0; i < kMaxSize; i++) {
77 const DesktopVector spot(i, i);
78 const HWND id = reinterpret_cast<HWND>(finder.GetWindowUnderPoint(spot));
79 if (id == console_window) {
80 success = true;
81 break;
82 }
83 }
84 if (should_restore_notopmost)
85 SetWindowPos(console_window, HWND_NOTOPMOST, 0, 0, 0, 0,
86 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
87
88 if (!success)
89 FAIL();
90 }
91
92 #else
93 TEST(WindowFinderTest, FindDrawerWindow) {
94 WindowFinder::Options options;
95 #if defined(WEBRTC_USE_X11)
96 std::unique_ptr<XAtomCache> cache;
97 const auto shared_x_display = SharedXDisplay::CreateDefault();
98 if (shared_x_display) {
99 cache = std::make_unique<XAtomCache>(shared_x_display->display());
100 options.cache = cache.get();
101 }
102 #endif
103 std::unique_ptr<WindowFinder> finder = WindowFinder::Create(options);
104 if (!finder) {
105 RTC_LOG(LS_WARNING)
106 << "No WindowFinder implementation for current platform.";
107 return;
108 }
109
110 std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
111 if (!drawer) {
112 RTC_LOG(LS_WARNING)
113 << "No ScreenDrawer implementation for current platform.";
114 return;
115 }
116
117 if (drawer->window_id() == kNullWindowId) {
118 // TODO(zijiehe): WindowFinderTest can use a dedicated window without
119 // relying on ScreenDrawer.
120 RTC_LOG(LS_WARNING)
121 << "ScreenDrawer implementation for current platform does "
122 "create a window.";
123 return;
124 }
125
126 // ScreenDrawer may not be able to bring the window to the top. So we test
127 // several spots, at least one of them should succeed.
128 const DesktopRect region = drawer->DrawableRegion();
129 if (region.is_empty()) {
130 RTC_LOG(LS_WARNING)
131 << "ScreenDrawer::DrawableRegion() is too small for the "
132 "WindowFinderTest.";
133 return;
134 }
135
136 for (int i = 0; i < region.width(); i++) {
137 const DesktopVector spot(
138 region.left() + i, region.top() + i * region.height() / region.width());
139 const WindowId id = finder->GetWindowUnderPoint(spot);
140 if (id == drawer->window_id()) {
141 return;
142 }
143 }
144
145 FAIL();
146 }
147 #endif
148
TEST(WindowFinderTest,ShouldReturnNullWindowIfSpotIsOutOfScreen)149 TEST(WindowFinderTest, ShouldReturnNullWindowIfSpotIsOutOfScreen) {
150 WindowFinder::Options options;
151 #if defined(WEBRTC_USE_X11)
152 std::unique_ptr<XAtomCache> cache;
153 const auto shared_x_display = SharedXDisplay::CreateDefault();
154 if (shared_x_display) {
155 cache = std::make_unique<XAtomCache>(shared_x_display->display());
156 options.cache = cache.get();
157 }
158 #endif
159 std::unique_ptr<WindowFinder> finder = WindowFinder::Create(options);
160 if (!finder) {
161 RTC_LOG(LS_WARNING)
162 << "No WindowFinder implementation for current platform.";
163 return;
164 }
165
166 ASSERT_EQ(kNullWindowId,
167 finder->GetWindowUnderPoint(DesktopVector(INT16_MAX, INT16_MAX)));
168 ASSERT_EQ(kNullWindowId,
169 finder->GetWindowUnderPoint(DesktopVector(INT16_MAX, INT16_MIN)));
170 ASSERT_EQ(kNullWindowId,
171 finder->GetWindowUnderPoint(DesktopVector(INT16_MIN, INT16_MAX)));
172 ASSERT_EQ(kNullWindowId,
173 finder->GetWindowUnderPoint(DesktopVector(INT16_MIN, INT16_MIN)));
174 }
175
176 } // namespace
177
178 } // namespace webrtc
179