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