Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / remoting / host / local_input_monitor_mac.mm
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/local_input_monitor.h"
6
7 #import <AppKit/AppKit.h>
8 #include <set>
9
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/mac/scoped_cftyperef.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/synchronization/lock.h"
18 #include "base/threading/non_thread_safe.h"
19 #include "remoting/host/client_session_control.h"
20 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMCarbonEvent.h"
21 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
22
23 // Esc Key Code is 53.
24 // http://boredzo.org/blog/wp-content/uploads/2007/05/IMTx-virtual-keycodes.pdf
25 static const NSUInteger kEscKeyCode = 53;
26
27 namespace remoting {
28 namespace {
29
30 class LocalInputMonitorMac : public base::NonThreadSafe,
31                              public LocalInputMonitor {
32  public:
33   // Invoked by LocalInputMonitorManager.
34   class EventHandler {
35    public:
36     virtual ~EventHandler() {}
37
38     virtual void OnLocalMouseMoved(const webrtc::DesktopVector& position) = 0;
39     virtual void OnDisconnectShortcut() = 0;
40   };
41
42   LocalInputMonitorMac(
43       scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
44       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
45       base::WeakPtr<ClientSessionControl> client_session_control);
46   virtual ~LocalInputMonitorMac();
47
48  private:
49   // The actual implementation resides in LocalInputMonitorMac::Core class.
50   class Core;
51   scoped_refptr<Core> core_;
52
53   DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorMac);
54 };
55
56 }  // namespace
57 }  // namespace remoting
58
59 @interface LocalInputMonitorManager : NSObject {
60  @private
61   GTMCarbonHotKey* hotKey_;
62   CFRunLoopSourceRef mouseRunLoopSource_;
63   base::ScopedCFTypeRef<CFMachPortRef> mouseMachPort_;
64   remoting::LocalInputMonitorMac::EventHandler* monitor_;
65 }
66
67 - (id)initWithMonitor:(remoting::LocalInputMonitorMac::EventHandler*)monitor;
68
69 // Called when the hotKey is hit.
70 - (void)hotKeyHit:(GTMCarbonHotKey*)hotKey;
71
72 // Called when the local mouse moves
73 - (void)localMouseMoved:(const webrtc::DesktopVector&)mousePos;
74
75 // Must be called when the LocalInputMonitorManager is no longer to be used.
76 // Similar to NSTimer in that more than a simple release is required.
77 - (void)invalidate;
78
79 @end
80
81 static CGEventRef LocalMouseMoved(CGEventTapProxy proxy, CGEventType type,
82                                   CGEventRef event, void* context) {
83   int64_t pid = CGEventGetIntegerValueField(event, kCGEventSourceUnixProcessID);
84   if (pid == 0) {
85     CGPoint cgMousePos = CGEventGetLocation(event);
86     webrtc::DesktopVector mousePos(cgMousePos.x, cgMousePos.y);
87     [static_cast<LocalInputMonitorManager*>(context) localMouseMoved:mousePos];
88   }
89   return NULL;
90 }
91
92 @implementation LocalInputMonitorManager
93
94 - (id)initWithMonitor:(remoting::LocalInputMonitorMac::EventHandler*)monitor {
95   if ((self = [super init])) {
96     monitor_ = monitor;
97
98     GTMCarbonEventDispatcherHandler* handler =
99         [GTMCarbonEventDispatcherHandler sharedEventDispatcherHandler];
100     hotKey_ = [handler registerHotKey:kEscKeyCode
101                              modifiers:(NSAlternateKeyMask | NSControlKeyMask)
102                                 target:self
103                                 action:@selector(hotKeyHit:)
104                               userInfo:nil
105                            whenPressed:YES];
106     if (!hotKey_) {
107       LOG(ERROR) << "registerHotKey failed.";
108     }
109     mouseMachPort_.reset(CGEventTapCreate(
110         kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly,
111         1 << kCGEventMouseMoved, LocalMouseMoved, self));
112     if (mouseMachPort_) {
113       mouseRunLoopSource_ = CFMachPortCreateRunLoopSource(
114           NULL, mouseMachPort_, 0);
115       CFRunLoopAddSource(
116           CFRunLoopGetMain(), mouseRunLoopSource_, kCFRunLoopCommonModes);
117     } else {
118       LOG(ERROR) << "CGEventTapCreate failed.";
119     }
120     if (!hotKey_ && !mouseMachPort_) {
121       [self release];
122       return nil;
123     }
124   }
125   return self;
126 }
127
128 - (void)hotKeyHit:(GTMCarbonHotKey*)hotKey {
129   monitor_->OnDisconnectShortcut();
130 }
131
132 - (void)localMouseMoved:(const webrtc::DesktopVector&)mousePos {
133   monitor_->OnLocalMouseMoved(mousePos);
134 }
135
136 - (void)invalidate {
137   if (hotKey_) {
138     GTMCarbonEventDispatcherHandler* handler =
139         [GTMCarbonEventDispatcherHandler sharedEventDispatcherHandler];
140     [handler unregisterHotKey:hotKey_];
141     hotKey_ = NULL;
142   }
143   if (mouseRunLoopSource_) {
144     CFMachPortInvalidate(mouseMachPort_);
145     CFRunLoopRemoveSource(
146         CFRunLoopGetMain(), mouseRunLoopSource_, kCFRunLoopCommonModes);
147     CFRelease(mouseRunLoopSource_);
148     mouseMachPort_.reset(0);
149     mouseRunLoopSource_ = NULL;
150   }
151 }
152
153 @end
154
155 namespace remoting {
156 namespace {
157
158 class LocalInputMonitorMac::Core
159     : public base::RefCountedThreadSafe<Core>,
160       public EventHandler {
161  public:
162   Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
163        scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
164        base::WeakPtr<ClientSessionControl> client_session_control);
165
166   void Start();
167   void Stop();
168
169  private:
170   friend class base::RefCountedThreadSafe<Core>;
171   virtual ~Core();
172
173   void StartOnUiThread();
174   void StopOnUiThread();
175
176   // EventHandler interface.
177   virtual void OnLocalMouseMoved(
178       const webrtc::DesktopVector& position) OVERRIDE;
179   virtual void OnDisconnectShortcut() OVERRIDE;
180
181   // Task runner on which public methods of this class must be called.
182   scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
183
184   // Task runner on which |window_| is created.
185   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
186
187   LocalInputMonitorManager* manager_;
188
189   // Invoked in the |caller_task_runner_| thread to report local mouse events
190   // and session disconnect requests.
191   base::WeakPtr<ClientSessionControl> client_session_control_;
192
193   webrtc::DesktopVector mouse_position_;
194
195   DISALLOW_COPY_AND_ASSIGN(Core);
196 };
197
198 LocalInputMonitorMac::LocalInputMonitorMac(
199     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
200     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
201     base::WeakPtr<ClientSessionControl> client_session_control)
202     : core_(new Core(caller_task_runner,
203                      ui_task_runner,
204                      client_session_control)) {
205   core_->Start();
206 }
207
208 LocalInputMonitorMac::~LocalInputMonitorMac() {
209   core_->Stop();
210 }
211
212 LocalInputMonitorMac::Core::Core(
213     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
214     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
215     base::WeakPtr<ClientSessionControl> client_session_control)
216     : caller_task_runner_(caller_task_runner),
217       ui_task_runner_(ui_task_runner),
218       manager_(nil),
219       client_session_control_(client_session_control) {
220   DCHECK(client_session_control_);
221 }
222
223 void LocalInputMonitorMac::Core::Start() {
224   DCHECK(caller_task_runner_->BelongsToCurrentThread());
225
226   ui_task_runner_->PostTask(FROM_HERE,
227                             base::Bind(&Core::StartOnUiThread, this));
228 }
229
230 void LocalInputMonitorMac::Core::Stop() {
231   DCHECK(caller_task_runner_->BelongsToCurrentThread());
232
233   ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::StopOnUiThread, this));
234 }
235
236 LocalInputMonitorMac::Core::~Core() {
237   DCHECK(manager_ == nil);
238 }
239
240 void LocalInputMonitorMac::Core::StartOnUiThread() {
241   DCHECK(ui_task_runner_->BelongsToCurrentThread());
242
243   manager_ = [[LocalInputMonitorManager alloc] initWithMonitor:this];
244 }
245
246 void LocalInputMonitorMac::Core::StopOnUiThread() {
247   DCHECK(ui_task_runner_->BelongsToCurrentThread());
248
249   [manager_ invalidate];
250   [manager_ release];
251   manager_ = nil;
252 }
253
254 void LocalInputMonitorMac::Core::OnLocalMouseMoved(
255     const webrtc::DesktopVector& position) {
256   // In some cases OS may emit bogus mouse-move events even when cursor is not
257   // actually moving. To handle this case properly verify that mouse position
258   // has changed. See crbug.com/360912 .
259   if (position.equals(mouse_position_)) {
260     return;
261   }
262
263   mouse_position_ = position;
264
265   caller_task_runner_->PostTask(
266       FROM_HERE, base::Bind(&ClientSessionControl::OnLocalMouseMoved,
267                             client_session_control_,
268                             position));
269 }
270
271 void LocalInputMonitorMac::Core::OnDisconnectShortcut() {
272   caller_task_runner_->PostTask(
273       FROM_HERE, base::Bind(&ClientSessionControl::DisconnectSession,
274                             client_session_control_));
275 }
276
277 }  // namespace
278
279 scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create(
280     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
281     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
282     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
283     base::WeakPtr<ClientSessionControl> client_session_control) {
284   return scoped_ptr<LocalInputMonitor>(
285       new LocalInputMonitorMac(caller_task_runner,
286                                ui_task_runner,
287                                client_session_control));
288 }
289
290 }  // namespace remoting