Remove dependency on content from remoting_host.
[chromium-blink-merge.git] / remoting / host / local_input_monitor_x11.cc
blob9e410f8fdba9629159a47e520ee9c5871e11bfca
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/host/local_input_monitor.h"
7 #include <sys/select.h>
8 #include <unistd.h>
9 #define XK_MISCELLANY
10 #include <X11/keysymdef.h>
12 #include "base/basictypes.h"
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/compiler_specific.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/message_loop/message_pump_libevent.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/threading/non_thread_safe.h"
22 #include "remoting/host/client_session_control.h"
23 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
25 // These includes need to be later than dictated by the style guide due to
26 // Xlib header pollution, specifically the min, max, and Status macros.
27 #include <X11/XKBlib.h>
28 #include <X11/Xlibint.h>
29 #include <X11/extensions/record.h>
31 namespace remoting {
33 namespace {
35 class LocalInputMonitorX11 : public base::NonThreadSafe,
36 public LocalInputMonitor {
37 public:
38 LocalInputMonitorX11(
39 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
40 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
41 base::WeakPtr<ClientSessionControl> client_session_control);
42 ~LocalInputMonitorX11() override;
44 private:
45 // The actual implementation resides in LocalInputMonitorX11::Core class.
46 class Core
47 : public base::RefCountedThreadSafe<Core>,
48 public base::MessagePumpLibevent::Watcher {
49 public:
50 Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
51 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
52 base::WeakPtr<ClientSessionControl> client_session_control);
54 void Start();
55 void Stop();
57 private:
58 friend class base::RefCountedThreadSafe<Core>;
59 ~Core() override;
61 void StartOnInputThread();
62 void StopOnInputThread();
64 // base::MessagePumpLibevent::Watcher interface.
65 void OnFileCanReadWithoutBlocking(int fd) override;
66 void OnFileCanWriteWithoutBlocking(int fd) override;
68 // Processes key and mouse events.
69 void ProcessXEvent(xEvent* event);
71 static void ProcessReply(XPointer self, XRecordInterceptData* data);
73 // Task runner on which public methods of this class must be called.
74 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
76 // Task runner on which X Window events are received.
77 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_;
79 // Points to the object receiving mouse event notifications and session
80 // disconnect requests.
81 base::WeakPtr<ClientSessionControl> client_session_control_;
83 // Used to receive base::MessagePumpLibevent::Watcher events.
84 base::MessagePumpLibevent::FileDescriptorWatcher controller_;
86 // True when Alt is pressed.
87 bool alt_pressed_;
89 // True when Ctrl is pressed.
90 bool ctrl_pressed_;
92 Display* display_;
93 Display* x_record_display_;
94 XRecordRange* x_record_range_[2];
95 XRecordContext x_record_context_;
97 DISALLOW_COPY_AND_ASSIGN(Core);
100 scoped_refptr<Core> core_;
102 DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorX11);
105 LocalInputMonitorX11::LocalInputMonitorX11(
106 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
107 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
108 base::WeakPtr<ClientSessionControl> client_session_control)
109 : core_(new Core(caller_task_runner,
110 input_task_runner,
111 client_session_control)) {
112 core_->Start();
115 LocalInputMonitorX11::~LocalInputMonitorX11() {
116 core_->Stop();
119 LocalInputMonitorX11::Core::Core(
120 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
121 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
122 base::WeakPtr<ClientSessionControl> client_session_control)
123 : caller_task_runner_(caller_task_runner),
124 input_task_runner_(input_task_runner),
125 client_session_control_(client_session_control),
126 alt_pressed_(false),
127 ctrl_pressed_(false),
128 display_(nullptr),
129 x_record_display_(nullptr),
130 x_record_context_(0) {
131 DCHECK(caller_task_runner_->BelongsToCurrentThread());
132 DCHECK(client_session_control_.get());
134 x_record_range_[0] = nullptr;
135 x_record_range_[1] = nullptr;
138 void LocalInputMonitorX11::Core::Start() {
139 DCHECK(caller_task_runner_->BelongsToCurrentThread());
141 input_task_runner_->PostTask(FROM_HERE,
142 base::Bind(&Core::StartOnInputThread, this));
145 void LocalInputMonitorX11::Core::Stop() {
146 DCHECK(caller_task_runner_->BelongsToCurrentThread());
148 input_task_runner_->PostTask(FROM_HERE,
149 base::Bind(&Core::StopOnInputThread, this));
152 LocalInputMonitorX11::Core::~Core() {
153 DCHECK(!display_);
154 DCHECK(!x_record_display_);
155 DCHECK(!x_record_range_[0]);
156 DCHECK(!x_record_range_[1]);
157 DCHECK(!x_record_context_);
160 void LocalInputMonitorX11::Core::StartOnInputThread() {
161 DCHECK(input_task_runner_->BelongsToCurrentThread());
162 DCHECK(!display_);
163 DCHECK(!x_record_display_);
164 DCHECK(!x_record_range_[0]);
165 DCHECK(!x_record_range_[1]);
166 DCHECK(!x_record_context_);
168 // TODO(jamiewalch): We should pass the display in. At that point, since
169 // XRecord needs a private connection to the X Server for its data channel
170 // and both channels are used from a separate thread, we'll need to duplicate
171 // them with something like the following:
172 // XOpenDisplay(DisplayString(display));
173 display_ = XOpenDisplay(nullptr);
174 x_record_display_ = XOpenDisplay(nullptr);
175 if (!display_ || !x_record_display_) {
176 LOG(ERROR) << "Couldn't open X display";
177 return;
180 int xr_opcode, xr_event, xr_error;
181 if (!XQueryExtension(display_, "RECORD", &xr_opcode, &xr_event, &xr_error)) {
182 LOG(ERROR) << "X Record extension not available.";
183 return;
186 x_record_range_[0] = XRecordAllocRange();
187 x_record_range_[1] = XRecordAllocRange();
188 if (!x_record_range_[0] || !x_record_range_[1]) {
189 LOG(ERROR) << "XRecordAllocRange failed.";
190 return;
192 x_record_range_[0]->device_events.first = MotionNotify;
193 x_record_range_[0]->device_events.last = MotionNotify;
194 x_record_range_[1]->device_events.first = KeyPress;
195 x_record_range_[1]->device_events.last = KeyRelease;
196 XRecordClientSpec client_spec = XRecordAllClients;
198 x_record_context_ = XRecordCreateContext(
199 x_record_display_, 0, &client_spec, 1, x_record_range_,
200 arraysize(x_record_range_));
201 if (!x_record_context_) {
202 LOG(ERROR) << "XRecordCreateContext failed.";
203 return;
206 if (!XRecordEnableContextAsync(x_record_display_, x_record_context_,
207 &Core::ProcessReply,
208 reinterpret_cast<XPointer>(this))) {
209 LOG(ERROR) << "XRecordEnableContextAsync failed.";
210 return;
213 // Register OnFileCanReadWithoutBlocking() to be called every time there is
214 // something to read from |x_record_display_|.
215 base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
216 int result =
217 message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_),
218 true,
219 base::MessageLoopForIO::WATCH_READ,
220 &controller_,
221 this);
222 if (!result) {
223 LOG(ERROR) << "Failed to create X record task.";
224 return;
227 // Fetch pending events if any.
228 while (XPending(x_record_display_)) {
229 XEvent ev;
230 XNextEvent(x_record_display_, &ev);
234 void LocalInputMonitorX11::Core::StopOnInputThread() {
235 DCHECK(input_task_runner_->BelongsToCurrentThread());
237 // Context must be disabled via the control channel because we can't send
238 // any X protocol traffic over the data channel while it's recording.
239 if (x_record_context_) {
240 XRecordDisableContext(display_, x_record_context_);
241 XFlush(display_);
244 controller_.StopWatchingFileDescriptor();
246 if (x_record_range_[0]) {
247 XFree(x_record_range_[0]);
248 x_record_range_[0] = nullptr;
250 if (x_record_range_[1]) {
251 XFree(x_record_range_[1]);
252 x_record_range_[1] = nullptr;
254 if (x_record_context_) {
255 XRecordFreeContext(x_record_display_, x_record_context_);
256 x_record_context_ = 0;
258 if (x_record_display_) {
259 XCloseDisplay(x_record_display_);
260 x_record_display_ = nullptr;
262 if (display_) {
263 XCloseDisplay(display_);
264 display_ = nullptr;
268 void LocalInputMonitorX11::Core::OnFileCanReadWithoutBlocking(int fd) {
269 DCHECK(input_task_runner_->BelongsToCurrentThread());
271 // Fetch pending events if any.
272 while (XPending(x_record_display_)) {
273 XEvent ev;
274 XNextEvent(x_record_display_, &ev);
278 void LocalInputMonitorX11::Core::OnFileCanWriteWithoutBlocking(int fd) {
279 NOTREACHED();
282 void LocalInputMonitorX11::Core::ProcessXEvent(xEvent* event) {
283 DCHECK(input_task_runner_->BelongsToCurrentThread());
285 if (event->u.u.type == MotionNotify) {
286 webrtc::DesktopVector position(event->u.keyButtonPointer.rootX,
287 event->u.keyButtonPointer.rootY);
288 caller_task_runner_->PostTask(
289 FROM_HERE, base::Bind(&ClientSessionControl::OnLocalMouseMoved,
290 client_session_control_,
291 position));
292 } else {
293 int key_code = event->u.u.detail;
294 bool down = event->u.u.type == KeyPress;
295 KeySym key_sym = XkbKeycodeToKeysym(display_, key_code, 0, 0);
296 if (key_sym == XK_Control_L || key_sym == XK_Control_R) {
297 ctrl_pressed_ = down;
298 } else if (key_sym == XK_Alt_L || key_sym == XK_Alt_R) {
299 alt_pressed_ = down;
300 } else if (key_sym == XK_Escape && down && alt_pressed_ && ctrl_pressed_) {
301 caller_task_runner_->PostTask(
302 FROM_HERE, base::Bind(&ClientSessionControl::DisconnectSession,
303 client_session_control_));
308 // static
309 void LocalInputMonitorX11::Core::ProcessReply(XPointer self,
310 XRecordInterceptData* data) {
311 if (data->category == XRecordFromServer) {
312 xEvent* event = reinterpret_cast<xEvent*>(data->data);
313 reinterpret_cast<Core*>(self)->ProcessXEvent(event);
315 XRecordFreeData(data);
318 } // namespace
320 scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create(
321 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
322 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
323 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
324 base::WeakPtr<ClientSessionControl> client_session_control) {
325 return make_scoped_ptr(new LocalInputMonitorX11(
326 caller_task_runner, input_task_runner, client_session_control));
329 } // namespace remoting