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