1 /*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/desktop_capture/mac/full_screen_mac_application_handler.h"
12 #include <libproc.h>
13 #include <algorithm>
14 #include <functional>
15 #include <string>
16 #include "absl/strings/match.h"
17 #include "modules/desktop_capture/mac/window_list_utils.h"
18
19 namespace webrtc {
20 namespace {
21
22 static constexpr const char* kPowerPointSlideShowTitles[] = {
23 u8"PowerPoint-Bildschirmpräsentation",
24 u8"Προβολή παρουσίασης PowerPoint",
25 u8"PowerPoint スライド ショー",
26 u8"PowerPoint Slide Show",
27 u8"PowerPoint 幻灯片放映",
28 u8"Presentación de PowerPoint",
29 u8"PowerPoint-slideshow",
30 u8"Presentazione di PowerPoint",
31 u8"Prezentácia programu PowerPoint",
32 u8"Apresentação do PowerPoint",
33 u8"PowerPoint-bildspel",
34 u8"Prezentace v aplikaci PowerPoint",
35 u8"PowerPoint 슬라이드 쇼",
36 u8"PowerPoint-lysbildefremvisning",
37 u8"PowerPoint-vetítés",
38 u8"PowerPoint Slayt Gösterisi",
39 u8"Pokaz slajdów programu PowerPoint",
40 u8"PowerPoint 投影片放映",
41 u8"Демонстрация PowerPoint",
42 u8"Diaporama PowerPoint",
43 u8"PowerPoint-diaesitys",
44 u8"Peragaan Slide PowerPoint",
45 u8"PowerPoint-diavoorstelling",
46 u8"การนำเสนอสไลด์ PowerPoint",
47 u8"Apresentação de slides do PowerPoint",
48 u8"הצגת שקופיות של PowerPoint",
49 u8"عرض شرائح في PowerPoint"};
50
51 class FullScreenMacApplicationHandler : public FullScreenApplicationHandler {
52 public:
53 using TitlePredicate =
54 std::function<bool(const std::string&, const std::string&)>;
55
FullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId,TitlePredicate title_predicate)56 FullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId,
57 TitlePredicate title_predicate)
58 : FullScreenApplicationHandler(sourceId),
59 title_predicate_(title_predicate),
60 owner_pid_(GetWindowOwnerPid(sourceId)) {}
61
InvalidateCacheIfNeeded(const DesktopCapturer::SourceList & source_list,int64_t timestamp) const62 void InvalidateCacheIfNeeded(const DesktopCapturer::SourceList& source_list,
63 int64_t timestamp) const {
64 // Copy only sources with the same pid
65 if (timestamp != cache_timestamp_) {
66 cache_sources_.clear();
67 std::copy_if(source_list.begin(), source_list.end(),
68 std::back_inserter(cache_sources_),
69 [&](const DesktopCapturer::Source& src) {
70 return src.id != GetSourceId() &&
71 GetWindowOwnerPid(src.id) == owner_pid_;
72 });
73 cache_timestamp_ = timestamp;
74 }
75 }
76
FindFullScreenWindowWithSamePid(const DesktopCapturer::SourceList & source_list,int64_t timestamp) const77 WindowId FindFullScreenWindowWithSamePid(
78 const DesktopCapturer::SourceList& source_list,
79 int64_t timestamp) const {
80 InvalidateCacheIfNeeded(source_list, timestamp);
81 if (cache_sources_.empty())
82 return kCGNullWindowID;
83
84 const auto original_window = GetSourceId();
85 const std::string title = GetWindowTitle(original_window);
86
87 // We can ignore any windows with empty titles cause regardless type of
88 // application it's impossible to verify that full screen window and
89 // original window are related to the same document.
90 if (title.empty())
91 return kCGNullWindowID;
92
93 MacDesktopConfiguration desktop_config =
94 MacDesktopConfiguration::GetCurrent(
95 MacDesktopConfiguration::TopLeftOrigin);
96
97 const auto it = std::find_if(
98 cache_sources_.begin(), cache_sources_.end(),
99 [&](const DesktopCapturer::Source& src) {
100 const std::string window_title = GetWindowTitle(src.id);
101
102 if (window_title.empty())
103 return false;
104
105 if (title_predicate_ && !title_predicate_(title, window_title))
106 return false;
107
108 return IsWindowFullScreen(desktop_config, src.id);
109 });
110
111 return it != cache_sources_.end() ? it->id : 0;
112 }
113
FindFullScreenWindow(const DesktopCapturer::SourceList & source_list,int64_t timestamp) const114 DesktopCapturer::SourceId FindFullScreenWindow(
115 const DesktopCapturer::SourceList& source_list,
116 int64_t timestamp) const override {
117 return IsWindowOnScreen(GetSourceId())
118 ? 0
119 : FindFullScreenWindowWithSamePid(source_list, timestamp);
120 }
121
122 private:
123 const TitlePredicate title_predicate_;
124 const int owner_pid_;
125 mutable int64_t cache_timestamp_ = 0;
126 mutable DesktopCapturer::SourceList cache_sources_;
127 };
128
equal_title_predicate(const std::string & original_title,const std::string & title)129 bool equal_title_predicate(const std::string& original_title,
130 const std::string& title) {
131 return original_title == title;
132 }
133
slide_show_title_predicate(const std::string & original_title,const std::string & title)134 bool slide_show_title_predicate(const std::string& original_title,
135 const std::string& title) {
136 if (title.find(original_title) == std::string::npos)
137 return false;
138
139 for (const char* pp_slide_title : kPowerPointSlideShowTitles) {
140 if (absl::StartsWith(title, pp_slide_title))
141 return true;
142 }
143 return false;
144 }
145
146 } // namespace
147
148 std::unique_ptr<FullScreenApplicationHandler>
CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId)149 CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId) {
150 std::unique_ptr<FullScreenApplicationHandler> result;
151 int pid = GetWindowOwnerPid(sourceId);
152 char buffer[PROC_PIDPATHINFO_MAXSIZE];
153 int path_length = proc_pidpath(pid, buffer, sizeof(buffer));
154 if (path_length > 0) {
155 const char* last_slash = strrchr(buffer, '/');
156 const std::string name{last_slash ? last_slash + 1 : buffer};
157 FullScreenMacApplicationHandler::TitlePredicate predicate = nullptr;
158 if (name.find("Google Chrome") == 0 || name == "Chromium") {
159 predicate = equal_title_predicate;
160 } else if (name == "Microsoft PowerPoint") {
161 predicate = slide_show_title_predicate;
162 } else if (name == "Keynote") {
163 predicate = equal_title_predicate;
164 }
165
166 if (predicate) {
167 result.reset(new FullScreenMacApplicationHandler(sourceId, predicate));
168 }
169 }
170
171 return result;
172 }
173
174 } // namespace webrtc
175