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/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>
35 class LocalInputMonitorLinux
: public base::NonThreadSafe
,
36 public LocalInputMonitor
{
38 LocalInputMonitorLinux(
39 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
40 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
41 base::WeakPtr
<ClientSessionControl
> client_session_control
);
42 virtual ~LocalInputMonitorLinux();
45 // The actual implementation resides in LocalInputMonitorLinux::Core class.
47 : public base::RefCountedThreadSafe
<Core
>,
48 public base::MessagePumpLibevent::Watcher
{
50 Core(scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner
,
51 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
52 base::WeakPtr
<ClientSessionControl
> client_session_control
);
58 friend class base::RefCountedThreadSafe
<Core
>;
61 void StartOnInputThread();
62 void StopOnInputThread();
64 // base::MessagePumpLibevent::Watcher interface.
65 virtual void OnFileCanReadWithoutBlocking(int fd
) OVERRIDE
;
66 virtual 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.
89 // True when Ctrl is pressed.
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(LocalInputMonitorLinux
);
105 LocalInputMonitorLinux::LocalInputMonitorLinux(
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
,
111 client_session_control
)) {
115 LocalInputMonitorLinux::~LocalInputMonitorLinux() {
119 LocalInputMonitorLinux::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
),
127 ctrl_pressed_(false),
129 x_record_display_(NULL
),
130 x_record_context_(0) {
131 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
132 DCHECK(client_session_control_
.get());
134 x_record_range_
[0] = NULL
;
135 x_record_range_
[1] = NULL
;
138 void LocalInputMonitorLinux::Core::Start() {
139 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
141 input_task_runner_
->PostTask(FROM_HERE
,
142 base::Bind(&Core::StartOnInputThread
, this));
145 void LocalInputMonitorLinux::Core::Stop() {
146 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
148 input_task_runner_
->PostTask(FROM_HERE
,
149 base::Bind(&Core::StopOnInputThread
, this));
152 LocalInputMonitorLinux::Core::~Core() {
154 DCHECK(!x_record_display_
);
155 DCHECK(!x_record_range_
[0]);
156 DCHECK(!x_record_range_
[1]);
157 DCHECK(!x_record_context_
);
160 void LocalInputMonitorLinux::Core::StartOnInputThread() {
161 DCHECK(input_task_runner_
->BelongsToCurrentThread());
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(NULL
);
174 x_record_display_
= XOpenDisplay(NULL
);
175 if (!display_
|| !x_record_display_
) {
176 LOG(ERROR
) << "Couldn't open X display";
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.";
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.";
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.";
206 if (!XRecordEnableContextAsync(x_record_display_
, x_record_context_
,
208 reinterpret_cast<XPointer
>(this))) {
209 LOG(ERROR
) << "XRecordEnableContextAsync failed.";
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();
217 message_loop
->WatchFileDescriptor(ConnectionNumber(x_record_display_
),
219 base::MessageLoopForIO::WATCH_READ
,
223 LOG(ERROR
) << "Failed to create X record task.";
227 // Fetch pending events if any.
228 while (XPending(x_record_display_
)) {
230 XNextEvent(x_record_display_
, &ev
);
234 void LocalInputMonitorLinux::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_
);
244 controller_
.StopWatchingFileDescriptor();
246 if (x_record_range_
[0]) {
247 XFree(x_record_range_
[0]);
248 x_record_range_
[0] = NULL
;
250 if (x_record_range_
[1]) {
251 XFree(x_record_range_
[1]);
252 x_record_range_
[1] = NULL
;
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_
= NULL
;
263 XCloseDisplay(display_
);
268 void LocalInputMonitorLinux::Core::OnFileCanReadWithoutBlocking(int fd
) {
269 DCHECK(input_task_runner_
->BelongsToCurrentThread());
271 // Fetch pending events if any.
272 while (XPending(x_record_display_
)) {
274 XNextEvent(x_record_display_
, &ev
);
278 void LocalInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd
) {
282 void LocalInputMonitorLinux::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_
,
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
) {
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_
));
309 void LocalInputMonitorLinux::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
);
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 scoped_ptr
<LocalInputMonitor
>(
326 new LocalInputMonitorLinux(caller_task_runner
,
328 client_session_control
));
331 } // namespace remoting