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