Line data Source code
1 : /*
2 : * Copyright (c) 2013 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 <memory>
12 :
13 : #include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
14 :
15 : #include <X11/extensions/Xfixes.h>
16 : #include <X11/Xlib.h>
17 : #include <X11/Xutil.h>
18 :
19 : #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
20 : #include "webrtc/modules/desktop_capture/desktop_frame.h"
21 : #include "webrtc/modules/desktop_capture/mouse_cursor.h"
22 : #include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
23 : #include "webrtc/system_wrappers/include/logging.h"
24 :
25 : namespace {
26 :
27 : // WindowCapturer returns window IDs of X11 windows with WM_STATE attribute.
28 : // These windows may not be immediate children of the root window, because
29 : // window managers may re-parent them to add decorations. However,
30 : // XQueryPointer() expects to be passed children of the root. This function
31 : // searches up the list of the windows to find the root child that corresponds
32 : // to |window|.
33 0 : Window GetTopLevelWindow(Display* display, Window window) {
34 0 : webrtc::XErrorTrap error_trap(display);
35 : while (true) {
36 : // If the window is in WithdrawnState then look at all of its children.
37 : ::Window root, parent;
38 : ::Window *children;
39 : unsigned int num_children;
40 0 : if (!XQueryTree(display, window, &root, &parent, &children,
41 : &num_children)) {
42 0 : LOG(LS_ERROR) << "Failed to query for child windows although window"
43 0 : << "does not have a valid WM_STATE.";
44 0 : return None;
45 : }
46 0 : if (children)
47 0 : XFree(children);
48 :
49 0 : if (parent == root)
50 0 : break;
51 :
52 0 : window = parent;
53 0 : }
54 :
55 0 : return window;
56 : }
57 :
58 : } // namespace
59 :
60 : namespace webrtc {
61 :
62 : class MouseCursorMonitorX11 : public MouseCursorMonitor,
63 : public SharedXDisplay::XEventHandler {
64 : public:
65 : MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window, Window inner_window);
66 : ~MouseCursorMonitorX11() override;
67 :
68 : void Start(Callback* callback, Mode mode) override;
69 : void Stop() override;
70 : void Capture() override;
71 :
72 : private:
73 : // SharedXDisplay::XEventHandler interface.
74 : bool HandleXEvent(const XEvent& event) override;
75 :
76 0 : Display* display() { return x_display_->display(); }
77 :
78 : // Captures current cursor shape and stores it in |cursor_shape_|.
79 : void CaptureCursor();
80 :
81 : rtc::scoped_refptr<SharedXDisplay> x_display_;
82 : Callback* callback_;
83 : Mode mode_;
84 : Window window_;
85 : Window inner_window_;
86 :
87 : bool have_xfixes_;
88 : int xfixes_event_base_;
89 : int xfixes_error_base_;
90 :
91 : std::unique_ptr<MouseCursor> cursor_shape_;
92 : };
93 :
94 0 : MouseCursorMonitorX11::MouseCursorMonitorX11(
95 : const DesktopCaptureOptions& options,
96 0 : Window window, Window inner_window)
97 : : x_display_(options.x_display()),
98 : callback_(NULL),
99 : mode_(SHAPE_AND_POSITION),
100 : window_(window),
101 : inner_window_(inner_window),
102 : have_xfixes_(false),
103 : xfixes_event_base_(-1),
104 0 : xfixes_error_base_(-1) {
105 : // Set a default initial cursor shape in case XFixes is not present.
106 0 : const int kSize = 5;
107 : std::unique_ptr<DesktopFrame> default_cursor(
108 0 : new BasicDesktopFrame(DesktopSize(kSize, kSize)));
109 : const uint8_t pixels[kSize * kSize] = {
110 : 0x00, 0x00, 0x00, 0x00, 0x00,
111 : 0x00, 0xff, 0xff, 0xff, 0x00,
112 : 0x00, 0xff, 0xff, 0xff, 0x00,
113 : 0x00, 0xff, 0xff, 0xff, 0x00,
114 : 0x00, 0x00, 0x00, 0x00, 0x00
115 0 : };
116 0 : uint8_t* ptr = default_cursor->data();
117 0 : for (int y = 0; y < kSize; ++y) {
118 0 : for (int x = 0; x < kSize; ++x) {
119 0 : *ptr++ = pixels[kSize * y + x];
120 0 : *ptr++ = pixels[kSize * y + x];
121 0 : *ptr++ = pixels[kSize * y + x];
122 0 : *ptr++ = 0xff;
123 : }
124 : }
125 0 : DesktopVector hotspot(2, 2);
126 0 : cursor_shape_.reset(new MouseCursor(default_cursor.release(), hotspot));
127 0 : }
128 :
129 0 : MouseCursorMonitorX11::~MouseCursorMonitorX11() {
130 0 : Stop();
131 0 : }
132 :
133 0 : void MouseCursorMonitorX11::Start(Callback* callback, Mode mode) {
134 : // Start can be called only if not started
135 0 : RTC_DCHECK(!callback_);
136 0 : RTC_DCHECK(callback);
137 :
138 0 : callback_ = callback;
139 0 : mode_ = mode;
140 :
141 0 : have_xfixes_ =
142 0 : XFixesQueryExtension(display(), &xfixes_event_base_, &xfixes_error_base_);
143 :
144 0 : if (have_xfixes_) {
145 : // Register for changes to the cursor shape.
146 0 : XErrorTrap error_trap(display());
147 0 : XFixesSelectCursorInput(display(), window_, XFixesDisplayCursorNotifyMask);
148 0 : x_display_->AddEventHandler(xfixes_event_base_ + XFixesCursorNotify, this);
149 :
150 0 : CaptureCursor();
151 : } else {
152 0 : LOG(LS_INFO) << "X server does not support XFixes.";
153 : }
154 0 : }
155 :
156 0 : void MouseCursorMonitorX11::Stop() {
157 0 : callback_ = NULL;
158 0 : if (have_xfixes_) {
159 0 : x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify,
160 0 : this);
161 : }
162 0 : }
163 :
164 0 : void MouseCursorMonitorX11::Capture() {
165 0 : RTC_DCHECK(callback_);
166 :
167 : // Process X11 events in case XFixes has sent cursor notification.
168 0 : x_display_->ProcessPendingXEvents();
169 :
170 : // cursor_shape_| is set only if we were notified of a cursor shape change.
171 0 : if (cursor_shape_.get())
172 0 : callback_->OnMouseCursor(cursor_shape_.release());
173 :
174 : // Get cursor position if necessary.
175 0 : if (mode_ == SHAPE_AND_POSITION) {
176 : int root_x;
177 : int root_y;
178 : int win_x;
179 : int win_y;
180 : Window root_window;
181 : Window child_window;
182 : unsigned int mask;
183 :
184 0 : XErrorTrap error_trap(display());
185 0 : Bool result = XQueryPointer(display(), inner_window_, &root_window, &child_window,
186 0 : &root_x, &root_y, &win_x, &win_y, &mask);
187 : CursorState state;
188 0 : if (!result || error_trap.GetLastErrorAndDisable() != 0) {
189 0 : state = OUTSIDE;
190 : } else {
191 : // In screen mode (window_ == root_window) the mouse is always inside.
192 : // XQueryPointer() sets |child_window| to None if the cursor is outside
193 : // |window_|.
194 0 : state =
195 0 : (window_ == root_window || child_window != None) ? INSIDE : OUTSIDE;
196 : }
197 :
198 0 : callback_->OnMouseCursorPosition(state,
199 0 : webrtc::DesktopVector(win_x, win_y));
200 : }
201 0 : }
202 :
203 0 : bool MouseCursorMonitorX11::HandleXEvent(const XEvent& event) {
204 0 : if (have_xfixes_ && event.type == xfixes_event_base_ + XFixesCursorNotify) {
205 : const XFixesCursorNotifyEvent* cursor_event =
206 0 : reinterpret_cast<const XFixesCursorNotifyEvent*>(&event);
207 0 : if (cursor_event->subtype == XFixesDisplayCursorNotify) {
208 0 : CaptureCursor();
209 : }
210 : // Return false, even if the event has been handled, because there might be
211 : // other listeners for cursor notifications.
212 : }
213 0 : return false;
214 : }
215 :
216 0 : void MouseCursorMonitorX11::CaptureCursor() {
217 0 : RTC_DCHECK(have_xfixes_);
218 :
219 : XFixesCursorImage* img;
220 : {
221 0 : XErrorTrap error_trap(display());
222 0 : img = XFixesGetCursorImage(display());
223 0 : if (!img || error_trap.GetLastErrorAndDisable() != 0)
224 0 : return;
225 : }
226 :
227 : std::unique_ptr<DesktopFrame> image(
228 0 : new BasicDesktopFrame(DesktopSize(img->width, img->height)));
229 :
230 : // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
231 0 : unsigned long* src = img->pixels;
232 0 : uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
233 0 : uint32_t* dst_end = dst + (img->width * img->height);
234 0 : while (dst < dst_end) {
235 0 : *dst++ = static_cast<uint32_t>(*src++);
236 : }
237 :
238 0 : DesktopVector hotspot(std::min(img->width, img->xhot),
239 0 : std::min(img->height, img->yhot));
240 :
241 0 : XFree(img);
242 :
243 0 : cursor_shape_.reset(new MouseCursor(image.release(), hotspot));
244 : }
245 :
246 : // static
247 0 : MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
248 : const DesktopCaptureOptions& options, WindowId window) {
249 0 : if (!options.x_display())
250 0 : return NULL;
251 0 : WindowId outer_window = GetTopLevelWindow(options.x_display()->display(), window);
252 0 : if (outer_window == None)
253 0 : return NULL;
254 0 : return new MouseCursorMonitorX11(options, outer_window, window);
255 : }
256 :
257 0 : MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
258 : const DesktopCaptureOptions& options,
259 : ScreenId screen) {
260 0 : if (!options.x_display())
261 0 : return NULL;
262 0 : WindowId window = DefaultRootWindow(options.x_display()->display());
263 0 : return new MouseCursorMonitorX11(options, window, window);
264 : }
265 :
266 : } // namespace webrtc
|