diff --git a/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc b/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc index 3a26c77618..a85c275ae9 100644 --- a/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc +++ b/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc @@ -24,55 +24,13 @@ namespace { const int64_t kUpdateIntervalMs = 500; -std::string GetWindowTitle(CGWindowID id) { - CFArrayRef window_id_array = - CFArrayCreate(NULL, reinterpret_cast(&id), 1, NULL); - CFArrayRef window_array = - CGWindowListCreateDescriptionFromArray(window_id_array); - std::string title; - - if (window_array && CFArrayGetCount(window_array)) { - CFDictionaryRef window = reinterpret_cast( - CFArrayGetValueAtIndex(window_array, 0)); - CFStringRef title_ref = reinterpret_cast( - CFDictionaryGetValue(window, kCGWindowName)); - - if (title_ref) - rtc::ToUtf8(title_ref, &title); - } - CFRelease(window_id_array); - CFRelease(window_array); - - return title; -} - -int GetWindowOwnerPid(CGWindowID id) { - CFArrayRef window_id_array = - CFArrayCreate(NULL, reinterpret_cast(&id), 1, NULL); - CFArrayRef window_array = - CGWindowListCreateDescriptionFromArray(window_id_array); - int pid = 0; - - if (window_array && CFArrayGetCount(window_array)) { - CFDictionaryRef window = reinterpret_cast( - CFArrayGetValueAtIndex(window_array, 0)); - CFNumberRef pid_ref = reinterpret_cast( - CFDictionaryGetValue(window, kCGWindowOwnerPID)); - - if (pid_ref) - CFNumberGetValue(pid_ref, kCFNumberIntType, &pid); - } - CFRelease(window_id_array); - CFRelease(window_array); - - return pid; -} - // Returns the window that is full-screen and has the same title and owner pid // as the input window. CGWindowID FindFullScreenWindowWithSamePidAndTitle(CGWindowID id) { - int pid = GetWindowOwnerPid(id); + const int pid = GetWindowOwnerPid(id); std::string title = GetWindowTitle(id); + if (title.empty()) + return kCGNullWindowID; // Only get on screen, non-desktop windows. CFArrayRef window_array = CGWindowListCopyWindowInfo( @@ -92,29 +50,18 @@ CGWindowID FindFullScreenWindowWithSamePidAndTitle(CGWindowID id) { for (CFIndex i = 0; i < count; ++i) { CFDictionaryRef window = reinterpret_cast( CFArrayGetValueAtIndex(window_array, i)); - CFStringRef window_title_ref = reinterpret_cast( - CFDictionaryGetValue(window, kCGWindowName)); - CFNumberRef window_id_ref = reinterpret_cast( - CFDictionaryGetValue(window, kCGWindowNumber)); - CFNumberRef window_pid_ref = reinterpret_cast( - CFDictionaryGetValue(window, kCGWindowOwnerPID)); - if (!window_title_ref || !window_id_ref || !window_pid_ref) + CGWindowID window_id = GetWindowId(window); + if (window_id == kNullWindowId) continue; - int window_pid = 0; - CFNumberGetValue(window_pid_ref, kCFNumberIntType, &window_pid); - if (window_pid != pid) + if (GetWindowOwnerPid(window) != pid) continue; - std::string window_title; - if (!rtc::ToUtf8(window_title_ref, &window_title) || - window_title != title) { + std::string window_title = GetWindowTitle(window); + if (window_title != title) continue; - } - CGWindowID window_id; - CFNumberGetValue(window_id_ref, kCFNumberIntType, &window_id); if (IsWindowFullScreen(desktop_config, window)) { full_screen_window = window_id; break; diff --git a/modules/desktop_capture/mac/screen_capturer_mac.mm b/modules/desktop_capture/mac/screen_capturer_mac.mm index 8f9a9d34af..41d19cd18f 100644 --- a/modules/desktop_capture/mac/screen_capturer_mac.mm +++ b/modules/desktop_capture/mac/screen_capturer_mac.mm @@ -13,6 +13,7 @@ #include "modules/desktop_capture/mac/screen_capturer_mac.h" #include "modules/desktop_capture/mac/desktop_frame_provider.h" +#include "modules/desktop_capture/mac/window_list_utils.h" #include "rtc_base/checks.h" #include "rtc_base/constructor_magic.h" #include "rtc_base/logging.h" @@ -75,11 +76,7 @@ CFArrayRef CreateWindowListWithExclusion(CGWindowID window_to_exclude) { CFDictionaryRef window = reinterpret_cast(CFArrayGetValueAtIndex(all_windows, i)); - CFNumberRef id_ref = - reinterpret_cast(CFDictionaryGetValue(window, kCGWindowNumber)); - - CGWindowID id; - CFNumberGetValue(id_ref, kCFNumberIntType, &id); + CGWindowID id = GetWindowId(window); if (id == window_to_exclude) { found = true; continue; diff --git a/modules/desktop_capture/mac/window_list_utils.cc b/modules/desktop_capture/mac/window_list_utils.cc index 6e1fc3f933..5899530bb4 100644 --- a/modules/desktop_capture/mac/window_list_utils.cc +++ b/modules/desktop_capture/mac/window_list_utils.cc @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "rtc_base/checks.h" #include "rtc_base/mac_utils.h" @@ -78,11 +80,8 @@ bool GetWindowList(rtc::FunctionView on_window, if (!window_array) return false; - MacDesktopConfiguration desktop_config; - if (ignore_minimized) { - desktop_config = MacDesktopConfiguration::GetCurrent( - MacDesktopConfiguration::TopLeftOrigin); - } + MacDesktopConfiguration desktop_config = MacDesktopConfiguration::GetCurrent( + MacDesktopConfiguration::TopLeftOrigin); // Check windows to make sure they have an id, title, and use window layer // other than 0. @@ -94,12 +93,6 @@ bool GetWindowList(rtc::FunctionView on_window, continue; } - CFStringRef window_title = reinterpret_cast( - CFDictionaryGetValue(window, kCGWindowName)); - if (!window_title) { - continue; - } - CFNumberRef window_id = reinterpret_cast( CFDictionaryGetValue(window, kCGWindowNumber)); if (!window_id) { @@ -127,6 +120,15 @@ bool GetWindowList(rtc::FunctionView on_window, continue; } + // If window title is empty, only consider it if it is either on screen or + // fullscreen. + CFStringRef window_title = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + if (!window_title && !IsWindowOnScreen(window) && + !IsWindowFullScreen(desktop_config, window)) { + continue; + } + if (!on_window(window)) { break; } @@ -138,16 +140,66 @@ bool GetWindowList(rtc::FunctionView on_window, 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}); + // Use a std::list so that iterators are preversed upon insertion and + // deletion. + std::list sources; + std::map::const_iterator> pid_itr_map; + const bool ret = GetWindowList( + [&sources, &pid_itr_map](CFDictionaryRef window) { + WindowId window_id = GetWindowId(window); + if (window_id != kNullWindowId) { + const std::string title = GetWindowTitle(window); + const int pid = GetWindowOwnerPid(window); + // Check if window for the same pid have been already inserted. + std::map::const_iterator>::iterator + itr = pid_itr_map.find(pid); + + // Only consider empty titles if the app has no other window with a + // proper title. + if (title.empty()) { + std::string owner_name = GetWindowOwnerName(window); + + // At this time we do not know if there will be other windows + // for the same pid unless they have been already inserted, hence + // the check in the map. Also skip the window if owner name is + // empty too. + if (!owner_name.empty() && (itr == pid_itr_map.end())) { + sources.push_back(DesktopCapturer::Source{window_id, owner_name}); + RTC_DCHECK(!sources.empty()); + // Get an iterator on the last valid element in the source list. + std::list::const_iterator last_source = + --sources.end(); + pid_itr_map.insert( + std::pair::const_iterator>( + pid, last_source)); + } + } else { + sources.push_back(DesktopCapturer::Source{window_id, title}); + // Once the window with empty title has been removed no other empty + // windows are allowed for the same pid. + if (itr != pid_itr_map.end() && (itr->second != sources.end())) { + sources.erase(itr->second); + // sdt::list::end() never changes during the lifetime of that + // list. + itr->second = sources.end(); + } + } } return true; }, ignore_minimized); + + if (!ret) + return false; + + RTC_DCHECK(windows); + windows->reserve(windows->size() + sources.size()); + std::copy(std::begin(sources), std::end(sources), + std::back_inserter(*windows)); + + return true; } // Returns true if the window is occupying a full screen. @@ -198,6 +250,37 @@ std::string GetWindowTitle(CFDictionaryRef window) { if (title && rtc::ToUtf8(title, &result)) { return result; } + + return std::string(); +} + +std::string GetWindowTitle(CGWindowID id) { + std::string title; + if (GetWindowRef(id, [&title](CFDictionaryRef window) { + title = GetWindowTitle(window); + })) { + return title; + } + return std::string(); +} + +std::string GetWindowOwnerName(CFDictionaryRef window) { + CFStringRef owner_name = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowOwnerName)); + std::string result; + if (owner_name && rtc::ToUtf8(owner_name, &result)) { + return result; + } + return std::string(); +} + +std::string GetWindowOwnerName(CGWindowID id) { + std::string owner_name; + if (GetWindowRef(id, [&owner_name](CFDictionaryRef window) { + owner_name = GetWindowOwnerPid(window); + })) { + return owner_name; + } return std::string(); } @@ -219,6 +302,31 @@ WindowId GetWindowId(CFDictionaryRef window) { return id; } +int GetWindowOwnerPid(CFDictionaryRef window) { + CFNumberRef window_pid = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowOwnerPID)); + if (!window_pid) { + return 0; + } + + int pid; + if (!CFNumberGetValue(window_pid, kCFNumberIntType, &pid)) { + return 0; + } + + return pid; +} + +int GetWindowOwnerPid(CGWindowID id) { + int pid; + if (GetWindowRef(id, [&pid](CFDictionaryRef window) { + pid = GetWindowOwnerPid(window); + })) { + return pid; + } + return 0; +} + float GetScaleFactorAtPosition(const MacDesktopConfiguration& desktop_config, DesktopVector position) { // Find the dpi to physical pixel scale for the screen where the mouse cursor diff --git a/modules/desktop_capture/mac/window_list_utils.h b/modules/desktop_capture/mac/window_list_utils.h index ea622c472b..ff9ad14872 100644 --- a/modules/desktop_capture/mac/window_list_utils.h +++ b/modules/desktop_capture/mac/window_list_utils.h @@ -48,10 +48,30 @@ bool IsWindowOnScreen(CGWindowID id); // valid title can be retrieved, this function returns an empty string. std::string GetWindowTitle(CFDictionaryRef window); +// Returns utf-8 encoded title of window |id|. If |id| cannot be found or no +// valid title can be retrieved, this function returns an empty string. +std::string GetWindowTitle(CGWindowID id); + +// Returns utf-8 encoded owner name of |window|. If |window| is not a window or +// if no valid owner name can be retrieved, returns an empty string. +std::string GetWindowOwnerName(CFDictionaryRef window); + +// Returns utf-8 encoded owner name of the given window |id|. If |id| cannot be +// found or if no valid owner name can be retrieved, returns an empty string. +std::string GetWindowOwnerName(CGWindowID id); + // 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 pid of the process owning |window|. Return 0 if |window| is not +// a window or no valid owner can be retrieved. +int GetWindowOwnerPid(CFDictionaryRef window); + +// Returns the pid of the process owning the window |id|. Return 0 if |id| +// cannot be found or no valid owner can be retrieved. +int GetWindowOwnerPid(CGWindowID id); + // Returns the DIP to physical pixel scale at |position|. |position| is in // *unscaled* system coordinate, i.e. it's device-independent and the primary // monitor starts from (0, 0). If |position| is out of the system display, this