OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/event_executor_linux.h" | 5 #include "remoting/host/event_executor.h" |
6 | 6 |
7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
8 #include <X11/XF86keysym.h> | 8 #include <X11/XF86keysym.h> |
9 #include <X11/keysym.h> | 9 #include <X11/keysym.h> |
10 #include <X11/extensions/XTest.h> | 10 #include <X11/extensions/XTest.h> |
11 | 11 |
| 12 #include "base/basictypes.h" |
| 13 #include "base/compiler_specific.h" |
12 #include "base/logging.h" | 14 #include "base/logging.h" |
13 #include "base/message_loop.h" | 15 #include "base/message_loop.h" |
14 #include "base/task.h" | 16 #include "base/task.h" |
15 #include "remoting/proto/internal.pb.h" | 17 #include "remoting/proto/internal.pb.h" |
16 | 18 |
17 namespace remoting { | 19 namespace remoting { |
18 | 20 |
19 using protocol::MouseEvent; | 21 using protocol::MouseEvent; |
20 using protocol::KeyEvent; | 22 using protocol::KeyEvent; |
21 | 23 |
22 static int MouseButtonToX11ButtonNumber( | 24 namespace { |
23 protocol::MouseEvent::MouseButton button) { | 25 |
| 26 // A class to generate events on Linux. |
| 27 class EventExecutorLinux : public EventExecutor { |
| 28 public: |
| 29 EventExecutorLinux(MessageLoopForUI* message_loop, Capturer* capturer); |
| 30 virtual ~EventExecutorLinux() {}; |
| 31 |
| 32 virtual void InjectKeyEvent(const KeyEvent* event, Task* done) OVERRIDE; |
| 33 virtual void InjectMouseEvent(const MouseEvent* event, Task* done) OVERRIDE; |
| 34 |
| 35 private: |
| 36 bool Init(); |
| 37 MessageLoopForUI* message_loop_; |
| 38 Capturer* capturer_; |
| 39 |
| 40 // X11 graphics context. |
| 41 Display* display_; |
| 42 Window root_window_; |
| 43 int width_; |
| 44 int height_; |
| 45 |
| 46 int test_event_base_; |
| 47 int test_error_base_; |
| 48 |
| 49 DISALLOW_COPY_AND_ASSIGN(EventExecutorLinux); |
| 50 }; |
| 51 |
| 52 int MouseButtonToX11ButtonNumber(MouseEvent::MouseButton button) { |
24 switch (button) { | 53 switch (button) { |
25 case MouseEvent::BUTTON_LEFT: | 54 case MouseEvent::BUTTON_LEFT: |
26 return 1; | 55 return 1; |
27 | 56 |
28 case MouseEvent::BUTTON_RIGHT: | 57 case MouseEvent::BUTTON_RIGHT: |
29 return 3; | 58 return 3; |
30 | 59 |
31 case MouseEvent::BUTTON_MIDDLE: | 60 case MouseEvent::BUTTON_MIDDLE: |
32 return 2; | 61 return 2; |
33 | 62 |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 -1, -1, -1, -1, | 218 -1, -1, -1, -1, |
190 // 0xF4 - 0xF7 | 219 // 0xF4 - 0xF7 |
191 -1, -1, /* VKEY_ATTN */ -1, /* VKEY_CRSEL */ -1, | 220 -1, -1, /* VKEY_ATTN */ -1, /* VKEY_CRSEL */ -1, |
192 // 0xF8 - 0xFB | 221 // 0xF8 - 0xFB |
193 /* VKEY_EXSEL */ -1, /* VKEY_EREOF */ -1, /* VKEY_PLAY */ -1, | 222 /* VKEY_EXSEL */ -1, /* VKEY_EREOF */ -1, /* VKEY_PLAY */ -1, |
194 /* VKEY_ZOOM */ -1, | 223 /* VKEY_ZOOM */ -1, |
195 // 0xFC - 0xFF | 224 // 0xFC - 0xFF |
196 /* VKEY_NONAME */ -1, /* VKEY_PA1 */ -1, /* VKEY_OEM_CLEAR */ -1, -1 | 225 /* VKEY_NONAME */ -1, /* VKEY_PA1 */ -1, /* VKEY_OEM_CLEAR */ -1, -1 |
197 }; | 226 }; |
198 | 227 |
199 static int ChromotocolKeycodeToX11Keysym(int32_t keycode) { | 228 int ChromotocolKeycodeToX11Keysym(int32_t keycode) { |
200 if (keycode < 0 || keycode > 255) { | 229 if (keycode < 0 || keycode > 255) { |
201 return -1; | 230 return -1; |
202 } | 231 } |
203 | 232 |
204 return kUsVkeyToKeysym[keycode]; | 233 return kUsVkeyToKeysym[keycode]; |
205 } | 234 } |
206 | 235 |
207 class EventExecutorLinuxPimpl { | 236 EventExecutorLinux::EventExecutorLinux( |
208 public: | 237 MessageLoopForUI* message_loop, Capturer* capturer) |
209 explicit EventExecutorLinuxPimpl(EventExecutorLinux* executor, | 238 : message_loop_(message_loop), |
210 Display* display); | 239 capturer_(capturer), |
211 | 240 display_(message_loop->GetDisplay()), |
212 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? | |
213 | |
214 void HandleMouse(const MouseEvent* message); | |
215 void HandleKey(const KeyEvent* key_event); | |
216 | |
217 private: | |
218 // Reference to containing class so we can access friend functions. | |
219 // Not owned. | |
220 EventExecutorLinux* executor_; | |
221 | |
222 // X11 graphics context. | |
223 Display* display_; | |
224 Window root_window_; | |
225 int width_; | |
226 int height_; | |
227 | |
228 int test_event_base_; | |
229 int test_error_base_; | |
230 }; | |
231 | |
232 EventExecutorLinuxPimpl::EventExecutorLinuxPimpl(EventExecutorLinux* executor, | |
233 Display* display) | |
234 : executor_(executor), | |
235 display_(display), | |
236 root_window_(BadValue), | 241 root_window_(BadValue), |
237 width_(0), | 242 width_(0), |
238 height_(0) { | 243 height_(0) { |
| 244 CHECK(Init()); |
239 } | 245 } |
240 | 246 |
241 bool EventExecutorLinuxPimpl::Init() { | 247 bool EventExecutorLinux::Init() { |
242 CHECK(display_); | 248 CHECK(display_); |
243 | 249 |
244 root_window_ = RootWindow(display_, DefaultScreen(display_)); | 250 root_window_ = RootWindow(display_, DefaultScreen(display_)); |
245 if (root_window_ == BadValue) { | 251 if (root_window_ == BadValue) { |
246 LOG(ERROR) << "Unable to get the root window"; | 252 LOG(ERROR) << "Unable to get the root window"; |
247 return false; | 253 return false; |
248 } | 254 } |
249 | 255 |
250 // TODO(ajwong): Do we want to check the major/minor version at all for XTest? | 256 // TODO(ajwong): Do we want to check the major/minor version at all for XTest? |
251 int major = 0; | 257 int major = 0; |
252 int minor = 0; | 258 int minor = 0; |
253 if (!XTestQueryExtension(display_, &test_event_base_, &test_error_base_, | 259 if (!XTestQueryExtension(display_, &test_event_base_, &test_error_base_, |
254 &major, &minor)) { | 260 &major, &minor)) { |
255 LOG(ERROR) << "Server does not support XTest."; | 261 LOG(ERROR) << "Server does not support XTest."; |
256 return false; | 262 return false; |
257 } | 263 } |
258 | 264 |
259 // Grab the width and height so we can figure out if mouse moves are out of | 265 // Grab the width and height so we can figure out if mouse moves are out of |
260 // range. | 266 // range. |
261 XWindowAttributes root_attr; | 267 XWindowAttributes root_attr; |
262 // TODO(ajwong): Handle resolution changes. | 268 // TODO(ajwong): Handle resolution changes. |
263 if (!XGetWindowAttributes(display_, root_window_, &root_attr)) { | 269 if (!XGetWindowAttributes(display_, root_window_, &root_attr)) { |
264 LOG(ERROR) << "Unable to get window attributes"; | 270 LOG(ERROR) << "Unable to get window attributes"; |
265 return false; | 271 return false; |
266 } | 272 } |
267 | 273 |
268 width_ = root_attr.width; | 274 width_ = root_attr.width; |
269 height_ = root_attr.height; | 275 height_ = root_attr.height; |
270 | |
271 return true; | 276 return true; |
272 } | 277 } |
273 | 278 |
274 void EventExecutorLinuxPimpl::HandleKey(const KeyEvent* key_event) { | 279 void EventExecutorLinux::InjectKeyEvent(const KeyEvent* event, Task* done) { |
| 280 if (MessageLoop::current() != message_loop_) { |
| 281 message_loop_->PostTask( |
| 282 FROM_HERE, |
| 283 NewRunnableMethod(this, &EventExecutorLinux::InjectKeyEvent, |
| 284 event, done)); |
| 285 return; |
| 286 } |
275 // TODO(ajwong): This will only work for QWERTY keyboards. | 287 // TODO(ajwong): This will only work for QWERTY keyboards. |
276 int keysym = ChromotocolKeycodeToX11Keysym(key_event->keycode()); | 288 int keysym = ChromotocolKeycodeToX11Keysym(event->keycode()); |
277 | 289 |
278 if (keysym == -1) { | 290 if (keysym == -1) { |
279 LOG(WARNING) << "Ignoring unknown key: " << key_event->keycode(); | 291 LOG(WARNING) << "Ignoring unknown key: " << event->keycode(); |
280 return; | 292 return; |
281 } | 293 } |
282 | 294 |
283 // Translate the keysym into a keycode understandable by the X display. | 295 // Translate the keysym into a keycode understandable by the X display. |
284 int keycode = XKeysymToKeycode(display_, keysym); | 296 int keycode = XKeysymToKeycode(display_, keysym); |
285 if (keycode == 0) { | 297 if (keycode == 0) { |
286 LOG(WARNING) << "Ignoring undefined keysym: " << keysym | 298 LOG(WARNING) << "Ignoring undefined keysym: " << keysym |
287 << " for key: " << key_event->keycode(); | 299 << " for key: " << event->keycode(); |
288 return; | 300 return; |
289 } | 301 } |
290 | 302 |
291 VLOG(3) << "Got pepper key: " << key_event->keycode() | 303 VLOG(3) << "Got pepper key: " << event->keycode() |
292 << " sending keysym: " << keysym | 304 << " sending keysym: " << keysym |
293 << " to keycode: " << keycode; | 305 << " to keycode: " << keycode; |
294 XTestFakeKeyEvent(display_, keycode, key_event->pressed(), CurrentTime); | 306 XTestFakeKeyEvent(display_, keycode, event->pressed(), CurrentTime); |
| 307 |
| 308 done->Run(); |
| 309 delete done; |
295 } | 310 } |
296 | 311 |
297 void EventExecutorLinuxPimpl::HandleMouse(const MouseEvent* event) { | 312 void EventExecutorLinux::InjectMouseEvent(const MouseEvent* event, |
| 313 Task* done) { |
| 314 if (MessageLoop::current() != message_loop_) { |
| 315 message_loop_->PostTask( |
| 316 FROM_HERE, |
| 317 NewRunnableMethod(this, &EventExecutorLinux::InjectMouseEvent, |
| 318 event, done)); |
| 319 return; |
| 320 } |
298 if (event->has_x() && event->has_y()) { | 321 if (event->has_x() && event->has_y()) { |
299 if (event->x() < 0 || event->y() < 0 || | 322 if (event->x() < 0 || event->y() < 0 || |
300 event->x() > width_ || event->y() > height_) { | 323 event->x() > width_ || event->y() > height_) { |
301 // A misbehaving client may send these. Drop events that are out of range. | 324 // A misbehaving client may send these. Drop events that are out of range. |
302 // TODO(ajwong): How can we log this sanely? We don't want to DOS the | 325 // TODO(ajwong): How can we log this sanely? We don't want to DOS the |
303 // server with a misbehaving client by logging like crazy. | 326 // server with a misbehaving client by logging like crazy. |
304 return; | 327 return; |
305 } | 328 } |
306 | 329 |
307 VLOG(3) << "Moving mouse to " << event->x() | 330 VLOG(3) << "Moving mouse to " << event->x() |
(...skipping 14 matching lines...) Expand all Loading... |
322 | 345 |
323 VLOG(3) << "Button " << event->button() | 346 VLOG(3) << "Button " << event->button() |
324 << " received, sending down " << button_number; | 347 << " received, sending down " << button_number; |
325 XTestFakeButtonEvent(display_, button_number, event->button_down(), | 348 XTestFakeButtonEvent(display_, button_number, event->button_down(), |
326 CurrentTime); | 349 CurrentTime); |
327 } | 350 } |
328 | 351 |
329 if (event->has_wheel_offset_x() && event->has_wheel_offset_y()) { | 352 if (event->has_wheel_offset_x() && event->has_wheel_offset_y()) { |
330 NOTIMPLEMENTED() << "No scroll wheel support yet."; | 353 NOTIMPLEMENTED() << "No scroll wheel support yet."; |
331 } | 354 } |
332 } | |
333 | 355 |
334 EventExecutorLinux::EventExecutorLinux( | |
335 MessageLoopForUI* message_loop, Capturer* capturer) | |
336 : message_loop_(message_loop), | |
337 capturer_(capturer), | |
338 pimpl_(new EventExecutorLinuxPimpl(this, message_loop->GetDisplay())) { | |
339 CHECK(pimpl_->Init()); | |
340 } | |
341 | |
342 EventExecutorLinux::~EventExecutorLinux() { | |
343 } | |
344 | |
345 void EventExecutorLinux::InjectKeyEvent(const KeyEvent* event, Task* done) { | |
346 if (MessageLoop::current() != message_loop_) { | |
347 message_loop_->PostTask( | |
348 FROM_HERE, | |
349 NewRunnableMethod(this, &EventExecutorLinux::InjectKeyEvent, | |
350 event, done)); | |
351 return; | |
352 } | |
353 pimpl_->HandleKey(event); | |
354 done->Run(); | 356 done->Run(); |
355 delete done; | 357 delete done; |
356 } | 358 } |
357 | 359 |
358 void EventExecutorLinux::InjectMouseEvent(const MouseEvent* event, | 360 } // namespace |
359 Task* done) { | |
360 if (MessageLoop::current() != message_loop_) { | |
361 message_loop_->PostTask( | |
362 FROM_HERE, | |
363 NewRunnableMethod(this, &EventExecutorLinux::InjectMouseEvent, | |
364 event, done)); | |
365 return; | |
366 } | |
367 pimpl_->HandleMouse(event); | |
368 done->Run(); | |
369 delete done; | |
370 } | |
371 | 361 |
372 protocol::InputStub* CreateEventExecutor(MessageLoopForUI* message_loop, | 362 EventExecutor* EventExecutor::Create(MessageLoopForUI* message_loop, |
373 Capturer* capturer) { | 363 Capturer* capturer) { |
374 return new EventExecutorLinux(message_loop, capturer); | 364 return new EventExecutorLinux(message_loop, capturer); |
375 } | 365 } |
376 | 366 |
377 } // namespace remoting | 367 } // namespace remoting |
| 368 |
| 369 DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::EventExecutorLinux); |
| 370 |
OLD | NEW |