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