1/* 2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27#import "WebEventFactory.h" 28 29#if USE(APPKIT) 30 31#import "WebKitSystemInterface.h" 32#import <wtf/ASCIICType.h> 33#import <WebCore/PlatformEventFactoryMac.h> 34#import <WebCore/Scrollbar.h> 35#import <WebCore/WindowsKeyboardCodes.h> 36 37using namespace WebCore; 38 39@interface NSEvent (WebNSEventDetails) 40- (NSInteger)_scrollCount; 41- (CGFloat)_unacceleratedScrollingDeltaX; 42- (CGFloat)_unacceleratedScrollingDeltaY; 43@end 44 45namespace WebKit { 46 47// FIXME: This is a huge copy/paste from WebCore/PlatformEventFactoryMac.mm. The code should be merged. 48 49static WebMouseEvent::Button currentMouseButton() 50{ 51 NSUInteger pressedMouseButtons = [NSEvent pressedMouseButtons]; 52 if (!pressedMouseButtons) 53 return WebMouseEvent::NoButton; 54 if (pressedMouseButtons == 1 << 0) 55 return WebMouseEvent::LeftButton; 56 if (pressedMouseButtons == 1 << 1) 57 return WebMouseEvent::RightButton; 58 return WebMouseEvent::MiddleButton; 59} 60 61static WebMouseEvent::Button mouseButtonForEvent(NSEvent *event) 62{ 63 switch ([event type]) { 64 case NSLeftMouseDown: 65 case NSLeftMouseUp: 66 case NSLeftMouseDragged: 67 return WebMouseEvent::LeftButton; 68 case NSRightMouseDown: 69 case NSRightMouseUp: 70 case NSRightMouseDragged: 71 return WebMouseEvent::RightButton; 72 case NSOtherMouseDown: 73 case NSOtherMouseUp: 74 case NSOtherMouseDragged: 75 return WebMouseEvent::MiddleButton; 76 case NSMouseEntered: 77 case NSMouseExited: 78 return currentMouseButton(); 79 default: 80 return WebMouseEvent::NoButton; 81 } 82} 83 84static WebEvent::Type mouseEventTypeForEvent(NSEvent* event) 85{ 86 switch ([event type]) { 87 case NSLeftMouseDragged: 88 case NSMouseEntered: 89 case NSMouseExited: 90 case NSMouseMoved: 91 case NSOtherMouseDragged: 92 case NSRightMouseDragged: 93 return WebEvent::MouseMove; 94 case NSLeftMouseDown: 95 case NSRightMouseDown: 96 case NSOtherMouseDown: 97 return WebEvent::MouseDown; 98 case NSLeftMouseUp: 99 case NSRightMouseUp: 100 case NSOtherMouseUp: 101 return WebEvent::MouseUp; 102 default: 103 return WebEvent::MouseMove; 104 } 105} 106 107static int clickCountForEvent(NSEvent *event) 108{ 109 switch ([event type]) { 110 case NSLeftMouseDown: 111 case NSLeftMouseUp: 112 case NSLeftMouseDragged: 113 case NSRightMouseDown: 114 case NSRightMouseUp: 115 case NSRightMouseDragged: 116 case NSOtherMouseDown: 117 case NSOtherMouseUp: 118 case NSOtherMouseDragged: 119 return [event clickCount]; 120 default: 121 return 0; 122 } 123} 124 125static NSScreen *screenForWindow(NSWindow *window) 126{ 127 NSScreen *screen = [window screen]; // nil if the window is off-screen 128 if (screen) 129 return screen; 130 131 NSArray *screens = [NSScreen screens]; 132 if ([screens count] > 0) 133 return [screens objectAtIndex:0]; // screen containing the menubar 134 135 return nil; 136} 137 138static NSPoint flipScreenPoint(const NSPoint& screenPoint, NSScreen *screen) 139{ 140 NSPoint flippedPoint = screenPoint; 141 flippedPoint.y = NSMaxY([screen frame]) - flippedPoint.y; 142 return flippedPoint; 143} 144 145static NSPoint globalPoint(const NSPoint& windowPoint, NSWindow *window) 146{ 147 return flipScreenPoint([window convertBaseToScreen:windowPoint], screenForWindow(window)); 148} 149 150static NSPoint globalPointForEvent(NSEvent *event) 151{ 152 switch ([event type]) { 153 case NSLeftMouseDown: 154 case NSLeftMouseDragged: 155 case NSLeftMouseUp: 156 case NSMouseEntered: 157 case NSMouseExited: 158 case NSMouseMoved: 159 case NSOtherMouseDown: 160 case NSOtherMouseDragged: 161 case NSOtherMouseUp: 162 case NSRightMouseDown: 163 case NSRightMouseDragged: 164 case NSRightMouseUp: 165 case NSScrollWheel: 166 return globalPoint([event locationInWindow], [event window]); 167 default: 168 return NSZeroPoint; 169 } 170} 171 172static NSPoint pointForEvent(NSEvent *event, NSView *windowView) 173{ 174 switch ([event type]) { 175 case NSLeftMouseDown: 176 case NSLeftMouseDragged: 177 case NSLeftMouseUp: 178 case NSMouseEntered: 179 case NSMouseExited: 180 case NSMouseMoved: 181 case NSOtherMouseDown: 182 case NSOtherMouseDragged: 183 case NSOtherMouseUp: 184 case NSRightMouseDown: 185 case NSRightMouseDragged: 186 case NSRightMouseUp: 187 case NSScrollWheel: { 188 // Note: This will have its origin at the bottom left of the window unless windowView is flipped. 189 // In those cases, the Y coordinate gets flipped by Widget::convertFromContainingWindow. 190 NSPoint location = [event locationInWindow]; 191 if (windowView) 192 location = [windowView convertPoint:location fromView:nil]; 193 return location; 194 } 195 default: 196 return NSZeroPoint; 197 } 198} 199 200static WebWheelEvent::Phase phaseForEvent(NSEvent *event) 201{ 202 uint32_t phase = WebWheelEvent::PhaseNone; 203 if ([event phase] & NSEventPhaseBegan) 204 phase |= WebWheelEvent::PhaseBegan; 205 if ([event phase] & NSEventPhaseStationary) 206 phase |= WebWheelEvent::PhaseStationary; 207 if ([event phase] & NSEventPhaseChanged) 208 phase |= WebWheelEvent::PhaseChanged; 209 if ([event phase] & NSEventPhaseEnded) 210 phase |= WebWheelEvent::PhaseEnded; 211 if ([event phase] & NSEventPhaseCancelled) 212 phase |= WebWheelEvent::PhaseCancelled; 213#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 214 if ([event phase] & NSEventPhaseMayBegin) 215 phase |= WebWheelEvent::PhaseMayBegin; 216#endif 217 218 return static_cast<WebWheelEvent::Phase>(phase); 219} 220 221static WebWheelEvent::Phase momentumPhaseForEvent(NSEvent *event) 222{ 223 uint32_t phase = WebWheelEvent::PhaseNone; 224 225 if ([event momentumPhase] & NSEventPhaseBegan) 226 phase |= WebWheelEvent::PhaseBegan; 227 if ([event momentumPhase] & NSEventPhaseStationary) 228 phase |= WebWheelEvent::PhaseStationary; 229 if ([event momentumPhase] & NSEventPhaseChanged) 230 phase |= WebWheelEvent::PhaseChanged; 231 if ([event momentumPhase] & NSEventPhaseEnded) 232 phase |= WebWheelEvent::PhaseEnded; 233 if ([event momentumPhase] & NSEventPhaseCancelled) 234 phase |= WebWheelEvent::PhaseCancelled; 235 236 return static_cast<WebWheelEvent::Phase>(phase); 237} 238 239#if ENABLE(GESTURE_EVENTS) 240static WebEvent::Type gestureEventTypeForEvent(NSEvent *event) 241{ 242 switch ([event type]) { 243 case NSEventTypeBeginGesture: 244 return WebEvent::GestureScrollBegin; 245 case NSEventTypeEndGesture: 246 return WebEvent::GestureScrollEnd; 247 default: 248 ASSERT_NOT_REACHED(); 249 return WebEvent::GestureScrollEnd; 250 } 251} 252#endif 253 254static inline String textFromEvent(NSEvent* event) 255{ 256 if ([event type] == NSFlagsChanged) 257 return String(""); 258 return String([event characters]); 259} 260 261static inline String unmodifiedTextFromEvent(NSEvent* event) 262{ 263 if ([event type] == NSFlagsChanged) 264 return String(""); 265 return String([event charactersIgnoringModifiers]); 266} 267 268static bool isKeypadEvent(NSEvent* event) 269{ 270 // Check that this is the type of event that has a keyCode. 271 switch ([event type]) { 272 case NSKeyDown: 273 case NSKeyUp: 274 case NSFlagsChanged: 275 break; 276 default: 277 return false; 278 } 279 280 switch ([event keyCode]) { 281 case 71: // Clear 282 case 81: // = 283 case 75: // / 284 case 67: // * 285 case 78: // - 286 case 69: // + 287 case 76: // Enter 288 case 65: // . 289 case 82: // 0 290 case 83: // 1 291 case 84: // 2 292 case 85: // 3 293 case 86: // 4 294 case 87: // 5 295 case 88: // 6 296 case 89: // 7 297 case 91: // 8 298 case 92: // 9 299 return true; 300 } 301 302 return false; 303} 304 305static inline bool isKeyUpEvent(NSEvent *event) 306{ 307 if ([event type] != NSFlagsChanged) 308 return [event type] == NSKeyUp; 309 // FIXME: This logic fails if the user presses both Shift keys at once, for example: 310 // we treat releasing one of them as keyDown. 311 switch ([event keyCode]) { 312 case 54: // Right Command 313 case 55: // Left Command 314 return ([event modifierFlags] & NSCommandKeyMask) == 0; 315 316 case 57: // Capslock 317 return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0; 318 319 case 56: // Left Shift 320 case 60: // Right Shift 321 return ([event modifierFlags] & NSShiftKeyMask) == 0; 322 323 case 58: // Left Alt 324 case 61: // Right Alt 325 return ([event modifierFlags] & NSAlternateKeyMask) == 0; 326 327 case 59: // Left Ctrl 328 case 62: // Right Ctrl 329 return ([event modifierFlags] & NSControlKeyMask) == 0; 330 331 case 63: // Function 332 return ([event modifierFlags] & NSFunctionKeyMask) == 0; 333 } 334 return false; 335} 336 337static inline WebEvent::Modifiers modifiersForEvent(NSEvent *event) 338{ 339 unsigned modifiers = 0; 340 if ([event modifierFlags] & NSAlphaShiftKeyMask) 341 modifiers |= WebEvent::CapsLockKey; 342 if ([event modifierFlags] & NSShiftKeyMask) 343 modifiers |= WebEvent::ShiftKey; 344 if ([event modifierFlags] & NSControlKeyMask) 345 modifiers |= WebEvent::ControlKey; 346 if ([event modifierFlags] & NSAlternateKeyMask) 347 modifiers |= WebEvent::AltKey; 348 if ([event modifierFlags] & NSCommandKeyMask) 349 modifiers |= WebEvent::MetaKey; 350 return (WebEvent::Modifiers)modifiers; 351} 352 353WebMouseEvent WebEventFactory::createWebMouseEvent(NSEvent *event, NSView *windowView) 354{ 355 NSPoint position = pointForEvent(event, windowView); 356 NSPoint globalPosition = globalPointForEvent(event); 357 358 WebEvent::Type type = mouseEventTypeForEvent(event); 359 WebMouseEvent::Button button = mouseButtonForEvent(event); 360 float deltaX = [event deltaX]; 361 float deltaY = [event deltaY]; 362 float deltaZ = [event deltaZ]; 363 int clickCount = clickCountForEvent(event); 364 WebEvent::Modifiers modifiers = modifiersForEvent(event); 365 double timestamp = [event timestamp]; 366 367 return WebMouseEvent(type, button, IntPoint(position), IntPoint(globalPosition), deltaX, deltaY, deltaZ, clickCount, modifiers, timestamp); 368} 369 370WebWheelEvent WebEventFactory::createWebWheelEvent(NSEvent *event, NSView *windowView) 371{ 372 NSPoint position = pointForEvent(event, windowView); 373 NSPoint globalPosition = globalPointForEvent(event); 374 375 BOOL continuous; 376 float deltaX = 0; 377 float deltaY = 0; 378 float wheelTicksX = 0; 379 float wheelTicksY = 0; 380 381 WKGetWheelEventDeltas(event, &deltaX, &deltaY, &continuous); 382 383 if (continuous) { 384 // smooth scroll events 385 wheelTicksX = deltaX / static_cast<float>(Scrollbar::pixelsPerLineStep()); 386 wheelTicksY = deltaY / static_cast<float>(Scrollbar::pixelsPerLineStep()); 387 } else { 388 // plain old wheel events 389 wheelTicksX = deltaX; 390 wheelTicksY = deltaY; 391 deltaX *= static_cast<float>(Scrollbar::pixelsPerLineStep()); 392 deltaY *= static_cast<float>(Scrollbar::pixelsPerLineStep()); 393 } 394 395 WebWheelEvent::Granularity granularity = WebWheelEvent::ScrollByPixelWheelEvent; 396 397#if HAVE(INVERTED_WHEEL_EVENTS) 398 bool directionInvertedFromDevice = [event isDirectionInvertedFromDevice]; 399#else 400 bool directionInvertedFromDevice = false; 401#endif 402 403 WebWheelEvent::Phase phase = phaseForEvent(event); 404 WebWheelEvent::Phase momentumPhase = momentumPhaseForEvent(event); 405 bool hasPreciseScrollingDeltas = continuous; 406 407 uint32_t scrollCount; 408 FloatSize unacceleratedScrollingDelta; 409 410 static bool nsEventSupportsScrollCount = [NSEvent instancesRespondToSelector:@selector(_scrollCount)]; 411 if (nsEventSupportsScrollCount) { 412 scrollCount = [event _scrollCount]; 413 unacceleratedScrollingDelta = FloatSize([event _unacceleratedScrollingDeltaX], [event _unacceleratedScrollingDeltaY]); 414 } else { 415 scrollCount = 0; 416 unacceleratedScrollingDelta = FloatSize(deltaX, deltaY); 417 } 418 419 WebEvent::Modifiers modifiers = modifiersForEvent(event); 420 double timestamp = [event timestamp]; 421 422 return WebWheelEvent(WebEvent::Wheel, IntPoint(position), IntPoint(globalPosition), FloatSize(deltaX, deltaY), FloatSize(wheelTicksX, wheelTicksY), granularity, directionInvertedFromDevice, phase, momentumPhase, hasPreciseScrollingDeltas, scrollCount, unacceleratedScrollingDelta, modifiers, timestamp); 423} 424 425WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(NSEvent *event, NSView *) 426{ 427 WebEvent::Type type = isKeyUpEvent(event) ? WebEvent::KeyUp : WebEvent::KeyDown; 428 String text = textFromEvent(event); 429 String unmodifiedText = unmodifiedTextFromEvent(event); 430 String keyIdentifier = keyIdentifierForKeyEvent(event); 431 int windowsVirtualKeyCode = windowsKeyCodeForKeyEvent(event); 432 int nativeVirtualKeyCode = [event keyCode]; 433 int macCharCode = WKGetNSEventKeyChar(event); 434 bool autoRepeat = ([event type] != NSFlagsChanged) && [event isARepeat]; 435 bool isKeypad = isKeypadEvent(event); 436 bool isSystemKey = false; // SystemKey is always false on the Mac. 437 WebEvent::Modifiers modifiers = modifiersForEvent(event); 438 double timestamp = [event timestamp]; 439 440 // Always use 13 for Enter/Return -- we don't want to use AppKit's different character for Enter. 441 if (windowsVirtualKeyCode == VK_RETURN) { 442 text = "\r"; 443 unmodifiedText = "\r"; 444 } 445 446 // AppKit sets text to "\x7F" for backspace, but the correct KeyboardEvent character code is 8. 447 if (windowsVirtualKeyCode == VK_BACK) { 448 text = "\x8"; 449 unmodifiedText = "\x8"; 450 } 451 452 // Always use 9 for Tab -- we don't want to use AppKit's different character for shift-tab. 453 if (windowsVirtualKeyCode == VK_TAB) { 454 text = "\x9"; 455 unmodifiedText = "\x9"; 456 } 457 458 return WebKeyboardEvent(type, text, unmodifiedText, keyIdentifier, windowsVirtualKeyCode, nativeVirtualKeyCode, macCharCode, autoRepeat, isKeypad, isSystemKey, modifiers, timestamp); 459} 460 461#if ENABLE(GESTURE_EVENTS) 462WebGestureEvent WebEventFactory::createWebGestureEvent(NSEvent *event, NSView *windowView) 463{ 464 WebEvent::Type type = gestureEventTypeForEvent(event); 465 NSPoint position = pointForEvent(event, windowView); 466 NSPoint globalPosition = globalPointForEvent(event); 467 WebEvent::Modifiers modifiers = modifiersForEvent(event); 468 double timestamp = [event timestamp]; 469 470 return WebGestureEvent(type, IntPoint(position), IntPoint(globalPosition), modifiers, timestamp); 471} 472#endif 473 474} // namespace WebKit 475 476#endif // USE(APPKIT) 477