blob: 1c74ed97ab0bdfaf2e32f16b32173eb5e31bd3eb [file] [log] [blame]
/*
* Copyright (c) 2014 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/mac/full_screen_chrome_window_detector.h"
#include <assert.h>
#include <libproc.h>
#include <string>
#include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
#include "webrtc/rtc_base/logging.h"
#include "webrtc/rtc_base/macutils.h"
#include "webrtc/rtc_base/timeutils.h"
namespace webrtc {
namespace {
const int64_t kUpdateIntervalMs = 500;
std::string GetWindowTitle(CGWindowID id) {
CFArrayRef window_id_array =
CFArrayCreate(NULL, reinterpret_cast<const void **>(&id), 1, NULL);
CFArrayRef window_array =
CGWindowListCreateDescriptionFromArray(window_id_array);
std::string title;
if (window_array && CFArrayGetCount(window_array)) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, 0));
CFStringRef title_ref = reinterpret_cast<CFStringRef>(
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<const void **>(&id), 1, NULL);
CFArrayRef window_array =
CGWindowListCreateDescriptionFromArray(window_id_array);
int pid = 0;
if (window_array && CFArrayGetCount(window_array)) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, 0));
CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
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);
std::string title = GetWindowTitle(id);
// Only get on screen, non-desktop windows.
CFArrayRef window_array = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
kCGNullWindowID);
if (!window_array)
return kCGNullWindowID;
CGWindowID full_screen_window = kCGNullWindowID;
MacDesktopConfiguration desktop_config = MacDesktopConfiguration::GetCurrent(
MacDesktopConfiguration::TopLeftOrigin);
// 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) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, i));
CFStringRef window_title_ref = reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(window, kCGWindowName));
CFNumberRef window_id_ref = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(window, kCGWindowNumber));
CFNumberRef window_pid_ref = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(window, kCGWindowOwnerPID));
if (!window_title_ref || !window_id_ref || !window_pid_ref)
continue;
int window_pid = 0;
CFNumberGetValue(window_pid_ref, kCFNumberIntType, &window_pid);
if (window_pid != pid)
continue;
std::string window_title;
if (!rtc::ToUtf8(window_title_ref, &window_title) ||
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;
}
}
CFRelease(window_array);
return full_screen_window;
}
bool IsChromeWindow(CGWindowID id) {
int pid = GetWindowOwnerPid(id);
char buffer[PROC_PIDPATHINFO_MAXSIZE];
int path_length = proc_pidpath(pid, buffer, sizeof(buffer));
if (path_length <= 0)
return false;
const char* last_slash = strrchr(buffer, '/');
std::string name(last_slash ? last_slash + 1 : buffer);
return name.find("Google Chrome") == 0 || name == "Chromium";
}
} // namespace
FullScreenChromeWindowDetector::FullScreenChromeWindowDetector()
: ref_count_(0), last_update_time_ns_(0) {}
FullScreenChromeWindowDetector::~FullScreenChromeWindowDetector() {}
CGWindowID FullScreenChromeWindowDetector::FindFullScreenWindow(
CGWindowID original_window) {
if (!IsChromeWindow(original_window) || IsWindowOnScreen(original_window))
return kCGNullWindowID;
CGWindowID full_screen_window_id =
FindFullScreenWindowWithSamePidAndTitle(original_window);
if (full_screen_window_id == kCGNullWindowID)
return kCGNullWindowID;
for (const auto& window : previous_window_list_) {
if (static_cast<CGWindowID>(window.id) != full_screen_window_id)
continue;
LOG(LS_WARNING) << "The full-screen window exists in the list.";
return kCGNullWindowID;
}
return full_screen_window_id;
}
void FullScreenChromeWindowDetector::UpdateWindowListIfNeeded(
CGWindowID original_window) {
if (IsChromeWindow(original_window) &&
(rtc::TimeNanos() - last_update_time_ns_) / rtc::kNumNanosecsPerMillisec
> kUpdateIntervalMs) {
previous_window_list_.clear();
previous_window_list_.swap(current_window_list_);
// No need to update the window list when the window is minimized.
if (!IsWindowOnScreen(original_window)) {
previous_window_list_.clear();
return;
}
GetWindowList(&current_window_list_, false);
last_update_time_ns_ = rtc::TimeNanos();
}
}
} // namespace webrtc