Add Presentation Mode Support for Capturing OpenOffice Impress document windows
* Add OpenOfficeApplicationHandler for MacOS and MS Windows. * List of available sources for FullScreenWindowDetector on MacOS can include a window with empty title along with titled window for one application. * List of available sources for FullScreenWindowDetector on MS Windows can include a window with empty title or invisible ones. Bug: webrtc:11462 Change-Id: Id09537579ef6617dee29759c66dc9f7493166ca8 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/171723 Commit-Queue: Jamie Walch <jamiewalch@chromium.org> Reviewed-by: Jamie Walch <jamiewalch@chromium.org> Reviewed-by: Wez <wez@google.com> Cr-Commit-Position: refs/heads/master@{#32561}
This commit is contained in:
@ -154,13 +154,30 @@ class CroppingWindowCapturerWin : public CroppingWindowCapturer {
|
||||
void CroppingWindowCapturerWin::CaptureFrame() {
|
||||
DesktopCapturer* win_capturer = window_capturer();
|
||||
if (win_capturer) {
|
||||
// Update the list of available sources and override source to capture if
|
||||
// FullScreenWindowDetector returns not zero
|
||||
// Feed the actual list of windows into full screen window detector.
|
||||
if (full_screen_window_detector_) {
|
||||
full_screen_window_detector_->UpdateWindowListIfNeeded(
|
||||
selected_window(),
|
||||
[win_capturer](DesktopCapturer::SourceList* sources) {
|
||||
return win_capturer->GetSourceList(sources);
|
||||
selected_window(), [this](DesktopCapturer::SourceList* sources) {
|
||||
// Get the list of top level windows, including ones with empty
|
||||
// title. win_capturer_->GetSourceList can't be used here
|
||||
// cause it filters out the windows with empty titles and
|
||||
// it uses responsiveness check which could lead to performance
|
||||
// issues.
|
||||
SourceList result;
|
||||
if (!webrtc::GetWindowList(GetWindowListFlags::kNone, &result))
|
||||
return false;
|
||||
|
||||
// Filter out windows not visible on current desktop
|
||||
auto it = std::remove_if(
|
||||
result.begin(), result.end(), [this](const auto& source) {
|
||||
HWND hwnd = reinterpret_cast<HWND>(source.id);
|
||||
return !window_capture_helper_
|
||||
.IsWindowVisibleOnCurrentDesktop(hwnd);
|
||||
});
|
||||
result.erase(it, result.end());
|
||||
|
||||
sources->swap(result);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
win_capturer->SelectSource(GetWindowToCapture());
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "absl/strings/match.h"
|
||||
#include "api/function_view.h"
|
||||
#include "modules/desktop_capture/mac/window_list_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -59,17 +60,17 @@ class FullScreenMacApplicationHandler : public FullScreenApplicationHandler {
|
||||
title_predicate_(title_predicate),
|
||||
owner_pid_(GetWindowOwnerPid(sourceId)) {}
|
||||
|
||||
protected:
|
||||
using CachePredicate =
|
||||
rtc::FunctionView<bool(const DesktopCapturer::Source&)>;
|
||||
|
||||
void InvalidateCacheIfNeeded(const DesktopCapturer::SourceList& source_list,
|
||||
int64_t timestamp) const {
|
||||
// Copy only sources with the same pid
|
||||
int64_t timestamp,
|
||||
CachePredicate predicate) const {
|
||||
if (timestamp != cache_timestamp_) {
|
||||
cache_sources_.clear();
|
||||
std::copy_if(source_list.begin(), source_list.end(),
|
||||
std::back_inserter(cache_sources_),
|
||||
[&](const DesktopCapturer::Source& src) {
|
||||
return src.id != GetSourceId() &&
|
||||
GetWindowOwnerPid(src.id) == owner_pid_;
|
||||
});
|
||||
std::back_inserter(cache_sources_), predicate);
|
||||
cache_timestamp_ = timestamp;
|
||||
}
|
||||
}
|
||||
@ -77,7 +78,11 @@ class FullScreenMacApplicationHandler : public FullScreenApplicationHandler {
|
||||
WindowId FindFullScreenWindowWithSamePid(
|
||||
const DesktopCapturer::SourceList& source_list,
|
||||
int64_t timestamp) const {
|
||||
InvalidateCacheIfNeeded(source_list, timestamp);
|
||||
InvalidateCacheIfNeeded(source_list, timestamp,
|
||||
[&](const DesktopCapturer::Source& src) {
|
||||
return src.id != GetSourceId() &&
|
||||
GetWindowOwnerPid(src.id) == owner_pid_;
|
||||
});
|
||||
if (cache_sources_.empty())
|
||||
return kCGNullWindowID;
|
||||
|
||||
@ -119,7 +124,7 @@ class FullScreenMacApplicationHandler : public FullScreenApplicationHandler {
|
||||
: FindFullScreenWindowWithSamePid(source_list, timestamp);
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
const TitlePredicate title_predicate_;
|
||||
const int owner_pid_;
|
||||
mutable int64_t cache_timestamp_ = 0;
|
||||
@ -143,6 +148,52 @@ bool slide_show_title_predicate(const std::string& original_title,
|
||||
return false;
|
||||
}
|
||||
|
||||
class OpenOfficeApplicationHandler : public FullScreenMacApplicationHandler {
|
||||
public:
|
||||
OpenOfficeApplicationHandler(DesktopCapturer::SourceId sourceId)
|
||||
: FullScreenMacApplicationHandler(sourceId, nullptr) {}
|
||||
|
||||
DesktopCapturer::SourceId FindFullScreenWindow(
|
||||
const DesktopCapturer::SourceList& source_list,
|
||||
int64_t timestamp) const override {
|
||||
InvalidateCacheIfNeeded(source_list, timestamp,
|
||||
[&](const DesktopCapturer::Source& src) {
|
||||
return GetWindowOwnerPid(src.id) == owner_pid_;
|
||||
});
|
||||
|
||||
const auto original_window = GetSourceId();
|
||||
const std::string original_title = GetWindowTitle(original_window);
|
||||
|
||||
// Check if we have only one document window, otherwise it's not possible
|
||||
// to securely match a document window and a slide show window which has
|
||||
// empty title.
|
||||
if (std::any_of(cache_sources_.begin(), cache_sources_.end(),
|
||||
[&original_title](const DesktopCapturer::Source& src) {
|
||||
return src.title.length() && src.title != original_title;
|
||||
})) {
|
||||
return kCGNullWindowID;
|
||||
}
|
||||
|
||||
MacDesktopConfiguration desktop_config =
|
||||
MacDesktopConfiguration::GetCurrent(
|
||||
MacDesktopConfiguration::TopLeftOrigin);
|
||||
|
||||
// Looking for slide show window,
|
||||
// it must be a full screen window with empty title
|
||||
const auto slide_show_window = std::find_if(
|
||||
cache_sources_.begin(), cache_sources_.end(), [&](const auto& src) {
|
||||
return src.title.empty() &&
|
||||
IsWindowFullScreen(desktop_config, src.id);
|
||||
});
|
||||
|
||||
if (slide_show_window == cache_sources_.end()) {
|
||||
return kCGNullWindowID;
|
||||
}
|
||||
|
||||
return slide_show_window->id;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<FullScreenApplicationHandler>
|
||||
@ -154,6 +205,7 @@ CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId) {
|
||||
if (path_length > 0) {
|
||||
const char* last_slash = strrchr(buffer, '/');
|
||||
const std::string name{last_slash ? last_slash + 1 : buffer};
|
||||
const std::string owner_name = GetWindowOwnerName(sourceId);
|
||||
FullScreenMacApplicationHandler::TitlePredicate predicate = nullptr;
|
||||
if (name.find("Google Chrome") == 0 || name == "Chromium") {
|
||||
predicate = equal_title_predicate;
|
||||
@ -161,6 +213,8 @@ CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId) {
|
||||
predicate = slide_show_title_predicate;
|
||||
} else if (name == "Keynote") {
|
||||
predicate = equal_title_predicate;
|
||||
} else if (owner_name == "OpenOffice") {
|
||||
return std::make_unique<OpenOfficeApplicationHandler>(sourceId);
|
||||
}
|
||||
|
||||
if (predicate) {
|
||||
|
@ -303,7 +303,7 @@ std::string GetWindowOwnerName(CFDictionaryRef window) {
|
||||
std::string GetWindowOwnerName(CGWindowID id) {
|
||||
std::string owner_name;
|
||||
if (GetWindowRef(id, [&owner_name](CFDictionaryRef window) {
|
||||
owner_name = GetWindowOwnerPid(window);
|
||||
owner_name = GetWindowOwnerName(window);
|
||||
})) {
|
||||
return owner_name;
|
||||
}
|
||||
|
@ -14,6 +14,9 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "absl/strings/match.h"
|
||||
#include "modules/desktop_capture/win/screen_capture_utils.h"
|
||||
#include "modules/desktop_capture/win/window_capture_utils.h"
|
||||
#include "rtc_base/arraysize.h"
|
||||
#include "rtc_base/logging.h" // For RTC_LOG_GLE
|
||||
#include "rtc_base/string_utils.h"
|
||||
@ -21,6 +24,25 @@
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
// Utility function to verify that |window| has class name equal to |class_name|
|
||||
bool CheckWindowClassName(HWND window, const wchar_t* class_name) {
|
||||
const size_t classNameLength = wcslen(class_name);
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa
|
||||
// says lpszClassName field in WNDCLASS is limited by 256 symbols, so we don't
|
||||
// need to have a buffer bigger than that.
|
||||
constexpr size_t kMaxClassNameLength = 256;
|
||||
WCHAR buffer[kMaxClassNameLength];
|
||||
|
||||
const int length = ::GetClassNameW(window, buffer, kMaxClassNameLength);
|
||||
if (length <= 0)
|
||||
return false;
|
||||
|
||||
if (static_cast<size_t>(length) != classNameLength)
|
||||
return false;
|
||||
return wcsncmp(buffer, class_name, classNameLength) == 0;
|
||||
}
|
||||
|
||||
std::string WindowText(HWND window) {
|
||||
size_t len = ::GetWindowTextLength(window);
|
||||
if (len == 0)
|
||||
@ -146,20 +168,7 @@ class FullScreenPowerPointHandler : public FullScreenApplicationHandler {
|
||||
}
|
||||
|
||||
bool IsEditorWindow(HWND window) const {
|
||||
constexpr WCHAR kScreenClassName[] = L"PPTFrameClass";
|
||||
constexpr size_t kScreenClassNameLength = arraysize(kScreenClassName) - 1;
|
||||
|
||||
// We need to verify that window class is equal to |kScreenClassName|.
|
||||
// To do that we need a buffer large enough to include a null terminated
|
||||
// string one code point bigger than |kScreenClassName|. It will help us to
|
||||
// check that size of class name string returned by GetClassNameW is equal
|
||||
// to |kScreenClassNameLength| not being limited by size of buffer (case
|
||||
// when |kScreenClassName| is a prefix for class name string).
|
||||
WCHAR buffer[arraysize(kScreenClassName) + 3];
|
||||
const int length = ::GetClassNameW(window, buffer, arraysize(buffer));
|
||||
if (length != kScreenClassNameLength)
|
||||
return false;
|
||||
return wcsncmp(buffer, kScreenClassName, kScreenClassNameLength) == 0;
|
||||
return CheckWindowClassName(window, L"PPTFrameClass");
|
||||
}
|
||||
|
||||
bool IsSlideShowWindow(HWND window) const {
|
||||
@ -170,6 +179,74 @@ class FullScreenPowerPointHandler : public FullScreenApplicationHandler {
|
||||
}
|
||||
};
|
||||
|
||||
class OpenOfficeApplicationHandler : public FullScreenApplicationHandler {
|
||||
public:
|
||||
explicit OpenOfficeApplicationHandler(DesktopCapturer::SourceId sourceId)
|
||||
: FullScreenApplicationHandler(sourceId) {}
|
||||
|
||||
DesktopCapturer::SourceId FindFullScreenWindow(
|
||||
const DesktopCapturer::SourceList& window_list,
|
||||
int64_t timestamp) const override {
|
||||
if (window_list.empty())
|
||||
return 0;
|
||||
|
||||
DWORD process_id = WindowProcessId(reinterpret_cast<HWND>(GetSourceId()));
|
||||
|
||||
DesktopCapturer::SourceList app_windows =
|
||||
GetProcessWindows(window_list, process_id, nullptr);
|
||||
|
||||
DesktopCapturer::SourceList document_windows;
|
||||
std::copy_if(
|
||||
app_windows.begin(), app_windows.end(),
|
||||
std::back_inserter(document_windows),
|
||||
[this](const DesktopCapturer::Source& x) { return IsEditorWindow(x); });
|
||||
|
||||
// Check if we have only one document window, otherwise it's not possible
|
||||
// to securely match a document window and a slide show window which has
|
||||
// empty title.
|
||||
if (document_windows.size() != 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if document window has been selected as a source
|
||||
if (document_windows.front().id != GetSourceId()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if we have a slide show window.
|
||||
auto slide_show_window =
|
||||
std::find_if(app_windows.begin(), app_windows.end(),
|
||||
[this](const DesktopCapturer::Source& x) {
|
||||
return IsSlideShowWindow(x);
|
||||
});
|
||||
|
||||
if (slide_show_window == app_windows.end())
|
||||
return 0;
|
||||
|
||||
return slide_show_window->id;
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsEditorWindow(const DesktopCapturer::Source& source) const {
|
||||
if (source.title.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckWindowClassName(reinterpret_cast<HWND>(source.id), L"SALFRAME");
|
||||
}
|
||||
|
||||
bool IsSlideShowWindow(const DesktopCapturer::Source& source) const {
|
||||
// Check title size to filter out a Presenter Control window which shares
|
||||
// window class with Slide Show window but has non empty title.
|
||||
if (!source.title.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckWindowClassName(reinterpret_cast<HWND>(source.id),
|
||||
L"SALTMPSUBFRAME");
|
||||
}
|
||||
};
|
||||
|
||||
std::wstring GetPathByWindowId(HWND window_id) {
|
||||
DWORD process_id = WindowProcessId(window_id);
|
||||
HANDLE process =
|
||||
@ -193,13 +270,17 @@ std::wstring GetPathByWindowId(HWND window_id) {
|
||||
std::unique_ptr<FullScreenApplicationHandler>
|
||||
CreateFullScreenWinApplicationHandler(DesktopCapturer::SourceId source_id) {
|
||||
std::unique_ptr<FullScreenApplicationHandler> result;
|
||||
std::wstring exe_path = GetPathByWindowId(reinterpret_cast<HWND>(source_id));
|
||||
HWND hwnd = reinterpret_cast<HWND>(source_id);
|
||||
std::wstring exe_path = GetPathByWindowId(hwnd);
|
||||
std::wstring file_name = FileNameFromPath(exe_path);
|
||||
std::transform(file_name.begin(), file_name.end(), file_name.begin(),
|
||||
std::towupper);
|
||||
|
||||
if (file_name == L"POWERPNT.EXE") {
|
||||
result = std::make_unique<FullScreenPowerPointHandler>(source_id);
|
||||
} else if (file_name == L"SOFFICE.BIN" &&
|
||||
absl::EndsWith(WindowText(hwnd), "OpenOffice Impress")) {
|
||||
result = std::make_unique<OpenOfficeApplicationHandler>(source_id);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -24,6 +24,93 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct GetWindowListParams {
|
||||
GetWindowListParams(int flags, DesktopCapturer::SourceList* result)
|
||||
: ignoreUntitled(flags & GetWindowListFlags::kIgnoreUntitled),
|
||||
ignoreUnresponsive(flags & GetWindowListFlags::kIgnoreUnresponsive),
|
||||
result(result) {}
|
||||
const bool ignoreUntitled;
|
||||
const bool ignoreUnresponsive;
|
||||
DesktopCapturer::SourceList* const result;
|
||||
};
|
||||
|
||||
BOOL CALLBACK GetWindowListHandler(HWND hwnd, LPARAM param) {
|
||||
GetWindowListParams* params = reinterpret_cast<GetWindowListParams*>(param);
|
||||
DesktopCapturer::SourceList* list = params->result;
|
||||
|
||||
// Skip untitled window if ignoreUntitled specified
|
||||
if (params->ignoreUntitled && GetWindowTextLength(hwnd) == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Skip invisible and minimized windows
|
||||
if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Skip windows which are not presented in the taskbar,
|
||||
// namely owned window if they don't have the app window style set
|
||||
HWND owner = GetWindow(hwnd, GW_OWNER);
|
||||
LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
if (owner && !(exstyle & WS_EX_APPWINDOW)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// If ignoreUnresponsive is true then skip unresponsive windows. Set timout
|
||||
// with 50ms, in case system is under heavy load, the check can wait longer
|
||||
// but wont' be too long to delay the the enumeration.
|
||||
const UINT uTimeout = 50; // ms
|
||||
if (params->ignoreUnresponsive &&
|
||||
!SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, uTimeout,
|
||||
nullptr)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Capture the window class name, to allow specific window classes to be
|
||||
// skipped.
|
||||
//
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa
|
||||
// says lpszClassName field in WNDCLASS is limited by 256 symbols, so we don't
|
||||
// need to have a buffer bigger than that.
|
||||
const size_t kMaxClassNameLength = 256;
|
||||
WCHAR class_name[kMaxClassNameLength] = L"";
|
||||
const int class_name_length =
|
||||
GetClassNameW(hwnd, class_name, kMaxClassNameLength);
|
||||
if (class_name_length < 1)
|
||||
return TRUE;
|
||||
|
||||
// Skip Program Manager window.
|
||||
if (wcscmp(class_name, L"Progman") == 0)
|
||||
return TRUE;
|
||||
|
||||
// Skip Start button window on Windows Vista, Windows 7.
|
||||
// On Windows 8, Windows 8.1, Windows 10 Start button is not a top level
|
||||
// window, so it will not be examined here.
|
||||
if (wcscmp(class_name, L"Button") == 0)
|
||||
return TRUE;
|
||||
|
||||
DesktopCapturer::Source window;
|
||||
window.id = reinterpret_cast<WindowId>(hwnd);
|
||||
|
||||
const size_t kTitleLength = 500;
|
||||
WCHAR window_title[kTitleLength] = L"";
|
||||
if (GetWindowTextW(hwnd, window_title, kTitleLength) > 0) {
|
||||
window.title = rtc::ToUtf8(window_title);
|
||||
}
|
||||
|
||||
// Skip windows when we failed to convert the title or it is empty.
|
||||
if (params->ignoreUntitled && window.title.empty())
|
||||
return TRUE;
|
||||
|
||||
list->push_back(window);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Prefix used to match the window class for Chrome windows.
|
||||
const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_";
|
||||
|
||||
@ -165,57 +252,10 @@ bool IsWindowValidAndVisible(HWND window) {
|
||||
return IsWindow(window) && IsWindowVisible(window) && !IsIconic(window);
|
||||
}
|
||||
|
||||
BOOL CALLBACK FilterUncapturableWindows(HWND hwnd, LPARAM param) {
|
||||
DesktopCapturer::SourceList* list =
|
||||
reinterpret_cast<DesktopCapturer::SourceList*>(param);
|
||||
|
||||
// Skip windows that are invisible, minimized, have no title, or are owned,
|
||||
// unless they have the app window style set.
|
||||
int len = GetWindowTextLength(hwnd);
|
||||
HWND owner = GetWindow(hwnd, GW_OWNER);
|
||||
LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
if (len == 0 || !IsWindowValidAndVisible(hwnd) ||
|
||||
(owner && !(exstyle & WS_EX_APPWINDOW))) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Skip unresponsive windows. Set timout with 50ms, in case system is under
|
||||
// heavy load. We could wait longer and have a lower false negative, but that
|
||||
// would delay the the enumeration.
|
||||
const UINT timeout = 50; // ms
|
||||
if (!SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, timeout,
|
||||
nullptr)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Skip the Program Manager window and the Start button.
|
||||
WCHAR class_name[256];
|
||||
const int class_name_length =
|
||||
GetClassNameW(hwnd, class_name, arraysize(class_name));
|
||||
if (class_name_length < 1)
|
||||
return TRUE;
|
||||
|
||||
// Skip Program Manager window and the Start button. This is the same logic
|
||||
// that's used in Win32WindowPicker in libjingle. Consider filtering other
|
||||
// windows as well (e.g. toolbars).
|
||||
if (wcscmp(class_name, L"Progman") == 0 || wcscmp(class_name, L"Button") == 0)
|
||||
return TRUE;
|
||||
|
||||
DesktopCapturer::Source window;
|
||||
window.id = reinterpret_cast<WindowId>(hwnd);
|
||||
|
||||
// Truncate the title if it's longer than 500 characters.
|
||||
WCHAR window_title[500];
|
||||
GetWindowTextW(hwnd, window_title, arraysize(window_title));
|
||||
window.title = rtc::ToUtf8(window_title);
|
||||
|
||||
// Skip windows when we failed to convert the title or it is empty.
|
||||
if (window.title.empty())
|
||||
return TRUE;
|
||||
|
||||
list->push_back(window);
|
||||
|
||||
return TRUE;
|
||||
bool GetWindowList(int flags, DesktopCapturer::SourceList* windows) {
|
||||
GetWindowListParams params(flags, windows);
|
||||
return ::EnumWindows(&GetWindowListHandler,
|
||||
reinterpret_cast<LPARAM>(¶ms)) != 0;
|
||||
}
|
||||
|
||||
// WindowCaptureHelperWin implementation.
|
||||
@ -374,9 +414,11 @@ bool WindowCaptureHelperWin::IsWindowCloaked(HWND hwnd) {
|
||||
|
||||
bool WindowCaptureHelperWin::EnumerateCapturableWindows(
|
||||
DesktopCapturer::SourceList* results) {
|
||||
LPARAM param = reinterpret_cast<LPARAM>(results);
|
||||
if (!EnumWindows(&FilterUncapturableWindows, param))
|
||||
if (!webrtc::GetWindowList((GetWindowListFlags::kIgnoreUntitled |
|
||||
GetWindowListFlags::kIgnoreUnresponsive),
|
||||
results)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto it = results->begin(); it != results->end();) {
|
||||
if (!IsWindowVisibleOnCurrentDesktop(reinterpret_cast<HWND>(it->id))) {
|
||||
|
@ -71,10 +71,20 @@ bool IsWindowMaximized(HWND window, bool* result);
|
||||
// visible, and that it is not minimized.
|
||||
bool IsWindowValidAndVisible(HWND window);
|
||||
|
||||
// This function is passed into the EnumWindows API and filters out windows that
|
||||
// we don't want to capture, e.g. minimized or unresponsive windows and the
|
||||
// Start menu.
|
||||
BOOL CALLBACK FilterUncapturableWindows(HWND hwnd, LPARAM param);
|
||||
enum GetWindowListFlags {
|
||||
kNone = 0x00,
|
||||
kIgnoreUntitled = 1 << 0,
|
||||
kIgnoreUnresponsive = 1 << 1,
|
||||
};
|
||||
|
||||
// Retrieves the list of top-level windows on the screen.
|
||||
// Some windows will be ignored:
|
||||
// - Those that are invisible or minimized.
|
||||
// - Program Manager & Start menu.
|
||||
// - [with kIgnoreUntitled] windows with no title.
|
||||
// - [with kIgnoreUnresponsive] windows that unresponsive.
|
||||
// Returns false if native APIs failed.
|
||||
bool GetWindowList(int flags, DesktopCapturer::SourceList* windows);
|
||||
|
||||
typedef HRESULT(WINAPI* DwmIsCompositionEnabledFunc)(BOOL* enabled);
|
||||
typedef HRESULT(WINAPI* DwmGetWindowAttributeFunc)(HWND hwnd,
|
||||
|
@ -161,7 +161,19 @@ void WindowCapturerMac::CaptureFrame() {
|
||||
if (full_screen_window_detector_) {
|
||||
full_screen_window_detector_->UpdateWindowListIfNeeded(
|
||||
window_id_, [](DesktopCapturer::SourceList* sources) {
|
||||
return webrtc::GetWindowList(sources, true, false);
|
||||
// Not using webrtc::GetWindowList(sources, true, false)
|
||||
// as it doesn't allow to have in the result window with
|
||||
// empty title along with titled window owned by the same pid.
|
||||
return webrtc::GetWindowList(
|
||||
[sources](CFDictionaryRef window) {
|
||||
WindowId window_id = GetWindowId(window);
|
||||
if (window_id != kNullWindowId) {
|
||||
sources->push_back(DesktopCapturer::Source{window_id, GetWindowTitle(window)});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
true,
|
||||
false);
|
||||
});
|
||||
|
||||
CGWindowID full_screen_window = full_screen_window_detector_->FindFullScreenWindow(window_id_);
|
||||
|
Reference in New Issue
Block a user