Introduced IDR_SIGNIN_INTERNALS_INDEX_* strings to grit_whitelist.txt
[chromium-blink-merge.git] / remoting / host / local_input_monitor_linux.cc
blob3d85bf8a2943873718f56befd4c7594316e936d7
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/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>
32 namespace remoting {
34 namespace {
36 class LocalInputMonitorLinux : public base::NonThreadSafe,
37 public LocalInputMonitor {
38 public:
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();
45 private:
46 // The actual implementation resides in LocalInputMonitorLinux::Core class.
47 class Core
48 : public base::RefCountedThreadSafe<Core>,
49 public base::MessagePumpLibevent::Watcher {
50 public:
51 Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
52 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
53 base::WeakPtr<ClientSessionControl> client_session_control);
55 void Start();
56 void Stop();
58 private:
59 friend class base::RefCountedThreadSafe<Core>;
60 virtual ~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.
88 bool alt_pressed_;
90 // True when Ctrl is pressed.
91 bool ctrl_pressed_;
93 Display* display_;
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,
111 input_task_runner,
112 client_session_control)) {
113 core_->Start();
116 LocalInputMonitorLinux::~LocalInputMonitorLinux() {
117 core_->Stop();
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),
127 alt_pressed_(false),
128 ctrl_pressed_(false),
129 display_(NULL),
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() {
154 DCHECK(!display_);
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());
163 DCHECK(!display_);
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";
178 return;
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.";
184 return;
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.";
191 return;
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.";
204 return;
207 if (!XRecordEnableContextAsync(x_record_display_, x_record_context_,
208 &Core::ProcessReply,
209 reinterpret_cast<XPointer>(this))) {
210 LOG(ERROR) << "XRecordEnableContextAsync failed.";
211 return;
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();
217 int result =
218 message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_),
219 true,
220 base::MessageLoopForIO::WATCH_READ,
221 &controller_,
222 this);
223 if (!result) {
224 LOG(ERROR) << "Failed to create X record task.";
225 return;
228 // Fetch pending events if any.
229 while (XPending(x_record_display_)) {
230 XEvent ev;
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_);
242 XFlush(display_);
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;
263 if (display_) {
264 XCloseDisplay(display_);
265 display_ = NULL;
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_)) {
274 XEvent ev;
275 XNextEvent(x_record_display_, &ev);
279 void LocalInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd) {
280 NOTREACHED();
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_,
292 position));
293 } else {
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) {
300 alt_pressed_ = down;
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_));
309 // static
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);
319 } // namespace
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,
328 input_task_runner,
329 client_session_control));
332 } // namespace remoting