Implement WindowUnderPoint() for Mac OSX and Windows
WindowUnderPoint() is a platform independent function to return the id of the first window in z-order under a certain DesktopVector. It equals to GetAncestor(WindowFromPoint(point), GA_ROOT) on Windows. This CL includes the change to Windows / Mac OSX only to control the size in a reasonable range. Implementation for Linux will be added in a coming change. Bug: webrtc:7950 Change-Id: I57e423294fc8aeaa12d05cb626a1912240b2d4d0 Reviewed-on: https://chromium-review.googlesource.com/595022 Commit-Queue: Zijie He <zijiehe@chromium.org> Reviewed-by: Jamie Walch <jamiewalch@chromium.org> Cr-Commit-Position: refs/heads/master@{#19263}
This commit is contained in:
parent
3ac7b1edb7
commit
b010a3242b
@ -251,6 +251,10 @@ rtc_static_library("desktop_capture") {
|
||||
"win/window_capture_utils.h",
|
||||
"window_capturer_mac.mm",
|
||||
"window_capturer_win.cc",
|
||||
"window_under_point.h",
|
||||
"window_under_point_linux.cc",
|
||||
"window_under_point_mac.mm",
|
||||
"window_under_point_win.cc",
|
||||
]
|
||||
|
||||
if (use_x11) {
|
||||
|
||||
@ -186,6 +186,9 @@ bool CroppingWindowCapturerWin::ShouldUseScreenCapturer() {
|
||||
|
||||
// Check if the window is occluded by any other window, excluding the child
|
||||
// windows, context menus, and |excluded_window_|.
|
||||
// TODO(zijiehe): EnumWindows enumerates root window only, so the window may
|
||||
// be covered by its own child window. See bug
|
||||
// https://bugs.chromium.org/p/webrtc/issues/detail?id=8062
|
||||
EnumWindows(&TopWindowVerifier, reinterpret_cast<LPARAM>(&context));
|
||||
return context.is_top_window;
|
||||
}
|
||||
|
||||
@ -104,6 +104,10 @@ class DesktopCapturer {
|
||||
|
||||
// Gets a list of sources current capturer supports. Returns false in case of
|
||||
// a failure.
|
||||
// For DesktopCapturer implementations to capture screens, this function
|
||||
// should return monitors.
|
||||
// For DesktopCapturer implementations to capture windows, this function
|
||||
// should only return root windows owned by applications.
|
||||
virtual bool GetSourceList(SourceList* sources);
|
||||
|
||||
// Selects a source to be captured. Returns false in case of a failure (e.g.
|
||||
|
||||
@ -12,13 +12,24 @@
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/macutils.h"
|
||||
|
||||
static_assert(
|
||||
static_cast<webrtc::WindowId>(kCGNullWindowID) == webrtc::kNullWindowId,
|
||||
"kNullWindowId needs to equal to kCGNullWindowID.");
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
bool GetWindowList(DesktopCapturer::SourceList* windows,
|
||||
bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
|
||||
bool ignore_minimized) {
|
||||
RTC_DCHECK(on_window);
|
||||
|
||||
// Only get on screen, non-desktop windows.
|
||||
// According to
|
||||
// https://developer.apple.com/documentation/coregraphics/cgwindowlistoption/1454105-optiononscreenonly ,
|
||||
// when kCGWindowListOptionOnScreenOnly is used, the order of windows are in
|
||||
// decreasing z-order.
|
||||
CFArrayRef window_array = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
|
||||
kCGNullWindowID);
|
||||
@ -34,38 +45,51 @@ bool GetWindowList(DesktopCapturer::SourceList* windows,
|
||||
// Check windows to make sure they have an id, title, and use window layer
|
||||
// other than 0.
|
||||
CFIndex count = CFArrayGetCount(window_array);
|
||||
for (CFIndex i = 0; i < count; ++i) {
|
||||
for (CFIndex i = 0; i < count; i++) {
|
||||
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
|
||||
CFArrayGetValueAtIndex(window_array, i));
|
||||
if (!window) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFStringRef window_title = reinterpret_cast<CFStringRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowName));
|
||||
if (!window_title) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowNumber));
|
||||
if (!window_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowLayer));
|
||||
if (window_title && window_id && window_layer) {
|
||||
// Skip windows with layer=0 (menu, dock).
|
||||
int layer;
|
||||
CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
|
||||
if (layer != 0)
|
||||
continue;
|
||||
if (!window_layer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int id;
|
||||
CFNumberGetValue(window_id, kCFNumberIntType, &id);
|
||||
// Skip windows with layer=0 (menu, dock).
|
||||
// TODO(zijiehe): The windows with layer != 0 are skipped, is this a bug in
|
||||
// code (not likely) or a bug in comments? What's the meaning of window
|
||||
// layer number in the first place.
|
||||
int layer;
|
||||
if (!CFNumberGetValue(window_layer, kCFNumberIntType, &layer)) {
|
||||
continue;
|
||||
}
|
||||
if (layer != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip windows that are minimized and not full screen.
|
||||
if (ignore_minimized && IsWindowMinimized(id) &&
|
||||
!IsWindowFullScreen(desktop_config, window)) {
|
||||
continue;
|
||||
}
|
||||
// Skip windows that are minimized and not full screen.
|
||||
if (ignore_minimized && IsWindowMinimized(window) &&
|
||||
!IsWindowFullScreen(desktop_config, window)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DesktopCapturer::Source window;
|
||||
window.id = id;
|
||||
if (!rtc::ToUtf8(window_title, &(window.title)) ||
|
||||
window.title.empty()) {
|
||||
continue;
|
||||
}
|
||||
windows->push_back(window);
|
||||
if (!on_window(window)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +97,20 @@ bool GetWindowList(DesktopCapturer::SourceList* windows,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetWindowList(DesktopCapturer::SourceList* windows,
|
||||
bool ignore_minimized) {
|
||||
return GetWindowList(
|
||||
[windows](CFDictionaryRef window) {
|
||||
WindowId id = GetWindowId(window);
|
||||
std::string title = GetWindowTitle(window);
|
||||
if (id != kNullWindowId && !title.empty()) {
|
||||
windows->push_back(DesktopCapturer::Source{ id, title });
|
||||
}
|
||||
return true;
|
||||
},
|
||||
ignore_minimized);
|
||||
}
|
||||
|
||||
// Returns true if the window is occupying a full screen.
|
||||
bool IsWindowFullScreen(
|
||||
const MacDesktopConfiguration& desktop_config,
|
||||
@ -86,7 +124,7 @@ bool IsWindowFullScreen(
|
||||
CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) {
|
||||
for (MacDisplayConfigurations::const_iterator it =
|
||||
desktop_config.displays.begin();
|
||||
it != desktop_config.displays.end(); ++it) {
|
||||
it != desktop_config.displays.end(); it++) {
|
||||
if (it->bounds.equals(DesktopRect::MakeXYWH(bounds.origin.x,
|
||||
bounds.origin.y,
|
||||
bounds.size.width,
|
||||
@ -100,6 +138,12 @@ bool IsWindowFullScreen(
|
||||
return fullscreen;
|
||||
}
|
||||
|
||||
bool IsWindowMinimized(CFDictionaryRef window) {
|
||||
CFBooleanRef on_screen = reinterpret_cast<CFBooleanRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowIsOnscreen));
|
||||
return !CFBooleanGetValue(on_screen);
|
||||
}
|
||||
|
||||
// Returns true if the window is minimized.
|
||||
bool IsWindowMinimized(CGWindowID id) {
|
||||
CFArrayRef window_id_array =
|
||||
@ -109,12 +153,8 @@ bool IsWindowMinimized(CGWindowID id) {
|
||||
bool minimized = false;
|
||||
|
||||
if (window_array && CFArrayGetCount(window_array)) {
|
||||
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
|
||||
CFArrayGetValueAtIndex(window_array, 0));
|
||||
CFBooleanRef on_screen = reinterpret_cast<CFBooleanRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowIsOnscreen));
|
||||
|
||||
minimized = !on_screen;
|
||||
minimized = IsWindowMinimized(reinterpret_cast<CFDictionaryRef>(
|
||||
CFArrayGetValueAtIndex(window_array, 0)));
|
||||
}
|
||||
|
||||
CFRelease(window_id_array);
|
||||
@ -123,6 +163,47 @@ bool IsWindowMinimized(CGWindowID id) {
|
||||
return minimized;
|
||||
}
|
||||
|
||||
std::string GetWindowTitle(CFDictionaryRef window) {
|
||||
CFStringRef title = reinterpret_cast<CFStringRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowName));
|
||||
std::string result;
|
||||
if (title && rtc::ToUtf8(title, &result)) {
|
||||
return result;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
WindowId GetWindowId(CFDictionaryRef window) {
|
||||
CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowNumber));
|
||||
if (!window_id) {
|
||||
return kNullWindowId;
|
||||
}
|
||||
|
||||
WindowId id;
|
||||
if (!CFNumberGetValue(window_id, kCFNumberIntType, &id)) {
|
||||
return kNullWindowId;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
DesktopRect GetWindowBounds(CFDictionaryRef window) {
|
||||
CFDictionaryRef window_bounds = reinterpret_cast<CFDictionaryRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowBounds));
|
||||
if (!window_bounds) {
|
||||
return DesktopRect();
|
||||
}
|
||||
|
||||
CGRect gc_window_rect;
|
||||
if (!CGRectMakeWithDictionaryRepresentation(window_bounds, &gc_window_rect)) {
|
||||
return DesktopRect();
|
||||
}
|
||||
|
||||
return DesktopRect::MakeXYWH(gc_window_rect.origin.x,
|
||||
gc_window_rect.origin.y,
|
||||
gc_window_rect.size.width,
|
||||
gc_window_rect.size.height);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -13,11 +13,22 @@
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#include "webrtc/modules/desktop_capture/desktop_capture_types.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_capturer.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
|
||||
#include "webrtc/rtc_base/function_view.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Iterates all on-screen windows in decreasing z-order and sends them
|
||||
// one-by-one to |on_window| function. If |on_window| returns false, this
|
||||
// function returns immediately. GetWindowList() returns false if native APIs
|
||||
// failed. Menus, dock, minimized windows and any windows which do not have a
|
||||
// valid window id or title will be ignored.
|
||||
bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
|
||||
bool ignore_minimized);
|
||||
|
||||
// Another helper function to get the on-screen windows.
|
||||
bool GetWindowList(DesktopCapturer::SourceList* windows, bool ignore_minimized);
|
||||
|
||||
@ -25,11 +36,24 @@ bool GetWindowList(DesktopCapturer::SourceList* windows, bool ignore_minimized);
|
||||
bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
|
||||
CFDictionaryRef window);
|
||||
|
||||
// Returns true if the |window| is minimized.
|
||||
bool IsWindowMinimized(CFDictionaryRef window);
|
||||
|
||||
// Returns true if the window is minimized.
|
||||
bool IsWindowMinimized(CGWindowID id);
|
||||
|
||||
// Returns utf-8 encoded title of |window|. If |window| is not a window or no
|
||||
// valid title can be retrieved, this function returns an empty string.
|
||||
std::string GetWindowTitle(CFDictionaryRef window);
|
||||
|
||||
// Returns id of |window|. If |window| is not a window or the window id cannot
|
||||
// be retrieved, this function returns kNullWindowId.
|
||||
WindowId GetWindowId(CFDictionaryRef window);
|
||||
|
||||
// Returns the bounds of |window|. If |window| is not a window or the bounds
|
||||
// cannot be retrieved, this function returns an empty DesktopRect.
|
||||
DesktopRect GetWindowBounds(CFDictionaryRef window);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_
|
||||
|
||||
|
||||
@ -115,6 +115,7 @@ WindowCapturerWin::~WindowCapturerWin() {}
|
||||
bool WindowCapturerWin::GetSourceList(SourceList* sources) {
|
||||
SourceList result;
|
||||
LPARAM param = reinterpret_cast<LPARAM>(&result);
|
||||
// EnumWindows only enumerates root windows.
|
||||
if (!EnumWindows(&WindowsEnumerationHandler, param))
|
||||
return false;
|
||||
sources->swap(result);
|
||||
|
||||
@ -327,6 +327,8 @@ bool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(zijiehe): This function should return the ancestor window of |window|
|
||||
// other than the root_window.
|
||||
::Window WindowCapturerLinux::GetApplicationWindow(::Window window) {
|
||||
int32_t state = GetWindowState(window);
|
||||
if (state == NormalState) {
|
||||
|
||||
26
webrtc/modules/desktop_capture/window_under_point.h
Normal file
26
webrtc/modules/desktop_capture/window_under_point.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_UNDER_POINT_H_
|
||||
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_UNDER_POINT_H_
|
||||
|
||||
#include "webrtc/modules/desktop_capture/desktop_capture_types.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Returns the id of the visible window under |point|. This function returns
|
||||
// kNullWindowId if no window is under |point| and the platform does not have
|
||||
// "root window" concept, i.e. the visible area under |point| is the desktop.
|
||||
WindowId GetWindowUnderPoint(DesktopVector point);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_UNDER_POINT_H_
|
||||
20
webrtc/modules/desktop_capture/window_under_point_linux.cc
Normal file
20
webrtc/modules/desktop_capture/window_under_point_linux.cc
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 "webrtc/modules/desktop_capture/window_under_point.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
WindowId GetWindowUnderPoint(DesktopVector point) {
|
||||
// TODO(zijiehe): Implementation required.
|
||||
return kNullWindowId;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
34
webrtc/modules/desktop_capture/window_under_point_mac.mm
Normal file
34
webrtc/modules/desktop_capture/window_under_point_mac.mm
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
|
||||
#include "webrtc/modules/desktop_capture/window_under_point.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
WindowId GetWindowUnderPoint(DesktopVector point) {
|
||||
WindowId id;
|
||||
if (!GetWindowList([&id, point](CFDictionaryRef window) {
|
||||
DesktopRect bounds = GetWindowBounds(window);
|
||||
if (bounds.Contains(point)) {
|
||||
id = GetWindowId(window);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
true)) {
|
||||
return kNullWindowId;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
35
webrtc/modules/desktop_capture/window_under_point_win.cc
Normal file
35
webrtc/modules/desktop_capture/window_under_point_win.cc
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 "webrtc/modules/desktop_capture/window_under_point.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
WindowId GetWindowUnderPoint(DesktopVector point) {
|
||||
HWND window = WindowFromPoint(POINT { point.x(), point.y() });
|
||||
if (!window) {
|
||||
return kNullWindowId;
|
||||
}
|
||||
|
||||
// The difference between GA_ROOTOWNER and GA_ROOT can be found at
|
||||
// https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/Hirr_DkuZdw.
|
||||
// In short, we should use GA_ROOT, since we only care about the root window
|
||||
// but not the owner.
|
||||
window = GetAncestor(window, GA_ROOT);
|
||||
if (!window) {
|
||||
return kNullWindowId;
|
||||
}
|
||||
|
||||
return reinterpret_cast<WindowId>(window);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
Loading…
x
Reference in New Issue
Block a user