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>
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/posix/eintr_wrapper.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/threading/non_thread_safe.h"
23 #include "remoting/host/client_session_control.h"
24 #include "third_party/skia/include/core/SkPoint.h"
26 // These includes need to be later than dictated by the style guide due to
27 // Xlib header pollution, specifically the min, max, and Status macros.
28 #include <X11/XKBlib.h>
29 #include <X11/Xlibint.h>
30 #include <X11/extensions/record.h>
36 class LocalInputMonitorLinux
: public base::NonThreadSafe
,
37 public LocalInputMonitor
{
39 LocalInputMonitorLinux(
40 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
41 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
42 base::WeakPtr
<ClientSessionControl
> client_session_control
);
43 virtual ~LocalInputMonitorLinux();
46 // The actual implementation resides in LocalInputMonitorLinux::Core class.
48 : public base::RefCountedThreadSafe
<Core
>,
49 public base::MessagePumpLibevent::Watcher
{
51 Core(scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
52 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
53 base::WeakPtr
<ClientSessionControl
> client_session_control
);
59 friend class base::RefCountedThreadSafe
<Core
>;
62 void StartOnInputThread();
63 void StopOnInputThread();
65 // base::MessagePumpLibevent::Watcher interface.
66 virtual void OnFileCanReadWithoutBlocking(int fd
) OVERRIDE
;
67 virtual void OnFileCanWriteWithoutBlocking(int fd
) OVERRIDE
;
69 // Processes key and mouse events.
70 void ProcessXEvent(xEvent
* event
);
72 static void ProcessReply(XPointer self
, XRecordInterceptData
* data
);
74 // Task runner on which public methods of this class must be called.
75 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner_
;
77 // Task runner on which X Window events are received.
78 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner_
;
80 // Points to the object receiving mouse event notifications and session
81 // disconnect requests.
82 base::WeakPtr
<ClientSessionControl
> client_session_control_
;
84 // Used to receive base::MessagePumpLibevent::Watcher events.
85 base::MessagePumpLibevent::FileDescriptorWatcher controller_
;
87 // True when Alt is pressed.
90 // True when Ctrl is pressed.
94 Display
* x_record_display_
;
95 XRecordRange
* x_record_range_
[2];
96 XRecordContext x_record_context_
;
98 DISALLOW_COPY_AND_ASSIGN(Core
);
101 scoped_refptr
<Core
> core_
;
103 DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorLinux
);
106 LocalInputMonitorLinux::LocalInputMonitorLinux(
107 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
108 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
109 base::WeakPtr
<ClientSessionControl
> client_session_control
)
110 : core_(new Core(caller_task_runner
,
112 client_session_control
)) {
116 LocalInputMonitorLinux::~LocalInputMonitorLinux() {
120 LocalInputMonitorLinux::Core::Core(
121 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
122 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
123 base::WeakPtr
<ClientSessionControl
> client_session_control
)
124 : caller_task_runner_(caller_task_runner
),
125 input_task_runner_(input_task_runner
),
126 client_session_control_(client_session_control
),
128 ctrl_pressed_(false),
130 x_record_display_(NULL
),
131 x_record_context_(0) {
132 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
133 DCHECK(client_session_control_
.get());
135 x_record_range_
[0] = NULL
;
136 x_record_range_
[1] = NULL
;
139 void LocalInputMonitorLinux::Core::Start() {
140 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
142 input_task_runner_
->PostTask(FROM_HERE
,
143 base::Bind(&Core::StartOnInputThread
, this));
146 void LocalInputMonitorLinux::Core::Stop() {
147 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
149 input_task_runner_
->PostTask(FROM_HERE
,
150 base::Bind(&Core::StopOnInputThread
, this));
153 LocalInputMonitorLinux::Core::~Core() {
155 DCHECK(!x_record_display_
);
156 DCHECK(!x_record_range_
[0]);
157 DCHECK(!x_record_range_
[1]);
158 DCHECK(!x_record_context_
);
161 void LocalInputMonitorLinux::Core::StartOnInputThread() {
162 DCHECK(input_task_runner_
->BelongsToCurrentThread());
164 DCHECK(!x_record_display_
);
165 DCHECK(!x_record_range_
[0]);
166 DCHECK(!x_record_range_
[1]);
167 DCHECK(!x_record_context_
);
169 // TODO(jamiewalch): We should pass the display in. At that point, since
170 // XRecord needs a private connection to the X Server for its data channel
171 // and both channels are used from a separate thread, we'll need to duplicate
172 // them with something like the following:
173 // XOpenDisplay(DisplayString(display));
174 display_
= XOpenDisplay(NULL
);
175 x_record_display_
= XOpenDisplay(NULL
);
176 if (!display_
|| !x_record_display_
) {
177 LOG(ERROR
) << "Couldn't open X display";
181 int xr_opcode
, xr_event
, xr_error
;
182 if (!XQueryExtension(display_
, "RECORD", &xr_opcode
, &xr_event
, &xr_error
)) {
183 LOG(ERROR
) << "X Record extension not available.";
187 x_record_range_
[0] = XRecordAllocRange();
188 x_record_range_
[1] = XRecordAllocRange();
189 if (!x_record_range_
[0] || !x_record_range_
[1]) {
190 LOG(ERROR
) << "XRecordAllocRange failed.";
193 x_record_range_
[0]->device_events
.first
= MotionNotify
;
194 x_record_range_
[0]->device_events
.last
= MotionNotify
;
195 x_record_range_
[1]->device_events
.first
= KeyPress
;
196 x_record_range_
[1]->device_events
.last
= KeyRelease
;
197 XRecordClientSpec client_spec
= XRecordAllClients
;
199 x_record_context_
= XRecordCreateContext(
200 x_record_display_
, 0, &client_spec
, 1, x_record_range_
,
201 arraysize(x_record_range_
));
202 if (!x_record_context_
) {
203 LOG(ERROR
) << "XRecordCreateContext failed.";
207 if (!XRecordEnableContextAsync(x_record_display_
, x_record_context_
,
209 reinterpret_cast<XPointer
>(this))) {
210 LOG(ERROR
) << "XRecordEnableContextAsync failed.";
214 // Register OnFileCanReadWithoutBlocking() to be called every time there is
215 // something to read from |x_record_display_|.
216 base::MessageLoopForIO
* message_loop
= base::MessageLoopForIO::current();
218 message_loop
->WatchFileDescriptor(ConnectionNumber(x_record_display_
),
220 base::MessageLoopForIO::WATCH_READ
,
224 LOG(ERROR
) << "Failed to create X record task.";
228 // Fetch pending events if any.
229 while (XPending(x_record_display_
)) {
231 XNextEvent(x_record_display_
, &ev
);
235 void LocalInputMonitorLinux::Core::StopOnInputThread() {
236 DCHECK(input_task_runner_
->BelongsToCurrentThread());
238 // Context must be disabled via the control channel because we can't send
239 // any X protocol traffic over the data channel while it's recording.
240 if (x_record_context_
) {
241 XRecordDisableContext(display_
, x_record_context_
);
245 controller_
.StopWatchingFileDescriptor();
247 if (x_record_range_
[0]) {
248 XFree(x_record_range_
[0]);
249 x_record_range_
[0] = NULL
;
251 if (x_record_range_
[1]) {
252 XFree(x_record_range_
[1]);
253 x_record_range_
[1] = NULL
;
255 if (x_record_context_
) {
256 XRecordFreeContext(x_record_display_
, x_record_context_
);
257 x_record_context_
= 0;
259 if (x_record_display_
) {
260 XCloseDisplay(x_record_display_
);
261 x_record_display_
= NULL
;
264 XCloseDisplay(display_
);
269 void LocalInputMonitorLinux::Core::OnFileCanReadWithoutBlocking(int fd
) {
270 DCHECK(input_task_runner_
->BelongsToCurrentThread());
272 // Fetch pending events if any.
273 while (XPending(x_record_display_
)) {
275 XNextEvent(x_record_display_
, &ev
);
279 void LocalInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd
) {
283 void LocalInputMonitorLinux::Core::ProcessXEvent(xEvent
* event
) {
284 DCHECK(input_task_runner_
->BelongsToCurrentThread());
286 if (event
->u
.u
.type
== MotionNotify
) {
287 SkIPoint
position(SkIPoint::Make(event
->u
.keyButtonPointer
.rootX
,
288 event
->u
.keyButtonPointer
.rootY
));
289 caller_task_runner_
->PostTask(
290 FROM_HERE
, base::Bind(&ClientSessionControl::OnLocalMouseMoved
,
291 client_session_control_
,
294 int key_code
= event
->u
.u
.detail
;
295 bool down
= event
->u
.u
.type
== KeyPress
;
296 KeySym key_sym
= XkbKeycodeToKeysym(display_
, key_code
, 0, 0);
297 if (key_sym
== XK_Control_L
|| key_sym
== XK_Control_R
) {
298 ctrl_pressed_
= down
;
299 } else if (key_sym
== XK_Alt_L
|| key_sym
== XK_Alt_R
) {
301 } else if (key_sym
== XK_Escape
&& down
&& alt_pressed_
&& ctrl_pressed_
) {
302 caller_task_runner_
->PostTask(
303 FROM_HERE
, base::Bind(&ClientSessionControl::DisconnectSession
,
304 client_session_control_
));
310 void LocalInputMonitorLinux::Core::ProcessReply(XPointer self
,
311 XRecordInterceptData
* data
) {
312 if (data
->category
== XRecordFromServer
) {
313 xEvent
* event
= reinterpret_cast<xEvent
*>(data
->data
);
314 reinterpret_cast<Core
*>(self
)->ProcessXEvent(event
);
316 XRecordFreeData(data
);
321 scoped_ptr
<LocalInputMonitor
> LocalInputMonitor::Create(
322 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
323 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
324 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
,
325 base::WeakPtr
<ClientSessionControl
> client_session_control
) {
326 return scoped_ptr
<LocalInputMonitor
>(
327 new LocalInputMonitorLinux(caller_task_runner
,
329 client_session_control
));
332 } // namespace remoting