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}
diff --git a/webrtc/modules/desktop_capture/BUILD.gn b/webrtc/modules/desktop_capture/BUILD.gn
index b0297e9..3b025c5 100644
--- a/webrtc/modules/desktop_capture/BUILD.gn
+++ b/webrtc/modules/desktop_capture/BUILD.gn
@@ -251,6 +251,10 @@
"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) {
diff --git a/webrtc/modules/desktop_capture/cropping_window_capturer_win.cc b/webrtc/modules/desktop_capture/cropping_window_capturer_win.cc
index b7c71c8..e14efe6 100644
--- a/webrtc/modules/desktop_capture/cropping_window_capturer_win.cc
+++ b/webrtc/modules/desktop_capture/cropping_window_capturer_win.cc
@@ -186,6 +186,9 @@
// 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;
}
diff --git a/webrtc/modules/desktop_capture/desktop_capturer.h b/webrtc/modules/desktop_capture/desktop_capturer.h
index 71834a9..5af69b8 100644
--- a/webrtc/modules/desktop_capture/desktop_capturer.h
+++ b/webrtc/modules/desktop_capture/desktop_capturer.h
@@ -104,6 +104,10 @@
// 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.
diff --git a/webrtc/modules/desktop_capture/mac/window_list_utils.cc b/webrtc/modules/desktop_capture/mac/window_list_utils.cc
index 7dcfae9..88b8367 100644
--- a/webrtc/modules/desktop_capture/mac/window_list_utils.cc
+++ b/webrtc/modules/desktop_capture/mac/window_list_utils.cc
@@ -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 @@
// 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 @@
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 @@
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 @@
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 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 @@
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
diff --git a/webrtc/modules/desktop_capture/mac/window_list_utils.h b/webrtc/modules/desktop_capture/mac/window_list_utils.h
index 7cc4571..6492315 100644
--- a/webrtc/modules/desktop_capture/mac/window_list_utils.h
+++ b/webrtc/modules/desktop_capture/mac/window_list_utils.h
@@ -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 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_
-
diff --git a/webrtc/modules/desktop_capture/window_capturer_win.cc b/webrtc/modules/desktop_capture/window_capturer_win.cc
index 4fffd6b..29fbebe 100644
--- a/webrtc/modules/desktop_capture/window_capturer_win.cc
+++ b/webrtc/modules/desktop_capture/window_capturer_win.cc
@@ -115,6 +115,7 @@
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);
diff --git a/webrtc/modules/desktop_capture/window_capturer_x11.cc b/webrtc/modules/desktop_capture/window_capturer_x11.cc
index 62c258a..28f5b96 100644
--- a/webrtc/modules/desktop_capture/window_capturer_x11.cc
+++ b/webrtc/modules/desktop_capture/window_capturer_x11.cc
@@ -327,6 +327,8 @@
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) {
diff --git a/webrtc/modules/desktop_capture/window_under_point.h b/webrtc/modules/desktop_capture/window_under_point.h
new file mode 100644
index 0000000..5e61207
--- /dev/null
+++ b/webrtc/modules/desktop_capture/window_under_point.h
@@ -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_
diff --git a/webrtc/modules/desktop_capture/window_under_point_linux.cc b/webrtc/modules/desktop_capture/window_under_point_linux.cc
new file mode 100644
index 0000000..7b1d0f7
--- /dev/null
+++ b/webrtc/modules/desktop_capture/window_under_point_linux.cc
@@ -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
diff --git a/webrtc/modules/desktop_capture/window_under_point_mac.mm b/webrtc/modules/desktop_capture/window_under_point_mac.mm
new file mode 100644
index 0000000..7fe57f3
--- /dev/null
+++ b/webrtc/modules/desktop_capture/window_under_point_mac.mm
@@ -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
diff --git a/webrtc/modules/desktop_capture/window_under_point_win.cc b/webrtc/modules/desktop_capture/window_under_point_win.cc
new file mode 100644
index 0000000..a5e7dc1
--- /dev/null
+++ b/webrtc/modules/desktop_capture/window_under_point_win.cc
@@ -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