Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / remoting / host / input_injector_mac.cc
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.
4
5 #include "remoting/host/input_injector.h"
6
7 #include <ApplicationServices/ApplicationServices.h>
8 #include <Carbon/Carbon.h>
9 #include <algorithm>
10
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/location.h"
15 #include "base/mac/scoped_cftyperef.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/single_thread_task_runner.h"
18 #include "remoting/host/clipboard.h"
19 #include "remoting/proto/internal.pb.h"
20 #include "remoting/protocol/message_decoder.h"
21 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
22 #include "third_party/webrtc/modules/desktop_capture/mac/desktop_configuration.h"
23 #include "ui/events/keycodes/dom4/keycode_converter.h"
24
25 namespace remoting {
26
27 namespace {
28
29 void SetOrClearBit(uint64_t &value, uint64_t bit, bool set_bit) {
30   value = set_bit ? (value | bit) : (value & ~bit);
31 }
32
33 // This value is not defined. Give it the obvious name so that if it is ever
34 // added there will be a handy compilation error to remind us to remove this
35 // definition.
36 const int kVK_RightCommand = 0x36;
37
38 using protocol::ClipboardEvent;
39 using protocol::KeyEvent;
40 using protocol::MouseEvent;
41
42 // A class to generate events on Mac.
43 class InputInjectorMac : public InputInjector {
44  public:
45   explicit InputInjectorMac(
46       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
47   virtual ~InputInjectorMac();
48
49   // ClipboardStub interface.
50   virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
51
52   // InputStub interface.
53   virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
54   virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
55
56   // InputInjector interface.
57   virtual void Start(
58       scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
59
60  private:
61   // The actual implementation resides in InputInjectorMac::Core class.
62   class Core : public base::RefCountedThreadSafe<Core> {
63    public:
64     explicit Core(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
65
66     // Mirrors the ClipboardStub interface.
67     void InjectClipboardEvent(const ClipboardEvent& event);
68
69     // Mirrors the InputStub interface.
70     void InjectKeyEvent(const KeyEvent& event);
71     void InjectMouseEvent(const MouseEvent& event);
72
73     // Mirrors the InputInjector interface.
74     void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
75
76     void Stop();
77
78    private:
79     friend class base::RefCountedThreadSafe<Core>;
80     virtual ~Core();
81
82     scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
83     webrtc::DesktopVector mouse_pos_;
84     uint32 mouse_button_state_;
85     scoped_ptr<Clipboard> clipboard_;
86     CGEventFlags left_modifiers_;
87     CGEventFlags right_modifiers_;
88
89     DISALLOW_COPY_AND_ASSIGN(Core);
90   };
91
92   scoped_refptr<Core> core_;
93
94   DISALLOW_COPY_AND_ASSIGN(InputInjectorMac);
95 };
96
97 InputInjectorMac::InputInjectorMac(
98     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
99   core_ = new Core(task_runner);
100 }
101
102 InputInjectorMac::~InputInjectorMac() {
103   core_->Stop();
104 }
105
106 void InputInjectorMac::InjectClipboardEvent(const ClipboardEvent& event) {
107   core_->InjectClipboardEvent(event);
108 }
109
110 void InputInjectorMac::InjectKeyEvent(const KeyEvent& event) {
111   core_->InjectKeyEvent(event);
112 }
113
114 void InputInjectorMac::InjectMouseEvent(const MouseEvent& event) {
115   core_->InjectMouseEvent(event);
116 }
117
118 void InputInjectorMac::Start(
119     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
120   core_->Start(client_clipboard.Pass());
121 }
122
123 InputInjectorMac::Core::Core(
124     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
125     : task_runner_(task_runner),
126       mouse_button_state_(0),
127       clipboard_(Clipboard::Create()),
128       left_modifiers_(0),
129       right_modifiers_(0) {
130   // Ensure that local hardware events are not suppressed after injecting
131   // input events.  This allows LocalInputMonitor to detect if the local mouse
132   // is being moved whilst a remote user is connected.
133   // This API is deprecated, but it is needed when using the deprecated
134   // injection APIs.
135   // If the non-deprecated injection APIs were used instead, the equivalent of
136   // this line would not be needed, as OS X defaults to _not_ suppressing local
137   // inputs in that case.
138 #pragma clang diagnostic push
139 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
140   CGSetLocalEventsSuppressionInterval(0.0);
141 #pragma clang diagnostic pop
142 }
143
144 void InputInjectorMac::Core::InjectClipboardEvent(const ClipboardEvent& event) {
145   if (!task_runner_->BelongsToCurrentThread()) {
146     task_runner_->PostTask(
147         FROM_HERE, base::Bind(&Core::InjectClipboardEvent, this, event));
148     return;
149   }
150
151   // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
152   clipboard_->InjectClipboardEvent(event);
153 }
154
155 void InputInjectorMac::Core::InjectKeyEvent(const KeyEvent& event) {
156   // HostEventDispatcher should filter events missing the pressed field.
157   if (!event.has_pressed() || !event.has_usb_keycode())
158     return;
159
160   ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
161   int keycode = key_converter->UsbKeycodeToNativeKeycode(event.usb_keycode());
162
163   VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
164           << " to keycode: " << keycode << std::dec;
165
166   // If we couldn't determine the Mac virtual key code then ignore the event.
167   if (keycode == key_converter->InvalidNativeKeycode())
168     return;
169
170   // If this is a modifier key, remember its new state so that it can be
171   // correctly applied to subsequent events.
172   if (keycode == kVK_Command) {
173     SetOrClearBit(left_modifiers_, kCGEventFlagMaskCommand, event.pressed());
174   } else if (keycode == kVK_Shift) {
175     SetOrClearBit(left_modifiers_, kCGEventFlagMaskShift, event.pressed());
176   } else if (keycode == kVK_Control) {
177     SetOrClearBit(left_modifiers_, kCGEventFlagMaskControl, event.pressed());
178   } else if (keycode == kVK_Option) {
179     SetOrClearBit(left_modifiers_, kCGEventFlagMaskAlternate, event.pressed());
180   } else if (keycode == kVK_RightCommand) {
181     SetOrClearBit(right_modifiers_, kCGEventFlagMaskCommand, event.pressed());
182   } else if (keycode == kVK_RightShift) {
183     SetOrClearBit(right_modifiers_, kCGEventFlagMaskShift, event.pressed());
184   } else if (keycode == kVK_RightControl) {
185     SetOrClearBit(right_modifiers_, kCGEventFlagMaskControl, event.pressed());
186   } else if (keycode == kVK_RightOption) {
187     SetOrClearBit(right_modifiers_, kCGEventFlagMaskAlternate, event.pressed());
188   }
189
190   base::ScopedCFTypeRef<CGEventRef> eventRef(
191       CGEventCreateKeyboardEvent(NULL, keycode, event.pressed()));
192
193   if (eventRef) {
194     // In addition to the modifier keys pressed right now, we also need to set
195     // AlphaShift if caps lock was active at the client (Mac ignores NumLock).
196     uint64_t flags = left_modifiers_ | right_modifiers_;
197     if (event.lock_states() & protocol::KeyEvent::LOCK_STATES_CAPSLOCK)
198       flags |= kCGEventFlagMaskAlphaShift;
199     CGEventSetFlags(eventRef, flags);
200
201     // Post the event to the current session.
202     CGEventPost(kCGSessionEventTap, eventRef);
203   }
204 }
205
206 void InputInjectorMac::Core::InjectMouseEvent(const MouseEvent& event) {
207   if (event.has_x() && event.has_y()) {
208     // On multi-monitor systems (0,0) refers to the top-left of the "main"
209     // display, whereas our coordinate scheme places (0,0) at the top-left of
210     // the bounding rectangle around all the displays, so we need to translate
211     // accordingly.
212
213     // Set the mouse position assuming single-monitor.
214     mouse_pos_.set(event.x(), event.y());
215
216     // Fetch the desktop configuration.
217     // TODO(wez): Optimize this out, or at least only enumerate displays in
218     // response to display-changed events. VideoFrameCapturer's VideoFrames
219     // could be augmented to include native cursor coordinates for use by
220     // MouseClampingFilter, removing the need for translation here.
221     webrtc::MacDesktopConfiguration desktop_config =
222         webrtc::MacDesktopConfiguration::GetCurrent(
223             webrtc::MacDesktopConfiguration::TopLeftOrigin);
224
225     // Translate the mouse position into desktop coordinates.
226     mouse_pos_.add(webrtc::DesktopVector(desktop_config.pixel_bounds.left(),
227                                          desktop_config.pixel_bounds.top()));
228
229     // Constrain the mouse position to the desktop coordinates.
230     mouse_pos_.set(
231        std::max(desktop_config.pixel_bounds.left(),
232            std::min(desktop_config.pixel_bounds.right(), mouse_pos_.x())),
233        std::max(desktop_config.pixel_bounds.top(),
234            std::min(desktop_config.pixel_bounds.bottom(), mouse_pos_.y())));
235
236     // Convert from pixel to Density Independent Pixel coordinates.
237     mouse_pos_.set(mouse_pos_.x() / desktop_config.dip_to_pixel_scale,
238                    mouse_pos_.y() / desktop_config.dip_to_pixel_scale);
239
240     VLOG(3) << "Moving mouse to " << mouse_pos_.x() << "," << mouse_pos_.y();
241   }
242   if (event.has_button() && event.has_button_down()) {
243     if (event.button() >= 1 && event.button() <= 3) {
244       VLOG(2) << "Button " << event.button()
245               << (event.button_down() ? " down" : " up");
246       int button_change = 1 << (event.button() - 1);
247       if (event.button_down())
248         mouse_button_state_ |= button_change;
249       else
250         mouse_button_state_ &= ~button_change;
251     } else {
252       VLOG(1) << "Unknown mouse button: " << event.button();
253     }
254   }
255   // We use the deprecated CGPostMouseEvent API because we receive low-level
256   // mouse events, whereas CGEventCreateMouseEvent is for injecting higher-level
257   // events. For example, the deprecated APIs will detect double-clicks or drags
258   // in a way that is consistent with how they would be generated using a local
259   // mouse, whereas the new APIs expect us to inject these higher-level events
260   // directly.
261   CGPoint position = CGPointMake(mouse_pos_.x(), mouse_pos_.y());
262   enum {
263     LeftBit = 1 << (MouseEvent::BUTTON_LEFT - 1),
264     MiddleBit = 1 << (MouseEvent::BUTTON_MIDDLE - 1),
265     RightBit = 1 << (MouseEvent::BUTTON_RIGHT - 1)
266   };
267 #pragma clang diagnostic push
268 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
269   CGError error = CGPostMouseEvent(position, true, 3,
270                                    (mouse_button_state_ & LeftBit) != 0,
271                                    (mouse_button_state_ & RightBit) != 0,
272                                    (mouse_button_state_ & MiddleBit) != 0);
273 #pragma clang diagnostic pop
274   if (error != kCGErrorSuccess)
275     LOG(WARNING) << "CGPostMouseEvent error " << error;
276
277   if (event.has_wheel_delta_x() && event.has_wheel_delta_y()) {
278     int delta_x = static_cast<int>(event.wheel_delta_x());
279     int delta_y = static_cast<int>(event.wheel_delta_y());
280     base::ScopedCFTypeRef<CGEventRef> event(CGEventCreateScrollWheelEvent(
281         NULL, kCGScrollEventUnitPixel, 2, delta_y, delta_x));
282     if (event)
283       CGEventPost(kCGSessionEventTap, event);
284   }
285 }
286
287 void InputInjectorMac::Core::Start(
288     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
289   if (!task_runner_->BelongsToCurrentThread()) {
290     task_runner_->PostTask(
291         FROM_HERE,
292         base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
293     return;
294   }
295
296   clipboard_->Start(client_clipboard.Pass());
297 }
298
299 void InputInjectorMac::Core::Stop() {
300   if (!task_runner_->BelongsToCurrentThread()) {
301     task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Stop, this));
302     return;
303   }
304
305   clipboard_->Stop();
306 }
307
308 InputInjectorMac::Core::~Core() {
309 }
310
311 }  // namespace
312
313 scoped_ptr<InputInjector> InputInjector::Create(
314     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
315     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
316   return scoped_ptr<InputInjector>(new InputInjectorMac(main_task_runner));
317 }
318
319 }  // namespace remoting