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