blob: f116424bf47a5d96a8664dec7877650891b703fc [file] [log] [blame]
/*
* 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