From 8944f6a3f5f301a9fe400b22dfcd72d548c78558 Mon Sep 17 00:00:00 2001 From: braveyao Date: Fri, 6 Apr 2018 10:59:50 -0700 Subject: [PATCH] [desktopCaptuer Win] exclude windows not on current virtual desktop Since Windows 10, Windows starts to support virtual desktops. The problem is when one virtual desktop is not the current one, we can still enumerate the windows on it, which are still marked as visible by OS. This causes troubles to decide if a window is on top to be cropped out. This cl is to utilize a COM API, IsWindowOnCurrentVirtualDesktop of VirtualDesktopManager, to make sure only the windows on current desktop will be enumerated. Bug: chromium:796112 Change-Id: I6e0546e90fbdb37365a8d98694ded0e30791628e Reviewed-on: https://webrtc-review.googlesource.com/65882 Reviewed-by: Jamie Walch Reviewed-by: Taylor Brandstetter Commit-Queue: Brave Yao Cr-Commit-Position: refs/heads/master@{#22842} --- .../cropping_window_capturer_win.cc | 22 ++++++----- .../win/window_capture_utils.cc | 37 +++++++++++++++++-- .../win/window_capture_utils.h | 15 ++++++-- .../desktop_capture/window_capturer_win.cc | 23 ++++++++---- rtc_base/win32.h | 6 +++ 5 files changed, 80 insertions(+), 23 deletions(-) diff --git a/modules/desktop_capture/cropping_window_capturer_win.cc b/modules/desktop_capture/cropping_window_capturer_win.cc index 6707236b3e..2c504a002a 100644 --- a/modules/desktop_capture/cropping_window_capturer_win.cc +++ b/modules/desktop_capture/cropping_window_capturer_win.cc @@ -24,10 +24,12 @@ namespace { struct TopWindowVerifierContext { TopWindowVerifierContext(HWND selected_window, HWND excluded_window, - DesktopRect selected_window_rect) + DesktopRect selected_window_rect, + WindowCaptureHelperWin* window_capture_helper) : selected_window(selected_window), excluded_window(excluded_window), selected_window_rect(selected_window_rect), + window_capture_helper(window_capture_helper), is_top_window(false) { RTC_DCHECK_NE(selected_window, excluded_window); } @@ -35,6 +37,7 @@ struct TopWindowVerifierContext { const HWND selected_window; const HWND excluded_window; const DesktopRect selected_window_rect; + WindowCaptureHelperWin* window_capture_helper; bool is_top_window; }; @@ -54,8 +57,8 @@ BOOL CALLBACK TopWindowVerifier(HWND hwnd, LPARAM param) { return TRUE; } - // Ignore hidden or minimized window. - if (IsIconic(hwnd) || !IsWindowVisible(hwnd)) { + // Ignore invisible window on current desktop. + if (!context->window_capture_helper->IsWindowVisibleOnCurrentDesktop(hwnd)) { return TRUE; } @@ -140,17 +143,17 @@ class CroppingWindowCapturerWin : public CroppingWindowCapturer { // rectangular, or the rect from GetWindowRect if the region is not set. DesktopRect window_region_rect_; - AeroChecker aero_checker_; + WindowCaptureHelperWin window_capture_helper_; }; bool CroppingWindowCapturerWin::ShouldUseScreenCapturer() { - if (!rtc::IsWindows8OrLater() && aero_checker_.IsAeroEnabled()) { + if (!rtc::IsWindows8OrLater() && window_capture_helper_.IsAeroEnabled()) { return false; } const HWND selected = reinterpret_cast(selected_window()); - // Check if the window is hidden or minimized. - if (IsIconic(selected) || !IsWindowVisible(selected)) { + // Check if the window is visible on current desktop. + if (!window_capture_helper_.IsWindowVisibleOnCurrentDesktop(selected)) { return false; } @@ -223,8 +226,9 @@ bool CroppingWindowCapturerWin::ShouldUseScreenCapturer() { // windows, context menus, and |excluded_window_|. // |content_rect| is preferred, see the comments in TopWindowVerifier() // function. - TopWindowVerifierContext context( - selected, reinterpret_cast(excluded_window()), content_rect); + TopWindowVerifierContext context(selected, + reinterpret_cast(excluded_window()), + content_rect, &window_capture_helper_); const LPARAM enum_param = reinterpret_cast(&context); EnumWindows(&TopWindowVerifier, enum_param); if (!context.is_top_window) { diff --git a/modules/desktop_capture/win/window_capture_utils.cc b/modules/desktop_capture/win/window_capture_utils.cc index 41c62dca59..8929bb7de3 100644 --- a/modules/desktop_capture/win/window_capture_utils.cc +++ b/modules/desktop_capture/win/window_capture_utils.cc @@ -12,6 +12,7 @@ #include "modules/desktop_capture/win/scoped_gdi_object.h" #include "rtc_base/checks.h" +#include "rtc_base/logging.h" #include "rtc_base/win32.h" namespace webrtc { @@ -130,22 +131,34 @@ bool IsWindowMaximized(HWND window, bool* result) { return true; } -AeroChecker::AeroChecker() : dwmapi_library_(nullptr), func_(nullptr) { +// WindowCaptureHelperWin implementation. +WindowCaptureHelperWin::WindowCaptureHelperWin() + : dwmapi_library_(nullptr), + func_(nullptr), + virtual_desktop_manager_(nullptr) { // Try to load dwmapi.dll dynamically since it is not available on XP. dwmapi_library_ = LoadLibrary(L"dwmapi.dll"); if (dwmapi_library_) { func_ = reinterpret_cast( GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled")); } + + if (rtc::IsWindows10OrLater()) { + if (FAILED(::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr, + CLSCTX_ALL, + IID_PPV_ARGS(&virtual_desktop_manager_)))) { + RTC_LOG(LS_WARNING) << "Fail to create instance of VirtualDesktopManager"; + } + } } -AeroChecker::~AeroChecker() { +WindowCaptureHelperWin::~WindowCaptureHelperWin() { if (dwmapi_library_) { FreeLibrary(dwmapi_library_); } } -bool AeroChecker::IsAeroEnabled() { +bool WindowCaptureHelperWin::IsAeroEnabled() { BOOL result = FALSE; if (func_) { func_(&result); @@ -153,4 +166,22 @@ bool AeroChecker::IsAeroEnabled() { return result != FALSE; } +bool WindowCaptureHelperWin::IsWindowOnCurrentDesktop(HWND hwnd) { + // Make sure the window is on the current virtual desktop. + if (virtual_desktop_manager_) { + BOOL on_current_desktop; + if (SUCCEEDED(virtual_desktop_manager_->IsWindowOnCurrentVirtualDesktop( + hwnd, &on_current_desktop)) && + !on_current_desktop) { + return false; + } + } + return true; +} + +bool WindowCaptureHelperWin::IsWindowVisibleOnCurrentDesktop(HWND hwnd) { + return !::IsIconic(hwnd) && ::IsWindowVisible(hwnd) && + IsWindowOnCurrentDesktop(hwnd); +} + } // namespace webrtc diff --git a/modules/desktop_capture/win/window_capture_utils.h b/modules/desktop_capture/win/window_capture_utils.h index c38995a82a..7cb5097aa3 100644 --- a/modules/desktop_capture/win/window_capture_utils.h +++ b/modules/desktop_capture/win/window_capture_utils.h @@ -8,7 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include +#include #include "modules/desktop_capture/desktop_geometry.h" #include "rtc_base/constructormagic.h" @@ -58,18 +60,23 @@ bool GetDcSize(HDC hdc, DesktopSize* size); bool IsWindowMaximized(HWND window, bool* result); typedef HRESULT (WINAPI *DwmIsCompositionEnabledFunc)(BOOL* enabled); -class AeroChecker { +class WindowCaptureHelperWin { public: - AeroChecker(); - ~AeroChecker(); + WindowCaptureHelperWin(); + ~WindowCaptureHelperWin(); bool IsAeroEnabled(); + bool IsWindowOnCurrentDesktop(HWND hwnd); + bool IsWindowVisibleOnCurrentDesktop(HWND hwnd); private: HMODULE dwmapi_library_; DwmIsCompositionEnabledFunc func_; - RTC_DISALLOW_COPY_AND_ASSIGN(AeroChecker); + // Only used on Win10+. + Microsoft::WRL::ComPtr virtual_desktop_manager_; + + RTC_DISALLOW_COPY_AND_ASSIGN(WindowCaptureHelperWin); }; } // namespace webrtc diff --git a/modules/desktop_capture/window_capturer_win.cc b/modules/desktop_capture/window_capturer_win.cc index 414eddf64b..d1a672b66f 100644 --- a/modules/desktop_capture/window_capturer_win.cc +++ b/modules/desktop_capture/window_capturer_win.cc @@ -131,7 +131,7 @@ class WindowCapturerWin : public DesktopCapturer { DesktopSize previous_size_; - AeroChecker aero_checker_; + WindowCaptureHelperWin window_capture_helper_; // This map is used to avoid flickering for the case when SelectWindow() calls // are interleaved with Capture() calls. @@ -151,6 +151,15 @@ bool WindowCapturerWin::GetSourceList(SourceList* sources) { // EnumWindows only enumerates root windows. if (!EnumWindows(&WindowsEnumerationHandler, param)) return false; + + for (auto it = result.begin(); it != result.end();) { + if (!window_capture_helper_.IsWindowOnCurrentDesktop( + reinterpret_cast(it->id))) { + it = result.erase(it); + } else { + ++it; + } + } sources->swap(result); std::map new_map; @@ -220,12 +229,11 @@ void WindowCapturerWin::CaptureFrame() { return; } - // Return a 1x1 black frame if the window is minimized or invisible, to match - // behavior on mace. Window can be temporarily invisible during the - // transition of full screen mode on/off. + // Return a 1x1 black frame if the window is minimized or invisible on current + // desktop, to match behavior on mace. Window can be temporarily invisible + // during the transition of full screen mode on/off. if (original_rect.is_empty() || - IsIconic(window_) || - !IsWindowVisible(window_)) { + !window_capture_helper_.IsWindowVisibleOnCurrentDesktop(window_)) { std::unique_ptr frame( new BasicDesktopFrame(DesktopSize(1, 1))); memset(frame->data(), 0, frame->stride() * frame->size().height()); @@ -295,7 +303,8 @@ void WindowCapturerWin::CaptureFrame() { // capturing - it somehow affects what we get from BitBlt() on the subsequent // captures. - if (!aero_checker_.IsAeroEnabled() || !previous_size_.equals(frame->size())) { + if (!window_capture_helper_.IsAeroEnabled() || + !previous_size_.equals(frame->size())) { result = PrintWindow(window_, mem_dc, 0); } diff --git a/rtc_base/win32.h b/rtc_base/win32.h index 78f66a77de..4e91687132 100644 --- a/rtc_base/win32.h +++ b/rtc_base/win32.h @@ -54,6 +54,7 @@ bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename); enum WindowsMajorVersions { kWindows2000 = 5, kWindowsVista = 6, + kWindows10 = 10, }; bool GetOsVersion(int* major, int* minor, int* build); @@ -74,6 +75,11 @@ inline bool IsWindows8OrLater() { (major > kWindowsVista || (major == kWindowsVista && minor >= 2))); } +inline bool IsWindows10OrLater() { + int major; + return (GetOsVersion(&major, nullptr, nullptr) && (major >= kWindows10)); +} + // Determine the current integrity level of the process. bool GetCurrentProcessIntegrityLevel(int* level);