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 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 std::pair<NSEvent*, NSEvent*> RightMouseClickInView(NSView* view,
118 NSUInteger clickCount) {
119 const NSRect bounds = [view convertRect:[view bounds] toView:nil];
120 const NSPoint mid_point = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
121 NSEvent* down = MouseEventAtPointInWindow(mid_point, NSRightMouseDown,
122 [view window], clickCount);
123 NSEvent* up = MouseEventAtPointInWindow(mid_point, NSRightMouseUp,
124 [view window], clickCount);
125 return std::make_pair(down, up);
128 NSEvent* KeyEventWithCharacter(unichar c) {
129 return KeyEventWithKeyCode(0, c, NSKeyDown, 0);
132 NSEvent* KeyEventWithType(NSEventType event_type, NSUInteger modifiers) {
133 return KeyEventWithKeyCode(0x78, 'x', event_type, modifiers);
136 NSEvent* KeyEventWithKeyCode(unsigned short key_code,
138 NSEventType event_type,
139 NSUInteger modifiers) {
140 NSString* chars = [NSString stringWithCharacters:&c length:1];
141 return [NSEvent keyEventWithType:event_type
143 modifierFlags:modifiers
148 charactersIgnoringModifiers:chars
153 NSEvent* EnterExitEventWithType(NSEventType event_type) {
154 return [NSEvent enterExitEventWithType:event_type
165 NSEvent* OtherEventWithType(NSEventType event_type) {
166 return [NSEvent otherEventWithType:event_type
177 NSTimeInterval TimeIntervalSinceSystemStartup() {
178 return UpTimeInNanoseconds() / 1000000000.0;
181 NSEvent* SynthesizeKeyEvent(NSWindow* window,
183 ui::KeyboardCode keycode,
185 // If caps lock is set for an alpha keycode, treat it as if shift was pressed.
186 // Note on Mac (unlike other platforms) shift while caps is down does not go
187 // back to lowercase.
188 if (keycode >= ui::VKEY_A && keycode <= ui::VKEY_Z &&
189 (flags & NSAlphaShiftKeyMask))
190 flags |= NSShiftKeyMask;
192 // Clear caps regardless -- MacKeyCodeForWindowsKeyCode doesn't implement
193 // logic to support it.
194 flags &= ~NSAlphaShiftKeyMask;
197 unichar shifted_character;
198 int macKeycode = ui::MacKeyCodeForWindowsKeyCode(
199 keycode, flags, &shifted_character, &character);
204 // Note that, in line with AppKit's documentation (and tracing "real" events),
205 // -[NSEvent charactersIngoringModifiers]" are "the characters generated by
206 // the receiving key event as if no modifier key (except for Shift)".
207 // So |charactersIgnoringModifiers| uses |shifted_character|.
208 NSString* charactersIgnoringModifiers =
209 [[[NSString alloc] initWithCharacters:&shifted_character
210 length:1] autorelease];
211 NSString* characters;
212 // The following were determined empirically on OSX 10.9.
213 if (flags & NSControlKeyMask) {
214 // If Ctrl is pressed, Cocoa always puts an empty string into |characters|.
215 characters = [NSString string];
216 } else if (flags & NSCommandKeyMask) {
217 // If Cmd is pressed, Cocoa puts a lowercase character into |characters|,
218 // regardless of Shift. If, however, Alt is also pressed then shift *is*
219 // preserved, but re-mappings for Alt are not implemented. Although we still
220 // need to support Alt for things like Alt+Left/Right which don't care.
222 [[[NSString alloc] initWithCharacters:&character length:1] autorelease];
224 // If just Shift or nothing is pressed, |characters| will match
225 // |charactersIgnoringModifiers|. Alt puts a special character into
226 // |characters| (not |charactersIgnoringModifiers|), but they're not mapped
228 characters = charactersIgnoringModifiers;
231 NSEventType type = (keyDown ? NSKeyDown : NSKeyUp);
233 // Modifier keys generate NSFlagsChanged event rather than
234 // NSKeyDown/NSKeyUp events.
235 if (keycode == ui::VKEY_CONTROL || keycode == ui::VKEY_SHIFT ||
236 keycode == ui::VKEY_MENU || keycode == ui::VKEY_COMMAND)
237 type = NSFlagsChanged;
239 // For events other than mouse moved, [event locationInWindow] is
240 // UNDEFINED if the event is not NSMouseMoved. Thus, the (0,0)
241 // location should be fine.
242 NSEvent* event = [NSEvent keyEventWithType:type
245 timestamp:TimeIntervalSinceSystemStartup()
246 windowNumber:[window windowNumber]
248 characters:characters
249 charactersIgnoringModifiers:charactersIgnoringModifiers
251 keyCode:(unsigned short)macKeycode];
256 } // namespace cocoa_test_event_utils