| /* |
| * Copyright (C) 2011 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "PlatformEventFactoryMac.h" |
| |
| #import "KeyEventCocoa.h" |
| #import "Logging.h" |
| #import "PlatformScreen.h" |
| #import "Scrollbar.h" |
| #import "WebCoreSystemInterface.h" |
| #import "WindowsKeyboardCodes.h" |
| #import <wtf/ASCIICType.h> |
| |
| namespace WebCore { |
| |
| IntPoint globalPoint(const NSPoint& windowPoint, NSWindow *window) |
| { |
| return IntPoint(flipScreenPoint([window convertBaseToScreen:windowPoint], screenForWindow(window))); |
| } |
| |
| static IntPoint globalPointForEvent(NSEvent *event) |
| { |
| switch ([event type]) { |
| case NSLeftMouseDown: |
| case NSLeftMouseDragged: |
| case NSLeftMouseUp: |
| case NSMouseEntered: |
| case NSMouseExited: |
| case NSMouseMoved: |
| case NSOtherMouseDown: |
| case NSOtherMouseDragged: |
| case NSOtherMouseUp: |
| case NSRightMouseDown: |
| case NSRightMouseDragged: |
| case NSRightMouseUp: |
| case NSScrollWheel: |
| return globalPoint([event locationInWindow], [event window]); |
| default: |
| return IntPoint(); |
| } |
| } |
| |
| static IntPoint pointForEvent(NSEvent *event, NSView *windowView) |
| { |
| switch ([event type]) { |
| case NSLeftMouseDown: |
| case NSLeftMouseDragged: |
| case NSLeftMouseUp: |
| case NSMouseEntered: |
| case NSMouseExited: |
| case NSMouseMoved: |
| case NSOtherMouseDown: |
| case NSOtherMouseDragged: |
| case NSOtherMouseUp: |
| case NSRightMouseDown: |
| case NSRightMouseDragged: |
| case NSRightMouseUp: |
| case NSScrollWheel: { |
| // Note: This will have its origin at the bottom left of the window unless windowView is flipped. |
| // In those cases, the Y coordinate gets flipped by Widget::convertFromContainingWindow. |
| NSPoint location = [event locationInWindow]; |
| if (windowView) |
| location = [windowView convertPoint:location fromView:nil]; |
| return IntPoint(location); |
| } |
| default: |
| return IntPoint(); |
| } |
| } |
| |
| static MouseButton mouseButtonForEvent(NSEvent *event) |
| { |
| switch ([event type]) { |
| case NSLeftMouseDown: |
| case NSLeftMouseUp: |
| case NSLeftMouseDragged: |
| return LeftButton; |
| case NSRightMouseDown: |
| case NSRightMouseUp: |
| case NSRightMouseDragged: |
| return RightButton; |
| case NSOtherMouseDown: |
| case NSOtherMouseUp: |
| case NSOtherMouseDragged: |
| return MiddleButton; |
| default: |
| return NoButton; |
| } |
| } |
| |
| static PlatformEvent::Type mouseEventTypeForEvent(NSEvent* event) |
| { |
| switch ([event type]) { |
| case NSLeftMouseDragged: |
| case NSMouseEntered: |
| case NSMouseExited: |
| case NSMouseMoved: |
| case NSOtherMouseDragged: |
| case NSRightMouseDragged: |
| return PlatformEvent::MouseMoved; |
| case NSLeftMouseDown: |
| case NSRightMouseDown: |
| case NSOtherMouseDown: |
| return PlatformEvent::MousePressed; |
| case NSLeftMouseUp: |
| case NSRightMouseUp: |
| case NSOtherMouseUp: |
| return PlatformEvent::MouseReleased; |
| default: |
| return PlatformEvent::MouseMoved; |
| } |
| } |
| |
| static int clickCountForEvent(NSEvent *event) |
| { |
| switch ([event type]) { |
| case NSLeftMouseDown: |
| case NSLeftMouseUp: |
| case NSLeftMouseDragged: |
| case NSRightMouseDown: |
| case NSRightMouseUp: |
| case NSRightMouseDragged: |
| case NSOtherMouseDown: |
| case NSOtherMouseUp: |
| case NSOtherMouseDragged: |
| return [event clickCount]; |
| default: |
| return 0; |
| } |
| } |
| |
| static PlatformWheelEventPhase momentumPhaseForEvent(NSEvent *event) |
| { |
| uint32_t phase = PlatformWheelEventPhaseNone; |
| |
| #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 |
| if ([event momentumPhase] & NSEventPhaseBegan) |
| phase |= PlatformWheelEventPhaseBegan; |
| if ([event momentumPhase] & NSEventPhaseStationary) |
| phase |= PlatformWheelEventPhaseStationary; |
| if ([event momentumPhase] & NSEventPhaseChanged) |
| phase |= PlatformWheelEventPhaseChanged; |
| if ([event momentumPhase] & NSEventPhaseEnded) |
| phase |= PlatformWheelEventPhaseEnded; |
| if ([event momentumPhase] & NSEventPhaseCancelled) |
| phase |= PlatformWheelEventPhaseCancelled; |
| #else |
| switch (wkGetNSEventMomentumPhase(event)) { |
| case wkEventPhaseNone: |
| phase = PlatformWheelEventPhaseNone; |
| break; |
| case wkEventPhaseBegan: |
| phase = PlatformWheelEventPhaseBegan; |
| break; |
| case wkEventPhaseChanged: |
| phase = PlatformWheelEventPhaseChanged; |
| break; |
| case wkEventPhaseEnded: |
| phase = PlatformWheelEventPhaseEnded; |
| break; |
| } |
| #endif |
| |
| return static_cast<PlatformWheelEventPhase>(phase); |
| } |
| |
| static PlatformWheelEventPhase phaseForEvent(NSEvent *event) |
| { |
| #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 |
| uint32_t phase = PlatformWheelEventPhaseNone; |
| if ([event phase] & NSEventPhaseBegan) |
| phase |= PlatformWheelEventPhaseBegan; |
| if ([event phase] & NSEventPhaseStationary) |
| phase |= PlatformWheelEventPhaseStationary; |
| if ([event phase] & NSEventPhaseChanged) |
| phase |= PlatformWheelEventPhaseChanged; |
| if ([event phase] & NSEventPhaseEnded) |
| phase |= PlatformWheelEventPhaseEnded; |
| if ([event phase] & NSEventPhaseCancelled) |
| phase |= PlatformWheelEventPhaseCancelled; |
| #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 |
| if ([event momentumPhase] & NSEventPhaseMayBegin) |
| phase |= PlatformWheelEventPhaseMayBegin; |
| #endif |
| |
| return static_cast<PlatformWheelEventPhase>(phase); |
| #else |
| UNUSED_PARAM(event); |
| return PlatformWheelEventPhaseNone; |
| #endif |
| } |
| |
| #if ENABLE(GESTURE_EVENTS) |
| static PlatformEvent::Type gestureEventTypeForEvent(NSEvent *event) |
| { |
| switch ([event type]) { |
| case NSEventTypeBeginGesture: |
| return PlatformEvent::GestureScrollBegin; |
| case NSEventTypeEndGesture: |
| return PlatformEvent::GestureScrollEnd; |
| default: |
| ASSERT_NOT_REACHED(); |
| return PlatformEvent::GestureScrollEnd; |
| } |
| } |
| #endif |
| |
| static inline String textFromEvent(NSEvent* event) |
| { |
| if ([event type] == NSFlagsChanged) |
| return String(""); |
| return String([event characters]); |
| } |
| |
| static inline String unmodifiedTextFromEvent(NSEvent* event) |
| { |
| if ([event type] == NSFlagsChanged) |
| return String(""); |
| return String([event charactersIgnoringModifiers]); |
| } |
| |
| String keyIdentifierForKeyEvent(NSEvent* event) |
| { |
| if ([event type] == NSFlagsChanged) |
| switch ([event keyCode]) { |
| case 54: // Right Command |
| case 55: // Left Command |
| return String("Meta"); |
| |
| case 57: // Capslock |
| return String("CapsLock"); |
| |
| case 56: // Left Shift |
| case 60: // Right Shift |
| return String("Shift"); |
| |
| case 58: // Left Alt |
| case 61: // Right Alt |
| return String("Alt"); |
| |
| case 59: // Left Ctrl |
| case 62: // Right Ctrl |
| return String("Control"); |
| |
| default: |
| ASSERT_NOT_REACHED(); |
| return String(""); |
| } |
| |
| NSString *s = [event charactersIgnoringModifiers]; |
| if ([s length] != 1) { |
| LOG(Events, "received an unexpected number of characters in key event: %u", [s length]); |
| return "Unidentified"; |
| } |
| return keyIdentifierForCharCode([s characterAtIndex:0]); |
| } |
| |
| static bool isKeypadEvent(NSEvent* event) |
| { |
| // Check that this is the type of event that has a keyCode. |
| switch ([event type]) { |
| case NSKeyDown: |
| case NSKeyUp: |
| case NSFlagsChanged: |
| break; |
| default: |
| return false; |
| } |
| |
| if ([event modifierFlags] & NSNumericPadKeyMask) |
| return true; |
| |
| switch ([event keyCode]) { |
| case 71: // Clear |
| case 81: // = |
| case 75: // / |
| case 67: // * |
| case 78: // - |
| case 69: // + |
| case 76: // Enter |
| case 65: // . |
| case 82: // 0 |
| case 83: // 1 |
| case 84: // 2 |
| case 85: // 3 |
| case 86: // 4 |
| case 87: // 5 |
| case 88: // 6 |
| case 89: // 7 |
| case 91: // 8 |
| case 92: // 9 |
| return true; |
| } |
| |
| return false; |
| } |
| |
| int windowsKeyCodeForKeyEvent(NSEvent* event) |
| { |
| int code = 0; |
| // There are several kinds of characters for which we produce key code from char code: |
| // 1. Roman letters. Windows keyboard layouts affect both virtual key codes and character codes for these, |
| // so e.g. 'A' gets the same keyCode on QWERTY, AZERTY or Dvorak layouts. |
| // 2. Keys for which there is no known Mac virtual key codes, like PrintScreen. |
| // 3. Certain punctuation keys. On Windows, these are also remapped depending on current keyboard layout, |
| // but see comment in windowsKeyCodeForCharCode(). |
| if (!isKeypadEvent(event) && ([event type] == NSKeyDown || [event type] == NSKeyUp)) { |
| // Cmd switches Roman letters for Dvorak-QWERTY layout, so try modified characters first. |
| NSString* s = [event characters]; |
| code = [s length] > 0 ? windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0; |
| if (code) |
| return code; |
| |
| // Ctrl+A on an AZERTY keyboard would get VK_Q keyCode if we relied on -[NSEvent keyCode] below. |
| s = [event charactersIgnoringModifiers]; |
| code = [s length] > 0 ? windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0; |
| if (code) |
| return code; |
| } |
| |
| // Map Mac virtual key code directly to Windows one for any keys not handled above. |
| // E.g. the key next to Caps Lock has the same Event.keyCode on U.S. keyboard ('A') and on Russian keyboard (CYRILLIC LETTER EF). |
| return windowsKeyCodeForKeyCode([event keyCode]); |
| } |
| |
| static inline bool isKeyUpEvent(NSEvent *event) |
| { |
| if ([event type] != NSFlagsChanged) |
| return [event type] == NSKeyUp; |
| // FIXME: This logic fails if the user presses both Shift keys at once, for example: |
| // we treat releasing one of them as keyDown. |
| switch ([event keyCode]) { |
| case 54: // Right Command |
| case 55: // Left Command |
| return ([event modifierFlags] & NSCommandKeyMask) == 0; |
| |
| case 57: // Capslock |
| return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0; |
| |
| case 56: // Left Shift |
| case 60: // Right Shift |
| return ([event modifierFlags] & NSShiftKeyMask) == 0; |
| |
| case 58: // Left Alt |
| case 61: // Right Alt |
| return ([event modifierFlags] & NSAlternateKeyMask) == 0; |
| |
| case 59: // Left Ctrl |
| case 62: // Right Ctrl |
| return ([event modifierFlags] & NSControlKeyMask) == 0; |
| |
| case 63: // Function |
| return ([event modifierFlags] & NSFunctionKeyMask) == 0; |
| } |
| return false; |
| } |
| |
| static inline PlatformEvent::Modifiers modifiersForEvent(NSEvent *event) |
| { |
| unsigned modifiers = 0; |
| if ([event modifierFlags] & NSShiftKeyMask) |
| modifiers |= PlatformEvent::ShiftKey; |
| if ([event modifierFlags] & NSControlKeyMask) |
| modifiers |= PlatformEvent::CtrlKey; |
| if ([event modifierFlags] & NSAlternateKeyMask) |
| modifiers |= PlatformEvent::AltKey; |
| if ([event modifierFlags] & NSCommandKeyMask) |
| modifiers |= PlatformEvent::MetaKey; |
| return (PlatformEvent::Modifiers)modifiers; |
| } |
| |
| |
| class PlatformMouseEventBuilder : public PlatformMouseEvent { |
| public: |
| PlatformMouseEventBuilder(NSEvent *event, NSView *windowView) |
| { |
| // PlatformEvent |
| m_type = mouseEventTypeForEvent(event); |
| m_modifiers = modifiersForEvent(event); |
| m_timestamp = [event timestamp]; |
| |
| // PlatformMouseEvent |
| m_position = pointForEvent(event, windowView); |
| m_globalPosition = globalPointForEvent(event); |
| m_button = mouseButtonForEvent(event); |
| m_clickCount = clickCountForEvent(event); |
| |
| // Mac specific |
| m_modifierFlags = [event modifierFlags]; |
| m_eventNumber = [event eventNumber]; |
| } |
| }; |
| |
| PlatformMouseEvent PlatformEventFactory::createPlatformMouseEvent(NSEvent *event, NSView *windowView) |
| { |
| return PlatformMouseEventBuilder(event, windowView); |
| } |
| |
| |
| class PlatformWheelEventBuilder : public PlatformWheelEvent { |
| public: |
| PlatformWheelEventBuilder(NSEvent *event, NSView *windowView) |
| { |
| // PlatformEvent |
| m_type = PlatformEvent::Wheel; |
| m_modifiers = modifiersForEvent(event); |
| m_timestamp = [event timestamp]; |
| |
| // PlatformWheelEvent |
| m_position = pointForEvent(event, windowView); |
| m_globalPosition = globalPointForEvent(event); |
| m_granularity = ScrollByPixelWheelEvent; |
| |
| BOOL continuous; |
| wkGetWheelEventDeltas(event, &m_deltaX, &m_deltaY, &continuous); |
| if (continuous) { |
| m_wheelTicksX = m_deltaX / static_cast<float>(Scrollbar::pixelsPerLineStep()); |
| m_wheelTicksY = m_deltaY / static_cast<float>(Scrollbar::pixelsPerLineStep()); |
| } else { |
| m_wheelTicksX = m_deltaX; |
| m_wheelTicksY = m_deltaY; |
| m_deltaX *= static_cast<float>(Scrollbar::pixelsPerLineStep()); |
| m_deltaY *= static_cast<float>(Scrollbar::pixelsPerLineStep()); |
| } |
| |
| m_phase = phaseForEvent(event); |
| m_momentumPhase = momentumPhaseForEvent(event); |
| m_hasPreciseScrollingDeltas = continuous; |
| |
| #if HAVE(INVERTED_WHEEL_EVENTS) |
| m_directionInvertedFromDevice = [event isDirectionInvertedFromDevice]; |
| #else |
| m_directionInvertedFromDevice = false; |
| #endif |
| } |
| }; |
| |
| PlatformWheelEvent PlatformEventFactory::createPlatformWheelEvent(NSEvent *event, NSView *windowView) |
| { |
| return PlatformWheelEventBuilder(event, windowView); |
| } |
| |
| |
| class PlatformKeyboardEventBuilder : public PlatformKeyboardEvent { |
| public: |
| PlatformKeyboardEventBuilder(NSEvent *event) |
| { |
| // PlatformEvent |
| m_type = isKeyUpEvent(event) ? PlatformEvent::KeyUp : PlatformEvent::KeyDown; |
| m_modifiers = modifiersForEvent(event); |
| m_timestamp = [event timestamp]; |
| |
| // PlatformKeyboardEvent |
| m_text = textFromEvent(event); |
| m_unmodifiedText = unmodifiedTextFromEvent(event); |
| m_keyIdentifier = keyIdentifierForKeyEvent(event); |
| m_windowsVirtualKeyCode = windowsKeyCodeForKeyEvent(event); |
| m_nativeVirtualKeyCode = [event keyCode]; |
| m_macCharCode = wkGetNSEventKeyChar(event); |
| m_autoRepeat = ([event type] != NSFlagsChanged) && [event isARepeat]; |
| m_isKeypad = isKeypadEvent(event); |
| m_isSystemKey = false; // SystemKey is always false on the Mac. |
| |
| // Always use 13 for Enter/Return -- we don't want to use AppKit's different character for Enter. |
| if (m_windowsVirtualKeyCode == VK_RETURN) { |
| m_text = "\r"; |
| m_unmodifiedText = "\r"; |
| } |
| |
| // AppKit sets text to "\x7F" for backspace, but the correct KeyboardEvent character code is 8. |
| if (m_windowsVirtualKeyCode == VK_BACK) { |
| m_text = "\x8"; |
| m_unmodifiedText = "\x8"; |
| } |
| |
| // Always use 9 for Tab -- we don't want to use AppKit's different character for shift-tab. |
| if (m_windowsVirtualKeyCode == VK_TAB) { |
| m_text = "\x9"; |
| m_unmodifiedText = "\x9"; |
| } |
| |
| // Mac specific. |
| m_macEvent = event; |
| } |
| }; |
| |
| PlatformKeyboardEvent PlatformEventFactory::createPlatformKeyboardEvent(NSEvent *event) |
| { |
| return PlatformKeyboardEventBuilder(event); |
| } |
| |
| #if ENABLE(GESTURE_EVENTS) |
| class PlatformGestureEventBuilder : public PlatformGestureEvent { |
| public: |
| PlatformGestureEventBuilder(NSEvent *event, NSView *windowView) |
| { |
| // PlatformEvent |
| m_type = gestureEventTypeForEvent(event); |
| m_modifiers = modifiersForEvent(event); |
| m_timestamp = [event timestamp]; |
| |
| // PlatformGestureEvent |
| m_position = pointForEvent(event, windowView); |
| m_globalPosition = globalPointForEvent(event); |
| m_deltaX = 0; |
| m_deltaY = 0; |
| } |
| }; |
| |
| PlatformGestureEvent PlatformEventFactory::createPlatformGestureEvent(NSEvent *event, NSView *windowView) |
| { |
| return PlatformGestureEventBuilder(event, windowView); |
| } |
| #endif |
| |
| } // namespace WebCore |