OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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.h" | 5 #include "remoting/host/event_executor.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 | 8 |
9 #include <X11/Xlib.h> | 9 #include <X11/Xlib.h> |
10 #include <X11/XF86keysym.h> | 10 #include <X11/XF86keysym.h> |
(...skipping 16 matching lines...) Expand all Loading... |
27 using protocol::MouseEvent; | 27 using protocol::MouseEvent; |
28 | 28 |
29 // USB to XKB keycode map table. | 29 // USB to XKB keycode map table. |
30 #define USB_KEYMAP(usb, xkb, win, mac) {usb, xkb} | 30 #define USB_KEYMAP(usb, xkb, win, mac) {usb, xkb} |
31 #include "remoting/host/usb_keycode_map.h" | 31 #include "remoting/host/usb_keycode_map.h" |
32 #undef USB_KEYMAP | 32 #undef USB_KEYMAP |
33 | 33 |
34 // A class to generate events on Linux. | 34 // A class to generate events on Linux. |
35 class EventExecutorLinux : public EventExecutor { | 35 class EventExecutorLinux : public EventExecutor { |
36 public: | 36 public: |
37 EventExecutorLinux(MessageLoop* message_loop, Capturer* capturer); | 37 EventExecutorLinux(MessageLoop* message_loop); |
38 virtual ~EventExecutorLinux(); | 38 virtual ~EventExecutorLinux(); |
39 | 39 |
40 bool Init(); | 40 bool Init(); |
41 | 41 |
42 // Clipboard stub interface. | 42 // Clipboard stub interface. |
43 virtual void InjectClipboardEvent(const ClipboardEvent& event) | 43 virtual void InjectClipboardEvent(const ClipboardEvent& event) |
44 OVERRIDE; | 44 OVERRIDE; |
45 | 45 |
46 // InputStub interface. | 46 // InputStub interface. |
47 virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE; | 47 virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE; |
48 virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE; | 48 virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE; |
49 | 49 |
50 private: | 50 private: |
51 // |mode| is one of the AutoRepeatModeOn, AutoRepeatModeOff, | 51 // |mode| is one of the AutoRepeatModeOn, AutoRepeatModeOff, |
52 // AutoRepeatModeDefault constants defined by the XChangeKeyboardControl() | 52 // AutoRepeatModeDefault constants defined by the XChangeKeyboardControl() |
53 // API. | 53 // API. |
54 void SetAutoRepeatForKey(int keycode, int mode); | 54 void SetAutoRepeatForKey(int keycode, int mode); |
55 void InjectScrollWheelClicks(int button, int count); | 55 void InjectScrollWheelClicks(int button, int count); |
56 | 56 |
57 MessageLoop* message_loop_; | 57 MessageLoop* message_loop_; |
58 | 58 |
59 std::set<int> pressed_keys_; | 59 std::set<int> pressed_keys_; |
60 | 60 |
61 // X11 graphics context. | 61 // X11 graphics context. |
62 Display* display_; | 62 Display* display_; |
63 Window root_window_; | 63 Window root_window_; |
64 int width_; | |
65 int height_; | |
66 | 64 |
67 int test_event_base_; | 65 int test_event_base_; |
68 int test_error_base_; | 66 int test_error_base_; |
69 | 67 |
70 DISALLOW_COPY_AND_ASSIGN(EventExecutorLinux); | 68 DISALLOW_COPY_AND_ASSIGN(EventExecutorLinux); |
71 }; | 69 }; |
72 | 70 |
73 int MouseButtonToX11ButtonNumber(MouseEvent::MouseButton button) { | 71 int MouseButtonToX11ButtonNumber(MouseEvent::MouseButton button) { |
74 switch (button) { | 72 switch (button) { |
75 case MouseEvent::BUTTON_LEFT: | 73 case MouseEvent::BUTTON_LEFT: |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 /* VKEY_NONAME */ -1, /* VKEY_PA1 */ -1, /* VKEY_OEM_CLEAR */ -1, -1 | 255 /* VKEY_NONAME */ -1, /* VKEY_PA1 */ -1, /* VKEY_OEM_CLEAR */ -1, -1 |
258 }; | 256 }; |
259 | 257 |
260 int ChromotocolKeycodeToX11Keysym(int32_t keycode) { | 258 int ChromotocolKeycodeToX11Keysym(int32_t keycode) { |
261 if (keycode < 0 || keycode > 255) | 259 if (keycode < 0 || keycode > 255) |
262 return kInvalidKeycode; | 260 return kInvalidKeycode; |
263 | 261 |
264 return kUsVkeyToKeysym[keycode]; | 262 return kUsVkeyToKeysym[keycode]; |
265 } | 263 } |
266 | 264 |
267 EventExecutorLinux::EventExecutorLinux(MessageLoop* message_loop, | 265 EventExecutorLinux::EventExecutorLinux(MessageLoop* message_loop) |
268 Capturer* capturer) | |
269 : message_loop_(message_loop), | 266 : message_loop_(message_loop), |
270 display_(XOpenDisplay(NULL)), | 267 display_(XOpenDisplay(NULL)), |
271 root_window_(BadValue), | 268 root_window_(BadValue) { |
272 width_(0), | |
273 height_(0) { | |
274 } | 269 } |
275 | 270 |
276 EventExecutorLinux::~EventExecutorLinux() { | 271 EventExecutorLinux::~EventExecutorLinux() { |
277 CHECK(pressed_keys_.empty()); | 272 CHECK(pressed_keys_.empty()); |
278 } | 273 } |
279 | 274 |
280 bool EventExecutorLinux::Init() { | 275 bool EventExecutorLinux::Init() { |
281 CHECK(display_); | 276 CHECK(display_); |
282 | 277 |
283 root_window_ = RootWindow(display_, DefaultScreen(display_)); | 278 root_window_ = RootWindow(display_, DefaultScreen(display_)); |
284 if (root_window_ == BadValue) { | 279 if (root_window_ == BadValue) { |
285 LOG(ERROR) << "Unable to get the root window"; | 280 LOG(ERROR) << "Unable to get the root window"; |
286 return false; | 281 return false; |
287 } | 282 } |
288 | 283 |
289 // TODO(ajwong): Do we want to check the major/minor version at all for XTest? | 284 // TODO(ajwong): Do we want to check the major/minor version at all for XTest? |
290 int major = 0; | 285 int major = 0; |
291 int minor = 0; | 286 int minor = 0; |
292 if (!XTestQueryExtension(display_, &test_event_base_, &test_error_base_, | 287 if (!XTestQueryExtension(display_, &test_event_base_, &test_error_base_, |
293 &major, &minor)) { | 288 &major, &minor)) { |
294 LOG(ERROR) << "Server does not support XTest."; | 289 LOG(ERROR) << "Server does not support XTest."; |
295 return false; | 290 return false; |
296 } | 291 } |
297 | 292 |
298 // Grab the width and height so we can figure out if mouse moves are out of | |
299 // range. | |
300 XWindowAttributes root_attr; | |
301 // TODO(ajwong): Handle resolution changes. | |
302 if (!XGetWindowAttributes(display_, root_window_, &root_attr)) { | |
303 LOG(ERROR) << "Unable to get window attributes"; | |
304 return false; | |
305 } | |
306 | |
307 width_ = root_attr.width; | |
308 height_ = root_attr.height; | |
309 return true; | 293 return true; |
310 } | 294 } |
311 | 295 |
312 void EventExecutorLinux::InjectClipboardEvent(const ClipboardEvent& event) { | 296 void EventExecutorLinux::InjectClipboardEvent(const ClipboardEvent& event) { |
313 // TODO(simonmorris): Implement clipboard injection. | 297 // TODO(simonmorris): Implement clipboard injection. |
314 } | 298 } |
315 | 299 |
316 void EventExecutorLinux::InjectKeyEvent(const KeyEvent& event) { | 300 void EventExecutorLinux::InjectKeyEvent(const KeyEvent& event) { |
317 // HostEventDispatcher should filter events missing the pressed field. | 301 // HostEventDispatcher should filter events missing the pressed field. |
318 DCHECK(event.has_pressed()); | 302 DCHECK(event.has_pressed()); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 control.auto_repeat_mode = mode; | 360 control.auto_repeat_mode = mode; |
377 XChangeKeyboardControl(display_, KBKey | KBAutoRepeatMode, &control); | 361 XChangeKeyboardControl(display_, KBKey | KBAutoRepeatMode, &control); |
378 } | 362 } |
379 | 363 |
380 void EventExecutorLinux::InjectScrollWheelClicks(int button, int count) { | 364 void EventExecutorLinux::InjectScrollWheelClicks(int button, int count) { |
381 for (int i = 0; i < count; i++) { | 365 for (int i = 0; i < count; i++) { |
382 // Generate a button-down and a button-up to simulate a wheel click. | 366 // Generate a button-down and a button-up to simulate a wheel click. |
383 XTestFakeButtonEvent(display_, button, true, CurrentTime); | 367 XTestFakeButtonEvent(display_, button, true, CurrentTime); |
384 XTestFakeButtonEvent(display_, button, false, CurrentTime); | 368 XTestFakeButtonEvent(display_, button, false, CurrentTime); |
385 } | 369 } |
386 XFlush(display_); | |
387 } | 370 } |
388 | 371 |
389 void EventExecutorLinux::InjectMouseEvent(const MouseEvent& event) { | 372 void EventExecutorLinux::InjectMouseEvent(const MouseEvent& event) { |
390 if (MessageLoop::current() != message_loop_) { | 373 if (MessageLoop::current() != message_loop_) { |
391 message_loop_->PostTask( | 374 message_loop_->PostTask( |
392 FROM_HERE, | 375 FROM_HERE, |
393 base::Bind(&EventExecutorLinux::InjectMouseEvent, | 376 base::Bind(&EventExecutorLinux::InjectMouseEvent, |
394 base::Unretained(this), event)); | 377 base::Unretained(this), event)); |
395 return; | 378 return; |
396 } | 379 } |
397 | 380 |
398 if (event.has_x() && event.has_y()) { | 381 if (event.has_x() && event.has_y()) { |
399 if (event.x() < 0 || event.y() < 0 || | |
400 event.x() > width_ || event.y() > height_) { | |
401 // A misbehaving client may send these. Drop events that are out of range. | |
402 // TODO(ajwong): How can we log this sanely? We don't want to DOS the | |
403 // server with a misbehaving client by logging like crazy. | |
404 return; | |
405 } | |
406 | |
407 VLOG(3) << "Moving mouse to " << event.x() | 382 VLOG(3) << "Moving mouse to " << event.x() |
408 << "," << event.y(); | 383 << "," << event.y(); |
409 XTestFakeMotionEvent(display_, DefaultScreen(display_), | 384 XTestFakeMotionEvent(display_, DefaultScreen(display_), |
410 event.x(), event.y(), | 385 event.x(), event.y(), |
411 CurrentTime); | 386 CurrentTime); |
412 XFlush(display_); | |
413 } | 387 } |
414 | 388 |
415 if (event.has_button() && event.has_button_down()) { | 389 if (event.has_button() && event.has_button_down()) { |
416 int button_number = MouseButtonToX11ButtonNumber(event.button()); | 390 int button_number = MouseButtonToX11ButtonNumber(event.button()); |
417 | 391 |
418 if (button_number < 0) { | 392 if (button_number < 0) { |
419 LOG(WARNING) << "Ignoring unknown button type: " << event.button(); | 393 LOG(WARNING) << "Ignoring unknown button type: " << event.button(); |
420 return; | 394 return; |
421 } | 395 } |
422 | 396 |
423 VLOG(3) << "Button " << event.button() | 397 VLOG(3) << "Button " << event.button() |
424 << " received, sending " | 398 << " received, sending " |
425 << (event.button_down() ? "down " : "up ") | 399 << (event.button_down() ? "down " : "up ") |
426 << button_number; | 400 << button_number; |
427 XTestFakeButtonEvent(display_, button_number, event.button_down(), | 401 XTestFakeButtonEvent(display_, button_number, event.button_down(), |
428 CurrentTime); | 402 CurrentTime); |
429 XFlush(display_); | |
430 } | 403 } |
431 | 404 |
432 if (event.has_wheel_offset_y() && event.wheel_offset_y() != 0) { | 405 if (event.has_wheel_offset_y() && event.wheel_offset_y() != 0) { |
433 int dy = event.wheel_offset_y(); | 406 int dy = event.wheel_offset_y(); |
434 InjectScrollWheelClicks(VerticalScrollWheelToX11ButtonNumber(dy), abs(dy)); | 407 InjectScrollWheelClicks(VerticalScrollWheelToX11ButtonNumber(dy), abs(dy)); |
435 } | 408 } |
436 if (event.has_wheel_offset_x() && event.wheel_offset_x() != 0) { | 409 if (event.has_wheel_offset_x() && event.wheel_offset_x() != 0) { |
437 int dx = event.wheel_offset_x(); | 410 int dx = event.wheel_offset_x(); |
438 InjectScrollWheelClicks(HorizontalScrollWheelToX11ButtonNumber(dx), | 411 InjectScrollWheelClicks(HorizontalScrollWheelToX11ButtonNumber(dx), |
439 abs(dx)); | 412 abs(dx)); |
440 } | 413 } |
| 414 |
| 415 XFlush(display_); |
441 } | 416 } |
442 | 417 |
443 } // namespace | 418 } // namespace |
444 | 419 |
445 scoped_ptr<protocol::HostEventStub> EventExecutor::Create( | 420 scoped_ptr<protocol::HostEventStub> EventExecutor::Create( |
446 MessageLoop* message_loop, Capturer* capturer) { | 421 MessageLoop* message_loop, Capturer* capturer) { |
447 scoped_ptr<EventExecutorLinux> executor( | 422 scoped_ptr<EventExecutorLinux> executor( |
448 new EventExecutorLinux(message_loop, capturer)); | 423 new EventExecutorLinux(message_loop)); |
449 if (!executor->Init()) { | 424 if (!executor->Init()) |
450 executor.reset(NULL); | 425 return scoped_ptr<protocol::HostEventStub>(NULL); |
451 } | |
452 return executor.PassAs<protocol::HostEventStub>(); | 426 return executor.PassAs<protocol::HostEventStub>(); |
453 } | 427 } |
454 | 428 |
455 } // namespace remoting | 429 } // namespace remoting |
OLD | NEW |