xref: /aosp_15_r20/external/webrtc/modules/desktop_capture/linux/x11/window_list_utils.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2017 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/linux/x11/window_list_utils.h"
12 
13 #include <X11/Xlib.h>
14 #include <X11/Xutil.h>
15 #include <string.h>
16 
17 #include <algorithm>
18 
19 #include "modules/desktop_capture/linux/x11/x_error_trap.h"
20 #include "modules/desktop_capture/linux/x11/x_window_property.h"
21 #include "rtc_base/checks.h"
22 #include "rtc_base/logging.h"
23 
24 namespace webrtc {
25 
26 namespace {
27 
28 class DeferXFree {
29  public:
DeferXFree(void * data)30   explicit DeferXFree(void* data) : data_(data) {}
31   ~DeferXFree();
32 
33  private:
34   void* const data_;
35 };
36 
~DeferXFree()37 DeferXFree::~DeferXFree() {
38   if (data_)
39     XFree(data_);
40 }
41 
42 // Iterates through `window` hierarchy to find first visible window, i.e. one
43 // that has WM_STATE property set to NormalState.
44 // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
GetApplicationWindow(XAtomCache * cache,::Window window)45 ::Window GetApplicationWindow(XAtomCache* cache, ::Window window) {
46   int32_t state = GetWindowState(cache, window);
47   if (state == NormalState) {
48     // Window has WM_STATE==NormalState. Return it.
49     return window;
50   } else if (state == IconicState) {
51     // Window is in minimized. Skip it.
52     return 0;
53   }
54 
55   RTC_DCHECK_EQ(state, WithdrawnState);
56   // If the window is in WithdrawnState then look at all of its children.
57   ::Window root, parent;
58   ::Window* children;
59   unsigned int num_children;
60   if (!XQueryTree(cache->display(), window, &root, &parent, &children,
61                   &num_children)) {
62     RTC_LOG(LS_ERROR) << "Failed to query for child windows although window"
63                          "does not have a valid WM_STATE.";
64     return 0;
65   }
66   ::Window app_window = 0;
67   for (unsigned int i = 0; i < num_children; ++i) {
68     app_window = GetApplicationWindow(cache, children[i]);
69     if (app_window)
70       break;
71   }
72 
73   if (children)
74     XFree(children);
75   return app_window;
76 }
77 
78 // Returns true if the `window` is a desktop element.
IsDesktopElement(XAtomCache * cache,::Window window)79 bool IsDesktopElement(XAtomCache* cache, ::Window window) {
80   RTC_DCHECK(cache);
81   if (window == 0)
82     return false;
83 
84   // First look for _NET_WM_WINDOW_TYPE. The standard
85   // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
86   // says this hint *should* be present on all windows, and we use the existence
87   // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
88   // a desktop element (that is, only "normal" windows should be shareable).
89   XWindowProperty<uint32_t> window_type(cache->display(), window,
90                                         cache->WindowType());
91   if (window_type.is_valid() && window_type.size() > 0) {
92     uint32_t* end = window_type.data() + window_type.size();
93     bool is_normal =
94         (end != std::find(window_type.data(), end, cache->WindowTypeNormal()));
95     return !is_normal;
96   }
97 
98   // Fall back on using the hint.
99   XClassHint class_hint;
100   Status status = XGetClassHint(cache->display(), window, &class_hint);
101   if (status == 0) {
102     // No hints, assume this is a normal application window.
103     return false;
104   }
105 
106   DeferXFree free_res_name(class_hint.res_name);
107   DeferXFree free_res_class(class_hint.res_class);
108   return strcmp("gnome-panel", class_hint.res_name) == 0 ||
109          strcmp("desktop_window", class_hint.res_name) == 0;
110 }
111 
112 }  // namespace
113 
GetWindowState(XAtomCache * cache,::Window window)114 int32_t GetWindowState(XAtomCache* cache, ::Window window) {
115   // Get WM_STATE property of the window.
116   XWindowProperty<uint32_t> window_state(cache->display(), window,
117                                          cache->WmState());
118 
119   // WM_STATE is considered to be set to WithdrawnState when it missing.
120   return window_state.is_valid() ? *window_state.data() : WithdrawnState;
121 }
122 
GetWindowList(XAtomCache * cache,rtc::FunctionView<bool (::Window)> on_window)123 bool GetWindowList(XAtomCache* cache,
124                    rtc::FunctionView<bool(::Window)> on_window) {
125   RTC_DCHECK(cache);
126   RTC_DCHECK(on_window);
127   ::Display* const display = cache->display();
128 
129   int failed_screens = 0;
130   const int num_screens = XScreenCount(display);
131   for (int screen = 0; screen < num_screens; screen++) {
132     ::Window root_window = XRootWindow(display, screen);
133     ::Window parent;
134     ::Window* children;
135     unsigned int num_children;
136     {
137       XErrorTrap error_trap(display);
138       if (XQueryTree(display, root_window, &root_window, &parent, &children,
139                      &num_children) == 0 ||
140           error_trap.GetLastErrorAndDisable() != 0) {
141         failed_screens++;
142         RTC_LOG(LS_ERROR) << "Failed to query for child windows for screen "
143                           << screen;
144         continue;
145       }
146     }
147 
148     DeferXFree free_children(children);
149 
150     for (unsigned int i = 0; i < num_children; i++) {
151       // Iterates in reverse order to return windows from front to back.
152       ::Window app_window =
153           GetApplicationWindow(cache, children[num_children - 1 - i]);
154       if (app_window && !IsDesktopElement(cache, app_window)) {
155         if (!on_window(app_window)) {
156           return true;
157         }
158       }
159     }
160   }
161 
162   return failed_screens < num_screens;
163 }
164 
GetWindowRect(::Display * display,::Window window,DesktopRect * rect,XWindowAttributes * attributes)165 bool GetWindowRect(::Display* display,
166                    ::Window window,
167                    DesktopRect* rect,
168                    XWindowAttributes* attributes /* = nullptr */) {
169   XWindowAttributes local_attributes;
170   int offset_x;
171   int offset_y;
172   if (attributes == nullptr) {
173     attributes = &local_attributes;
174   }
175 
176   {
177     XErrorTrap error_trap(display);
178     if (!XGetWindowAttributes(display, window, attributes) ||
179         error_trap.GetLastErrorAndDisable() != 0) {
180       return false;
181     }
182   }
183   *rect = DesktopRectFromXAttributes(*attributes);
184 
185   {
186     XErrorTrap error_trap(display);
187     ::Window child;
188     if (!XTranslateCoordinates(display, window, attributes->root, -rect->left(),
189                                -rect->top(), &offset_x, &offset_y, &child) ||
190         error_trap.GetLastErrorAndDisable() != 0) {
191       return false;
192     }
193   }
194   rect->Translate(offset_x, offset_y);
195   return true;
196 }
197 
198 }  // namespace webrtc
199