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.
5 #include "remoting/host/local_input_monitor.h"
7 #import <AppKit/AppKit.h>
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 #include "third_party/skia/include/core/SkPoint.h"
21 #import "third_party/GTM/AppKit/GTMCarbonEvent.h"
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;
30 class LocalInputMonitorMac : public base::NonThreadSafe,
31 public LocalInputMonitor {
33 // Invoked by LocalInputMonitorManager.
36 virtual ~EventHandler() {}
38 virtual void OnLocalMouseMoved(const SkIPoint& position) = 0;
39 virtual void OnDisconnectShortcut() = 0;
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();
49 // The actual implementation resides in LocalInputMonitorMac::Core class.
51 scoped_refptr<Core> core_;
53 DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorMac);
57 } // namespace remoting
59 @interface LocalInputMonitorManager : NSObject {
61 GTMCarbonHotKey* hotKey_;
62 CFRunLoopSourceRef mouseRunLoopSource_;
63 base::ScopedCFTypeRef<CFMachPortRef> mouseMachPort_;
64 remoting::LocalInputMonitorMac::EventHandler* monitor_;
67 - (id)initWithMonitor:(remoting::LocalInputMonitorMac::EventHandler*)monitor;
69 // Called when the hotKey is hit.
70 - (void)hotKeyHit:(GTMCarbonHotKey*)hotKey;
72 // Called when the local mouse moves
73 - (void)localMouseMoved:(const SkIPoint&)mousePos;
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.
81 static CGEventRef LocalMouseMoved(CGEventTapProxy proxy, CGEventType type,
82 CGEventRef event, void* context) {
83 int64_t pid = CGEventGetIntegerValueField(event, kCGEventSourceUnixProcessID);
85 CGPoint cgMousePos = CGEventGetLocation(event);
86 SkIPoint mousePos = SkIPoint::Make(cgMousePos.x, cgMousePos.y);
87 [static_cast<LocalInputMonitorManager*>(context) localMouseMoved:mousePos];
92 @implementation LocalInputMonitorManager
94 - (id)initWithMonitor:(remoting::LocalInputMonitorMac::EventHandler*)monitor {
95 if ((self = [super init])) {
98 GTMCarbonEventDispatcherHandler* handler =
99 [GTMCarbonEventDispatcherHandler sharedEventDispatcherHandler];
100 hotKey_ = [handler registerHotKey:kEscKeyCode
101 modifiers:(NSAlternateKeyMask | NSControlKeyMask)
103 action:@selector(hotKeyHit:)
107 LOG(ERROR) << "registerHotKey failed.";
109 mouseMachPort_.reset(CGEventTapCreate(
110 kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly,
111 1 << kCGEventMouseMoved, LocalMouseMoved, self));
112 if (mouseMachPort_) {
113 mouseRunLoopSource_ = CFMachPortCreateRunLoopSource(
114 NULL, mouseMachPort_, 0);
116 CFRunLoopGetMain(), mouseRunLoopSource_, kCFRunLoopCommonModes);
118 LOG(ERROR) << "CGEventTapCreate failed.";
120 if (!hotKey_ && !mouseMachPort_) {
128 - (void)hotKeyHit:(GTMCarbonHotKey*)hotKey {
129 monitor_->OnDisconnectShortcut();
132 - (void)localMouseMoved:(const SkIPoint&)mousePos {
133 monitor_->OnLocalMouseMoved(mousePos);
138 GTMCarbonEventDispatcherHandler* handler =
139 [GTMCarbonEventDispatcherHandler sharedEventDispatcherHandler];
140 [handler unregisterHotKey:hotKey_];
143 if (mouseRunLoopSource_) {
144 CFMachPortInvalidate(mouseMachPort_);
145 CFRunLoopRemoveSource(
146 CFRunLoopGetMain(), mouseRunLoopSource_, kCFRunLoopCommonModes);
147 CFRelease(mouseRunLoopSource_);
148 mouseMachPort_.reset(0);
149 mouseRunLoopSource_ = NULL;
158 class LocalInputMonitorMac::Core
159 : public base::RefCountedThreadSafe<Core>,
160 public EventHandler {
162 Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
163 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
164 base::WeakPtr<ClientSessionControl> client_session_control);
170 friend class base::RefCountedThreadSafe<Core>;
173 void StartOnUiThread();
174 void StopOnUiThread();
176 // EventHandler interface.
177 virtual void OnLocalMouseMoved(const SkIPoint& position) OVERRIDE;
178 virtual void OnDisconnectShortcut() OVERRIDE;
180 // Task runner on which public methods of this class must be called.
181 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
183 // Task runner on which |window_| is created.
184 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
186 LocalInputMonitorManager* manager_;
188 // Invoked in the |caller_task_runner_| thread to report local mouse events
189 // and session disconnect requests.
190 base::WeakPtr<ClientSessionControl> client_session_control_;
192 DISALLOW_COPY_AND_ASSIGN(Core);
195 LocalInputMonitorMac::LocalInputMonitorMac(
196 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
197 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
198 base::WeakPtr<ClientSessionControl> client_session_control)
199 : core_(new Core(caller_task_runner,
201 client_session_control)) {
205 LocalInputMonitorMac::~LocalInputMonitorMac() {
209 LocalInputMonitorMac::Core::Core(
210 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
211 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
212 base::WeakPtr<ClientSessionControl> client_session_control)
213 : caller_task_runner_(caller_task_runner),
214 ui_task_runner_(ui_task_runner),
216 client_session_control_(client_session_control) {
217 DCHECK(client_session_control_);
220 void LocalInputMonitorMac::Core::Start() {
221 DCHECK(caller_task_runner_->BelongsToCurrentThread());
223 ui_task_runner_->PostTask(FROM_HERE,
224 base::Bind(&Core::StartOnUiThread, this));
227 void LocalInputMonitorMac::Core::Stop() {
228 DCHECK(caller_task_runner_->BelongsToCurrentThread());
230 ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::StopOnUiThread, this));
233 LocalInputMonitorMac::Core::~Core() {
234 DCHECK(manager_ == nil);
237 void LocalInputMonitorMac::Core::StartOnUiThread() {
238 DCHECK(ui_task_runner_->BelongsToCurrentThread());
240 manager_ = [[LocalInputMonitorManager alloc] initWithMonitor:this];
243 void LocalInputMonitorMac::Core::StopOnUiThread() {
244 DCHECK(ui_task_runner_->BelongsToCurrentThread());
246 [manager_ invalidate];
251 void LocalInputMonitorMac::Core::OnLocalMouseMoved(const SkIPoint& position) {
252 caller_task_runner_->PostTask(
253 FROM_HERE, base::Bind(&ClientSessionControl::OnLocalMouseMoved,
254 client_session_control_,
258 void LocalInputMonitorMac::Core::OnDisconnectShortcut() {
259 caller_task_runner_->PostTask(
260 FROM_HERE, base::Bind(&ClientSessionControl::DisconnectSession,
261 client_session_control_));
266 scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create(
267 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
268 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
269 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
270 base::WeakPtr<ClientSessionControl> client_session_control) {
271 return scoped_ptr<LocalInputMonitor>(
272 new LocalInputMonitorMac(caller_task_runner,
274 client_session_control));
277 } // namespace remoting