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