OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "ui/base/test/ui_controls.h" | 5 #include "ui/base/test/ui_controls.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
12 #import "base/mac/foundation_util.h" | |
13 #import "base/mac/scoped_nsautorelease_pool.h" | |
14 #import "base/mac/scoped_nsobject.h" | |
15 #import "base/mac/scoped_objc_class_swizzler.h" | |
12 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
17 #include "base/thread_task_runner_handle.h" | |
13 #include "ui/base/cocoa/cocoa_base_utils.h" | 18 #include "ui/base/cocoa/cocoa_base_utils.h" |
19 #include "content/public/browser/browser_thread.h" | |
14 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" | 20 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" |
15 #import "ui/events/test/cocoa_test_event_utils.h" | 21 #import "ui/events/test/cocoa_test_event_utils.h" |
22 #import "ui/gfx/mac/coordinate_conversion.h" | |
23 #include "ui/gfx/screen.h" | |
16 | 24 |
17 // Implementation details: We use [NSApplication sendEvent:] instead | 25 // Implementation details: We use [NSApplication sendEvent:] instead |
18 // of [NSApplication postEvent:atStart:] so that the event gets sent | 26 // of [NSApplication postEvent:atStart:] so that the event gets sent |
19 // immediately. This lets us run the post-event task right | 27 // immediately. This lets us run the post-event task right |
20 // immediately as well. Unfortunately I cannot subclass NSEvent (it's | 28 // immediately as well. Unfortunately I cannot subclass NSEvent (it's |
21 // probably a class cluster) to allow other easy answers. For | 29 // probably a class cluster) to allow other easy answers. For |
22 // example, if I could subclass NSEvent, I could run the Task in it's | 30 // example, if I could subclass NSEvent, I could run the Task in it's |
23 // dealloc routine (which necessarily happens after the event is | 31 // dealloc routine (which necessarily happens after the event is |
24 // dispatched). Unlike Linux, Mac does not have message loop | 32 // dispatched). Unlike Linux, Mac does not have message loop |
25 // observer/notification. Unlike windows, I cannot post non-events | 33 // observer/notification. Unlike windows, I cannot post non-events |
(...skipping 15 matching lines...) Expand all Loading... | |
41 // events causes BrowserKeyEventsTest.CommandKeyEvents to fail. | 49 // events causes BrowserKeyEventsTest.CommandKeyEvents to fail. |
42 // See http://crbug.com/49270 | 50 // See http://crbug.com/49270 |
43 // 2. On OSX 10.6, [NSEvent addLocalMonitorForEventsMatchingMask:handler:] may | 51 // 2. On OSX 10.6, [NSEvent addLocalMonitorForEventsMatchingMask:handler:] may |
44 // be used, so that we don't need to poll the event queue time to time. | 52 // be used, so that we don't need to poll the event queue time to time. |
45 | 53 |
46 using cocoa_test_event_utils::SynthesizeKeyEvent; | 54 using cocoa_test_event_utils::SynthesizeKeyEvent; |
47 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; | 55 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; |
48 | 56 |
49 namespace { | 57 namespace { |
50 | 58 |
59 // When enabled, all simulated mouse events will be posted to | |
60 // the WindowServer, and the actual mouse will move on the screen. | |
61 bool g_use_cgevents = false; | |
62 | |
51 // Stores the current mouse location on the screen. So that we can use it | 63 // Stores the current mouse location on the screen. So that we can use it |
52 // when firing keyboard and mouse click events. | 64 // when firing keyboard and mouse click events. |
53 NSPoint g_mouse_location = { 0, 0 }; | 65 NSPoint g_mouse_location = { 0, 0 }; |
54 | 66 |
67 // Stores the current pressed mouse buttons. Indexed by | |
68 // ui_controls::MouseButton. | |
69 bool g_mouse_button_down[3] = { false, false, false }; | |
70 | |
55 bool g_ui_controls_enabled = false; | 71 bool g_ui_controls_enabled = false; |
56 | 72 |
57 // Creates the proper sequence of autoreleased key events for a key down + up. | 73 // Creates the proper sequence of autoreleased key events for a key down + up. |
58 void SynthesizeKeyEventsSequence(NSWindow* window, | 74 void SynthesizeKeyEventsSequence(NSWindow* window, |
59 ui::KeyboardCode keycode, | 75 ui::KeyboardCode keycode, |
60 bool control, | 76 bool control, |
61 bool shift, | 77 bool shift, |
62 bool alt, | 78 bool alt, |
63 bool command, | 79 bool command, |
64 std::vector<NSEvent*>* events) { | 80 std::vector<NSEvent*>* events) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
115 events->push_back(event); | 131 events->push_back(event); |
116 } | 132 } |
117 if (control) { | 133 if (control) { |
118 flags &= ~NSControlKeyMask; | 134 flags &= ~NSControlKeyMask; |
119 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags); | 135 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags); |
120 DCHECK(event); | 136 DCHECK(event); |
121 events->push_back(event); | 137 events->push_back(event); |
122 } | 138 } |
123 } | 139 } |
124 | 140 |
141 void RunTaskInTaskRunner( | |
142 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | |
143 const base::Closure& task) { | |
144 task_runner->PostTask(FROM_HERE, task); | |
145 } | |
146 | |
125 // A helper function to watch for the event queue. The specific task will be | 147 // A helper function to watch for the event queue. The specific task will be |
126 // fired when there is no more event in the queue. | 148 // fired when there is no more event in the queue. |
149 // NOTE: It should be run on the UI thread, as otherwise it will always report | |
150 // there are no pending events. | |
127 void EventQueueWatcher(const base::Closure& task) { | 151 void EventQueueWatcher(const base::Closure& task) { |
152 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
128 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask | 153 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask |
129 untilDate:nil | 154 untilDate:nil |
130 inMode:NSDefaultRunLoopMode | 155 inMode:NSDefaultRunLoopMode |
131 dequeue:NO]; | 156 dequeue:NO]; |
132 // If there is still event in the queue, then we need to check again. | 157 // If there is still event in the queue, then we need to check again. |
133 if (event) { | 158 if (event) { |
134 base::MessageLoop::current()->PostTask( | 159 base::MessageLoop::current()->PostTask( |
135 FROM_HERE, | 160 FROM_HERE, |
136 base::Bind(&EventQueueWatcher, task)); | 161 base::Bind(&EventQueueWatcher, task)); |
137 } else { | 162 } else { |
(...skipping 28 matching lines...) Expand all Loading... | |
166 return window; | 191 return window; |
167 } | 192 } |
168 | 193 |
169 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test | 194 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test |
170 // uses those, it will need to handle that itself. | 195 // uses those, it will need to handle that itself. |
171 return nil; | 196 return nil; |
172 } | 197 } |
173 | 198 |
174 } // namespace | 199 } // namespace |
175 | 200 |
201 // Since CGEvents take some time to reach -[NSApplication sendEvent:] we use | |
202 // this class to wait for the events to start being processed before sending | |
203 // finish notifications. | |
204 class EventMonitor { | |
205 public: | |
206 void NotifyWhenEventIsProcessed(NSEvent* event, const base::Closure& task) { | |
207 dispatch_sync(queue_, ^{ | |
208 tasks_.emplace_back(Task(event, task)); | |
209 }); | |
210 } | |
211 | |
212 void ProcessingEvent(NSEvent* event) { | |
213 dispatch_sync(queue_, ^{ | |
214 auto it = std::find_if( | |
215 tasks_.begin(), tasks_.end(), | |
216 [&event](const Task& task) { return task.MatchesEvent(event); }); | |
217 if (it != tasks_.end()) { | |
218 it->Run(); | |
219 tasks_.erase(it); | |
220 } | |
221 }); | |
222 } | |
223 | |
224 static EventMonitor* Instance() { | |
225 static EventMonitor* monitor = nullptr; | |
226 if (!monitor) { | |
227 monitor = new EventMonitor(); | |
228 } | |
229 return monitor; | |
230 } | |
231 | |
232 private: | |
233 class Task { | |
234 public: | |
235 Task(NSEvent* event, const base::Closure& task) | |
236 : task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
237 event_([event retain]), | |
238 finish_closure_(task) {} | |
239 | |
240 bool MatchesEvent(NSEvent* event) const { | |
241 // When moving the window using BridgedNativeWidget::RunMoveLoop the | |
242 // locationInWindow can be a little inconsistent with what we expect. | |
243 // Seems that only comparing event type is fine. | |
244 return | |
245 [event_ type] == [event type] && [event_ subtype] == [event subtype]; | |
246 } | |
247 | |
248 void Run() { | |
249 // We get here before the event is actually processed. Run the | |
250 // EventQueueWatcher on the main thread in order to wait for all events to | |
251 // finish processing. | |
252 content::BrowserThread::PostTask( | |
253 content::BrowserThread::UI, FROM_HERE, | |
254 base::Bind( | |
255 &EventQueueWatcher, | |
256 base::Bind(&RunTaskInTaskRunner, task_runner_, finish_closure_))); | |
257 } | |
258 | |
259 private: | |
260 // Events could be spawned on background threads. Be sure to invoke the | |
261 // |finish_closure_| on an appropriate thread. | |
262 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
263 base::scoped_nsobject<NSEvent> event_; | |
264 base::Closure finish_closure_; | |
265 }; | |
266 | |
267 EventMonitor() | |
268 : queue_(dispatch_queue_create("ui_controls_mac.EventMonitor", | |
269 DISPATCH_QUEUE_SERIAL)), | |
270 send_event_swizzler_( | |
271 new base::mac::ScopedObjCClassSwizzler([NSApplication class], | |
272 @selector(sendEvent:), | |
273 @selector(cr_sendEvent:))) { | |
274 } | |
275 | |
276 std::vector<Task> tasks_; | |
277 | |
278 // synchronizes access to the |tasks_| in case we spawn the events on a | |
279 // different thread | |
280 dispatch_queue_t queue_ = 0; | |
281 | |
282 scoped_ptr<base::mac::ScopedObjCClassSwizzler> | |
283 send_event_swizzler_; | |
284 | |
285 DISALLOW_COPY_AND_ASSIGN(EventMonitor); | |
286 }; | |
287 | |
288 // Donates testing implementations of NSApplication methods. We can't simply | |
289 // use -[NSEvent addLocalMonitorForEventsMatchingMask:handler:], as other event | |
290 // monitors could have precedence, and they could filter the events before we | |
291 // can see them. But since nobody swizzles -[NSApplication sendEvent:] we should | |
292 // be safe. | |
293 @interface NSApplication(TestingDonor) | |
294 @end | |
295 | |
296 @implementation NSApplication(TestingDonor) | |
297 - (void)cr_sendEvent:(NSEvent*)event { | |
298 // Invoke the finish handler before the event is processed, since we can get | |
299 // stuck in BridgedNativeWidget::RunMoveLoop and would never see the event | |
300 // otherwise. | |
301 EventMonitor::Instance()->ProcessingEvent(event); | |
302 | |
303 [self cr_sendEvent:event]; | |
304 } | |
305 @end | |
306 | |
307 // Donates testing implementations of NSEvent methods. | |
308 @interface FakeNSEventTestingDonor : NSObject | |
309 @end | |
310 | |
311 @implementation FakeNSEventTestingDonor | |
312 + (NSPoint)mouseLocation { | |
313 return g_mouse_location; | |
314 } | |
315 | |
316 + (NSUInteger)pressedMouseButtons { | |
317 NSUInteger result = 0; | |
318 const int buttons[3] = { | |
319 ui_controls::LEFT, ui_controls::RIGHT, ui_controls::MIDDLE}; | |
320 for (unsigned int i = 0; i < arraysize(buttons); ++i) { | |
321 if (g_mouse_button_down[buttons[i]]) | |
322 result |= (1 << i); | |
323 } | |
324 return result; | |
325 } | |
326 @end | |
327 | |
328 // Donates testing implementations of NSWindow methods. | |
329 @interface FakeNSWindowTestingDonor : NSObject | |
330 @end | |
331 | |
332 @implementation FakeNSWindowTestingDonor | |
333 - (NSPoint)mouseLocationOutsideOfEventStream { | |
334 NSWindow* window = base::mac::ObjCCastStrict<NSWindow>(self); | |
335 return [window convertScreenToBase:g_mouse_location]; | |
336 } | |
337 @end | |
338 | |
339 namespace { | |
340 class NSEventSwizzler { | |
341 public: | |
342 static void Install() { | |
343 static NSEventSwizzler* swizzler = nullptr; | |
344 if (!swizzler) { | |
345 swizzler = new NSEventSwizzler(); | |
346 } | |
347 } | |
348 | |
349 protected: | |
350 NSEventSwizzler() | |
351 : mouse_location_swizzler_(new base::mac::ScopedObjCClassSwizzler( | |
352 [NSEvent class], | |
353 [FakeNSEventTestingDonor class], | |
354 @selector(mouseLocation))), | |
355 pressed_mouse_buttons_swizzler_(new base::mac::ScopedObjCClassSwizzler( | |
356 [NSEvent class], | |
357 [FakeNSEventTestingDonor class], | |
358 @selector(pressedMouseButtons))), | |
359 mouse_location_outside_of_event_stream_swizzler_( | |
360 new base::mac::ScopedObjCClassSwizzler( | |
361 [NSWindow class], | |
362 [FakeNSWindowTestingDonor class], | |
363 @selector(mouseLocationOutsideOfEventStream))) {} | |
364 | |
365 private: | |
366 scoped_ptr<base::mac::ScopedObjCClassSwizzler> mouse_location_swizzler_; | |
367 scoped_ptr<base::mac::ScopedObjCClassSwizzler> | |
368 pressed_mouse_buttons_swizzler_; | |
369 scoped_ptr<base::mac::ScopedObjCClassSwizzler> | |
370 mouse_location_outside_of_event_stream_swizzler_; | |
371 }; | |
372 } // namespace | |
373 | |
176 namespace ui_controls { | 374 namespace ui_controls { |
177 | 375 |
178 void EnableUIControls() { | 376 void EnableUIControls() { |
179 g_ui_controls_enabled = true; | 377 g_ui_controls_enabled = true; |
180 } | 378 } |
181 | 379 |
182 bool SendKeyPress(gfx::NativeWindow window, | 380 bool SendKeyPress(gfx::NativeWindow window, |
183 ui::KeyboardCode key, | 381 ui::KeyboardCode key, |
184 bool control, | 382 bool control, |
185 bool shift, | 383 bool shift, |
186 bool alt, | 384 bool alt, |
187 bool command) { | 385 bool command) { |
188 CHECK(g_ui_controls_enabled); | 386 CHECK(g_ui_controls_enabled); |
189 return SendKeyPressNotifyWhenDone(window, key, | 387 return SendKeyPressNotifyWhenDone(window, key, |
190 control, shift, alt, command, | 388 control, shift, alt, command, |
191 base::Closure()); | 389 base::Closure()); |
192 } | 390 } |
193 | 391 |
194 // Win and Linux implement a SendKeyPress() this as a | 392 // Win and Linux implement a SendKeyPress() this as a |
195 // SendKeyPressAndRelease(), so we should as well (despite the name). | 393 // SendKeyPressAndRelease(), so we should as well (despite the name). |
196 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, | 394 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, |
197 ui::KeyboardCode key, | 395 ui::KeyboardCode key, |
198 bool control, | 396 bool control, |
199 bool shift, | 397 bool shift, |
200 bool alt, | 398 bool alt, |
201 bool command, | 399 bool command, |
202 const base::Closure& task) { | 400 const base::Closure& task) { |
401 DCHECK(!g_use_cgevents) << "Not implemented"; | |
203 CHECK(g_ui_controls_enabled); | 402 CHECK(g_ui_controls_enabled); |
204 DCHECK(base::MessageLoopForUI::IsCurrent()); | 403 DCHECK(base::MessageLoopForUI::IsCurrent()); |
205 | 404 |
405 // We want to destroy the autoreleased event ASAP. | |
tapted
2016/03/10 11:51:19
what happens if we don't? I think since we rely on
themblsha
2016/03/10 17:18:58
It was necessary when I've added an associated obj
| |
406 base::mac::ScopedNSAutoreleasePool pool; | |
407 | |
206 std::vector<NSEvent*> events; | 408 std::vector<NSEvent*> events; |
207 SynthesizeKeyEventsSequence( | 409 SynthesizeKeyEventsSequence( |
208 window, key, control, shift, alt, command, &events); | 410 window, key, control, shift, alt, command, &events); |
209 | 411 |
210 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes | 412 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes |
211 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 | 413 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 |
212 // But using [NSApplication sendEvent:] should be safe for keyboard events, | 414 // But using [NSApplication sendEvent:] should be safe for keyboard events, |
213 // because until now, no code wants to retrieve the next event when handling | 415 // because until now, no code wants to retrieve the next event when handling |
214 // a keyboard event. | 416 // a keyboard event. |
215 for (std::vector<NSEvent*>::iterator iter = events.begin(); | 417 for (std::vector<NSEvent*>::iterator iter = events.begin(); |
(...skipping 12 matching lines...) Expand all Loading... | |
228 CHECK(g_ui_controls_enabled); | 430 CHECK(g_ui_controls_enabled); |
229 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); | 431 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); |
230 } | 432 } |
231 | 433 |
232 // Input position is in screen coordinates. However, NSMouseMoved | 434 // Input position is in screen coordinates. However, NSMouseMoved |
233 // events require them window-relative, so we adjust. We *DO* flip | 435 // events require them window-relative, so we adjust. We *DO* flip |
234 // the coordinate space, so input events can be the same for all | 436 // the coordinate space, so input events can be the same for all |
235 // platforms. E.g. (0,0) is upper-left. | 437 // platforms. E.g. (0,0) is upper-left. |
236 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { | 438 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { |
237 CHECK(g_ui_controls_enabled); | 439 CHECK(g_ui_controls_enabled); |
238 CGFloat screenHeight = | 440 g_mouse_location = gfx::ScreenPointToNSPoint(gfx::Point(x, y)); // flip! |
239 [[[NSScreen screens] firstObject] frame].size.height; | 441 NSEventSwizzler::Install(); |
240 g_mouse_location = NSMakePoint(x, screenHeight - y); // flip! | |
241 | 442 |
242 NSWindow* window = WindowAtCurrentMouseLocation(); | 443 NSWindow* window = WindowAtCurrentMouseLocation(); |
243 | 444 |
244 NSPoint pointInWindow = g_mouse_location; | 445 NSPoint pointInWindow = g_mouse_location; |
245 if (window) | 446 if (window) |
246 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); | 447 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); |
247 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); | 448 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); |
248 | 449 |
450 NSEventType event_type = NSMouseMoved; | |
451 if (g_mouse_button_down[LEFT]) { | |
452 event_type = NSLeftMouseDragged; | |
453 } else if (g_mouse_button_down[RIGHT]) { | |
454 event_type = NSRightMouseDragged; | |
455 } else if (g_mouse_button_down[MIDDLE]) { | |
456 event_type = NSOtherMouseDragged; | |
457 } | |
458 | |
459 // We want to destroy the autoreleased event ASAP. | |
460 base::mac::ScopedNSAutoreleasePool pool; | |
461 | |
249 NSEvent* event = | 462 NSEvent* event = |
250 [NSEvent mouseEventWithType:NSMouseMoved | 463 [NSEvent mouseEventWithType:event_type |
251 location:pointInWindow | 464 location:pointInWindow |
252 modifierFlags:0 | 465 modifierFlags:0 |
253 timestamp:timestamp | 466 timestamp:timestamp |
254 windowNumber:[window windowNumber] | 467 windowNumber:[window windowNumber] |
255 context:nil | 468 context:nil |
256 eventNumber:0 | 469 eventNumber:0 |
257 clickCount:0 | 470 clickCount:(event_type == NSMouseMoved ? 0 : 1) |
258 pressure:0.0]; | 471 pressure:(event_type == NSMouseMoved ? 0.0 : 1.0)]; |
259 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
260 | 472 |
261 if (!task.is_null()) { | 473 if (!task.is_null()) { |
262 base::MessageLoop::current()->PostTask( | 474 EventMonitor::Instance()->NotifyWhenEventIsProcessed(event, task); |
263 FROM_HERE, base::Bind(&EventQueueWatcher, task)); | 475 } |
476 | |
477 gfx::Point gp = gfx::ScreenPointFromNSPoint(g_mouse_location); | |
478 CGWarpMouseCursorPosition(CGPointMake(gp.x(), gp.y())); | |
479 | |
480 if (g_use_cgevents) { | |
481 CGEventPost(kCGSessionEventTap, [event CGEvent]); | |
482 } else { | |
483 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
264 } | 484 } |
265 | 485 |
266 return true; | 486 return true; |
267 } | 487 } |
268 | 488 |
269 bool SendMouseEvents(MouseButton type, int state) { | 489 bool SendMouseEvents(MouseButton type, int state) { |
270 CHECK(g_ui_controls_enabled); | 490 CHECK(g_ui_controls_enabled); |
271 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); | 491 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); |
272 } | 492 } |
273 | 493 |
274 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, | 494 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, |
275 const base::Closure& task) { | 495 const base::Closure& task) { |
276 CHECK(g_ui_controls_enabled); | 496 CHECK(g_ui_controls_enabled); |
277 // On windows it appears state can be (UP|DOWN). It is unclear if | 497 // On windows it appears state can be (UP|DOWN). It is unclear if |
278 // that'll happen here but prepare for it just in case. | 498 // that'll happen here but prepare for it just in case. |
279 if (state == (UP|DOWN)) { | 499 if (state == (UP|DOWN)) { |
280 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) && | 500 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) && |
281 SendMouseEventsNotifyWhenDone(type, UP, task)); | 501 SendMouseEventsNotifyWhenDone(type, UP, task)); |
282 } | 502 } |
283 NSEventType etype = NSLeftMouseDown; | 503 NSEventType event_type = NSLeftMouseDown; |
284 if (type == LEFT) { | 504 if (type == LEFT) { |
285 if (state == UP) { | 505 if (state == UP) { |
286 etype = NSLeftMouseUp; | 506 event_type = NSLeftMouseUp; |
287 } else { | 507 } else { |
288 etype = NSLeftMouseDown; | 508 event_type = NSLeftMouseDown; |
289 } | 509 } |
290 } else if (type == MIDDLE) { | 510 } else if (type == MIDDLE) { |
291 if (state == UP) { | 511 if (state == UP) { |
292 etype = NSOtherMouseUp; | 512 event_type = NSOtherMouseUp; |
293 } else { | 513 } else { |
294 etype = NSOtherMouseDown; | 514 event_type = NSOtherMouseDown; |
295 } | 515 } |
296 } else if (type == RIGHT) { | 516 } else if (type == RIGHT) { |
297 if (state == UP) { | 517 if (state == UP) { |
298 etype = NSRightMouseUp; | 518 event_type = NSRightMouseUp; |
299 } else { | 519 } else { |
300 etype = NSRightMouseDown; | 520 event_type = NSRightMouseDown; |
301 } | 521 } |
302 } else { | 522 } else { |
523 NOTREACHED(); | |
303 return false; | 524 return false; |
304 } | 525 } |
526 g_mouse_button_down[type] = state == DOWN; | |
527 | |
305 NSWindow* window = WindowAtCurrentMouseLocation(); | 528 NSWindow* window = WindowAtCurrentMouseLocation(); |
306 NSPoint pointInWindow = g_mouse_location; | 529 NSPoint pointInWindow = g_mouse_location; |
307 if (window) | 530 if (window) |
308 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); | 531 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); |
309 | 532 |
533 NSEventSwizzler::Install(); | |
534 | |
535 // We want to destroy the autoreleased event ASAP. | |
536 base::mac::ScopedNSAutoreleasePool pool; | |
537 | |
310 NSEvent* event = | 538 NSEvent* event = |
311 [NSEvent mouseEventWithType:etype | 539 [NSEvent mouseEventWithType:event_type |
312 location:pointInWindow | 540 location:pointInWindow |
313 modifierFlags:0 | 541 modifierFlags:0 |
314 timestamp:TimeIntervalSinceSystemStartup() | 542 timestamp:TimeIntervalSinceSystemStartup() |
315 windowNumber:[window windowNumber] | 543 windowNumber:[window windowNumber] |
316 context:nil | 544 context:nil |
317 eventNumber:0 | 545 eventNumber:0 |
318 clickCount:1 | 546 clickCount:1 |
319 pressure:(state == DOWN ? 1.0 : 0.0 )]; | 547 pressure:(state == DOWN ? 1.0 : 0.0 )]; |
320 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
321 | 548 |
322 if (!task.is_null()) { | 549 if (!task.is_null()) { |
323 base::MessageLoop::current()->PostTask( | 550 EventMonitor::Instance()->NotifyWhenEventIsProcessed(event, task); |
324 FROM_HERE, base::Bind(&EventQueueWatcher, task)); | 551 } |
552 | |
553 if (g_use_cgevents) { | |
554 CGEventPost(kCGSessionEventTap, [event CGEvent]); | |
555 } else { | |
556 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
325 } | 557 } |
326 | 558 |
327 return true; | 559 return true; |
328 } | 560 } |
329 | 561 |
330 bool SendMouseClick(MouseButton type) { | 562 bool SendMouseClick(MouseButton type) { |
331 CHECK(g_ui_controls_enabled); | 563 CHECK(g_ui_controls_enabled); |
332 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, base::Closure()); | 564 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, base::Closure()); |
333 } | 565 } |
334 | 566 |
335 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { | 567 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { |
336 base::MessageLoop::current()->PostTask( | 568 base::MessageLoop::current()->PostTask( |
337 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); | 569 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); |
338 } | 570 } |
339 | 571 |
340 bool IsFullKeyboardAccessEnabled() { | 572 bool IsFullKeyboardAccessEnabled() { |
341 return [NSApp isFullKeyboardAccessEnabled]; | 573 return [NSApp isFullKeyboardAccessEnabled]; |
342 } | 574 } |
343 | 575 |
576 void SetSendMouseEventsAsCGEvents(bool enable_cgevents) { | |
577 g_use_cgevents = enable_cgevents; | |
578 } | |
579 | |
580 bool SendMouseEventsAsCGEvents() { | |
581 return g_use_cgevents; | |
582 } | |
583 | |
584 void NotifyWhenEventIsProcessed(NSEvent* event, const base::Closure& task) { | |
585 EventMonitor::Instance()->NotifyWhenEventIsProcessed(event, task); | |
586 } | |
587 | |
344 } // namespace ui_controls | 588 } // namespace ui_controls |
OLD | NEW |