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 229