1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import <Cocoa/Cocoa.h>
6 #include <mach/mach_time.h>
8 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
9 #include "ui/events/test/cocoa_test_event_utils.h"
11 namespace cocoa_test_event_utils {
16 // http://stackoverflow.com/questions/1597383/cgeventtimestamp-to-nsdate
17 // Which credits Apple sample code for this routine.
18 uint64_t UpTimeInNanoseconds(void) {
21 static mach_timebase_info_data_t sTimebaseInfo;
23 time = mach_absolute_time();
25 // Convert to nanoseconds.
27 // If this is the first time we've run, get the timebase.
28 // We can use denom == 0 to indicate that sTimebaseInfo is
29 // uninitialised because it makes no sense to have a zero
30 // denominator is a fraction.
31 if (sTimebaseInfo.denom == 0) {
32 (void) mach_timebase_info(&sTimebaseInfo);
35 // This could overflow; for testing needs we probably don't care.
36 timeNano = time * sTimebaseInfo.numer / sTimebaseInfo.denom;
42 NSEvent* MouseEventAtPoint(NSPoint point, NSEventType type,
43 NSUInteger modifiers) {
44 if (type == NSOtherMouseUp) {
45 // To synthesize middle clicks we need to create a CGEvent with the
46 // "center" button flags so that our resulting NSEvent will have the
47 // appropriate buttonNumber field. NSEvent provides no way to create a
48 // mouse event with a buttonNumber directly.
49 CGPoint location = { point.x, point.y };
50 CGEventRef cg_event = CGEventCreateMouseEvent(NULL, kCGEventOtherMouseUp,
52 kCGMouseButtonCenter);
53 // Also specify the modifiers for the middle click case. This makes this
54 // test resilient to external modifiers being pressed.
55 CGEventSetFlags(cg_event, modifiers);
56 NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
60 return [NSEvent mouseEventWithType:type
62 modifierFlags:modifiers
71 NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers) {
72 return MouseEventAtPoint(NSZeroPoint, type, modifiers);
75 static NSEvent* MouseEventAtPointInWindow(NSPoint point,
78 NSUInteger clickCount) {
79 return [NSEvent mouseEventWithType:type
83 windowNumber:[window windowNumber]
90 NSEvent* RightMouseDownAtPointInWindow(NSPoint point, NSWindow* window) {
91 return MouseEventAtPointInWindow(point, NSRightMouseDown, window, 1);
94 NSEvent* RightMouseDownAtPoint(NSPoint point) {
95 return RightMouseDownAtPointInWindow(point, nil);
98 NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window) {
99 return MouseEventAtPointInWindow(point, NSLeftMouseDown, window, 1);
102 NSEvent* LeftMouseDownAtPoint(NSPoint point) {
103 return LeftMouseDownAtPointInWindow(point, nil);
106 std::pair<NSEvent*,NSEvent*> MouseClickInView(NSView* view,
107 NSUInteger clickCount) {
108 const NSRect bounds = [view convertRect:[view bounds] toView:nil];
109 const NSPoint mid_point = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
110 NSEvent* down = MouseEventAtPointInWindow(mid_point, NSLeftMouseDown,
111 [view window], clickCount);
112 NSEvent* up = MouseEventAtPointInWindow(mid_point, NSLeftMouseUp,
113 [view window], clickCount);
114 return std::make_pair(down, up);
117 NSEvent* KeyEventWithCharacter(unichar c) {
118 return KeyEventWithKeyCode(0, c, NSKeyDown, 0);
121 NSEvent* KeyEventWithType(NSEventType event_type, NSUInteger modifiers) {
122 return KeyEventWithKeyCode(0x78, 'x', event_type, modifiers);
125 NSEvent* KeyEventWithKeyCode(unsigned short key_code,
127 NSEventType event_type,
128 NSUInteger modifiers) {
129 NSString* chars = [NSString stringWithCharacters:&c length:1];
130 return [NSEvent keyEventWithType:event_type
132 modifierFlags:modifiers
137 charactersIgnoringModifiers:chars
142 NSEvent* EnterExitEventWithType(NSEventType event_type) {
143 return [NSEvent enterExitEventWithType:event_type
154 NSEvent* OtherEventWithType(NSEventType event_type) {
155 return [NSEvent otherEventWithType:event_type
166 NSTimeInterval TimeIntervalSinceSystemStartup() {
167 return UpTimeInNanoseconds() / 1000000000.0;
170 NSEvent* SynthesizeKeyEvent(NSWindow* window,
172 ui::KeyboardCode keycode,
175 unichar shifted_character;
176 int macKeycode = ui::MacKeyCodeForWindowsKeyCode(
177 keycode, flags, &shifted_character, &character);
182 // Note that, in line with AppKit's documentation (and tracing "real" events),
183 // -[NSEvent charactersIngoringModifiers]" are "the characters generated by
184 // the receiving key event as if no modifier key (except for Shift)".
185 // So |charactersIgnoringModifiers| uses |shifted_character|.
186 NSString* charactersIgnoringModifiers =
187 [[[NSString alloc] initWithCharacters:&shifted_character
188 length:1] autorelease];
189 NSString* characters;
190 // The following were determined empirically on OSX 10.9.
191 if (flags & NSControlKeyMask) {
192 // If Ctrl is pressed, Cocoa always puts an empty string into |characters|.
193 characters = [NSString string];
194 } else if (flags & NSCommandKeyMask) {
195 // If Cmd is pressed, Cocoa puts a lowercase character into |characters|,
196 // regardless of Shift. If, however, Alt is also pressed then shift *is*
197 // preserved, but re-mappings for Alt are not implemented. Although we still
198 // need to support Alt for things like Alt+Left/Right which don't care.
200 [[[NSString alloc] initWithCharacters:&character length:1] autorelease];
202 // If just Shift or nothing is pressed, |characters| will match
203 // |charactersIgnoringModifiers|. Alt puts a special character into
204 // |characters| (not |charactersIgnoringModifiers|), but they're not mapped
206 characters = charactersIgnoringModifiers;
209 NSEventType type = (keyDown ? NSKeyDown : NSKeyUp);
211 // Modifier keys generate NSFlagsChanged event rather than
212 // NSKeyDown/NSKeyUp events.
213 if (keycode == ui::VKEY_CONTROL || keycode == ui::VKEY_SHIFT ||
214 keycode == ui::VKEY_MENU || keycode == ui::VKEY_COMMAND)
215 type = NSFlagsChanged;
217 // For events other than mouse moved, [event locationInWindow] is
218 // UNDEFINED if the event is not NSMouseMoved. Thus, the (0,0)
219 // location should be fine.
220 NSEvent* event = [NSEvent keyEventWithType:type
223 timestamp:TimeIntervalSinceSystemStartup()
224 windowNumber:[window windowNumber]
226 characters:characters
227 charactersIgnoringModifiers:charactersIgnoringModifiers
229 keyCode:(unsigned short)macKeycode];
234 } // namespace cocoa_test_event_utils