1/*
2 * Copyright (C) 2011 Samsung Electronics
3 * Copyright (C) 2012 Intel Corporation
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "WebEventFactory.h"
29
30#include "EflKeyboardUtilities.h"
31#include <WebCore/AffineTransform.h>
32#include <WebCore/Scrollbar.h>
33
34#if ENABLE(TOUCH_EVENTS)
35#include "APIArray.h"
36#include "EwkTouchEvent.h"
37#include "EwkTouchPoint.h"
38#include "WKAPICast.h"
39#endif
40
41using namespace WebCore;
42
43namespace WebKit {
44
45enum {
46    VerticalScrollDirection = 0,
47    HorizontalScrollDirection = 1
48};
49
50enum {
51    LeftButton = 1,
52    MiddleButton = 2,
53    RightButton = 3
54};
55
56static const char keyPadPrefix[] = "KP_";
57
58static inline WebEvent::Modifiers modifiersForEvent(const Evas_Modifier* modifiers)
59{
60    unsigned result = 0;
61
62    if (evas_key_modifier_is_set(modifiers, "Shift"))
63        result |= WebEvent::ShiftKey;
64    if (evas_key_modifier_is_set(modifiers, "Control"))
65        result |= WebEvent::ControlKey;
66    if (evas_key_modifier_is_set(modifiers, "Alt"))
67        result |= WebEvent::AltKey;
68    if (evas_key_modifier_is_set(modifiers, "Meta"))
69        result |= WebEvent::MetaKey;
70
71    return static_cast<WebEvent::Modifiers>(result);
72}
73
74static inline WebMouseEvent::Button buttonForEvent(int button)
75{
76    if (button == LeftButton)
77        return WebMouseEvent::LeftButton;
78    if (button == MiddleButton)
79        return WebMouseEvent::MiddleButton;
80    if (button == RightButton)
81        return WebMouseEvent::RightButton;
82
83    return WebMouseEvent::NoButton;
84}
85
86static inline int clickCountForEvent(const Evas_Button_Flags flags)
87{
88    if (flags & EVAS_BUTTON_TRIPLE_CLICK)
89        return 3;
90    if (flags & EVAS_BUTTON_DOUBLE_CLICK)
91        return 2;
92
93    return 1;
94}
95
96static inline double convertMillisecondToSecond(unsigned timestamp)
97{
98    return static_cast<double>(timestamp) / 1000;
99}
100
101WebMouseEvent WebEventFactory::createWebMouseEvent(const Evas_Event_Mouse_Down* event, const AffineTransform& toWebContent, const AffineTransform& toDeviceScreen)
102{
103    IntPoint pos(event->canvas.x, event->canvas.y);
104    return WebMouseEvent(WebEvent::MouseDown,
105        buttonForEvent(event->button),
106        toWebContent.mapPoint(pos),
107        toDeviceScreen.mapPoint(pos),
108        0 /* deltaX */,
109        0 /* deltaY */,
110        0 /* deltaZ */,
111        clickCountForEvent(event->flags),
112        modifiersForEvent(event->modifiers),
113        convertMillisecondToSecond(event->timestamp));
114}
115
116WebMouseEvent WebEventFactory::createWebMouseEvent(const Evas_Event_Mouse_Up* event, const AffineTransform& toWebContent, const AffineTransform& toDeviceScreen)
117{
118    IntPoint pos(event->canvas.x, event->canvas.y);
119    return WebMouseEvent(WebEvent::MouseUp,
120        buttonForEvent(event->button),
121        toWebContent.mapPoint(pos),
122        toDeviceScreen.mapPoint(pos),
123        0 /* deltaX */,
124        0 /* deltaY */,
125        0 /* deltaZ */,
126        clickCountForEvent(event->flags),
127        modifiersForEvent(event->modifiers),
128        convertMillisecondToSecond(event->timestamp));
129}
130
131WebMouseEvent WebEventFactory::createWebMouseEvent(const Evas_Event_Mouse_Move* event, const AffineTransform& toWebContent, const AffineTransform& toDeviceScreen)
132{
133    IntPoint pos(event->cur.canvas.x, event->cur.canvas.y);
134    return WebMouseEvent(WebEvent::MouseMove,
135        buttonForEvent(event->buttons),
136        toWebContent.mapPoint(pos),
137        toDeviceScreen.mapPoint(pos),
138        (event->cur.canvas.x - event->prev.canvas.x) /* deltaX */,
139        (event->cur.canvas.y - event->prev.canvas.y) /* deltaY */,
140        0 /* deltaZ */,
141        0 /* clickCount */,
142        modifiersForEvent(event->modifiers),
143        convertMillisecondToSecond(event->timestamp));
144}
145
146WebWheelEvent WebEventFactory::createWebWheelEvent(const Evas_Event_Mouse_Wheel* event, const AffineTransform& toWebContent, const AffineTransform& toDeviceScreen)
147{
148    float deltaX = 0;
149    float deltaY = 0;
150    float wheelTicksX = 0;
151    float wheelTicksY = 0;
152
153    // A negative z value means (in EFL) that we are scrolling down, so we need
154    // to invert the value.
155    if (event->direction == VerticalScrollDirection) {
156        deltaX = 0;
157        deltaY = - event->z;
158    } else if (event->direction == HorizontalScrollDirection) {
159        deltaX = - event->z;
160        deltaY = 0;
161    }
162
163    wheelTicksX = deltaX;
164    wheelTicksY = deltaY;
165    deltaX *= static_cast<float>(Scrollbar::pixelsPerLineStep());
166    deltaY *= static_cast<float>(Scrollbar::pixelsPerLineStep());
167
168    IntPoint pos(event->canvas.x, event->canvas.y);
169
170    return WebWheelEvent(WebEvent::Wheel,
171        toWebContent.mapPoint(pos),
172        toDeviceScreen.mapPoint(pos),
173        FloatSize(deltaX, deltaY),
174        FloatSize(wheelTicksX, wheelTicksY),
175        WebWheelEvent::ScrollByPixelWheelEvent,
176        modifiersForEvent(event->modifiers),
177        convertMillisecondToSecond(event->timestamp));
178}
179
180WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(const Evas_Event_Key_Down* event)
181{
182    const String keyName(event->key);
183    return WebKeyboardEvent(WebEvent::KeyDown,
184        String::fromUTF8(event->string),
185        String::fromUTF8(event->string),
186        keyIdentifierForEvasKeyName(keyName),
187        windowsKeyCodeForEvasKeyName(keyName),
188        0 /* FIXME: nativeVirtualKeyCode */,
189        0 /* macCharCode */,
190        false /* FIXME: isAutoRepeat */,
191        keyName.startsWith(keyPadPrefix),
192        false /* isSystemKey */,
193        modifiersForEvent(event->modifiers),
194        convertMillisecondToSecond(event->timestamp));
195}
196
197WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(const Evas_Event_Key_Up* event)
198{
199    const String keyName(event->key);
200    return WebKeyboardEvent(WebEvent::KeyUp,
201        String::fromUTF8(event->string),
202        String::fromUTF8(event->string),
203        keyIdentifierForEvasKeyName(keyName),
204        windowsKeyCodeForEvasKeyName(keyName),
205        0 /* FIXME: nativeVirtualKeyCode */,
206        0 /* macCharCode */,
207        false /* FIXME: isAutoRepeat */,
208        keyName.startsWith(keyPadPrefix),
209        false /* isSystemKey */,
210        modifiersForEvent(event->modifiers),
211        convertMillisecondToSecond(event->timestamp));
212}
213
214#if ENABLE(TOUCH_EVENTS)
215static inline WebPlatformTouchPoint::TouchPointState toWebPlatformTouchPointState(WKTouchPointState state)
216{
217    switch (state) {
218    case kWKTouchPointStateTouchReleased:
219        return WebPlatformTouchPoint::TouchReleased;
220    case kWKTouchPointStateTouchMoved:
221        return WebPlatformTouchPoint::TouchMoved;
222    case kWKTouchPointStateTouchPressed:
223        return WebPlatformTouchPoint::TouchPressed;
224    case kWKTouchPointStateTouchStationary:
225        return WebPlatformTouchPoint::TouchStationary;
226    case kWKTouchPointStateTouchCancelled:
227    default:
228        return WebPlatformTouchPoint::TouchCancelled;
229    }
230}
231
232static inline WebEvent::Type toWebEventType(WKEventType type)
233{
234    switch (type) {
235    case kWKEventTypeTouchStart:
236        return WebEvent::TouchStart;
237    case kWKEventTypeTouchMove:
238        return WebEvent::TouchMove;
239    case kWKEventTypeTouchEnd:
240        return WebEvent::TouchEnd;
241    case kWKEventTypeTouchCancel:
242        return WebEvent::TouchCancel;
243    default:
244        return WebEvent::NoType;
245    }
246
247}
248
249static inline WebEvent::Modifiers toWebEventModifiers(unsigned modifiers)
250{
251    unsigned result = 0;
252
253    if (modifiers & kWKEventModifiersShiftKey)
254        result |= WebEvent::ShiftKey;
255    if (modifiers & kWKEventModifiersControlKey)
256        result |= WebEvent::ControlKey;
257    if (modifiers & kWKEventModifiersAltKey)
258        result |= WebEvent::AltKey;
259    if (modifiers & kWKEventModifiersMetaKey)
260        result |= WebEvent::MetaKey;
261
262    return static_cast<WebEvent::Modifiers>(result);
263}
264
265WebTouchEvent WebEventFactory::createWebTouchEvent(const EwkTouchEvent* event, const AffineTransform& toWebContent)
266{
267    API::Array* touchPointsArray = toImpl(event->touchPoints());
268    size_t size = touchPointsArray->size();
269
270    Vector<WebPlatformTouchPoint> touchPoints;
271    touchPoints.reserveInitialCapacity(size);
272
273    for (size_t i = 0; i < size; ++i) {
274        if (EwkTouchPoint* point = touchPointsArray->at<EwkTouchPoint>(i))
275            touchPoints.uncheckedAppend(WebPlatformTouchPoint(point->id(), toWebPlatformTouchPointState(point->state()), toIntPoint(point->screenPosition()), toWebContent.mapPoint(toIntPoint(point->position())), toIntSize(point->radius()), point->rotationAngle(), point->forceFactor()));
276    }
277
278    return WebTouchEvent(toWebEventType(event->eventType()), touchPoints, toWebEventModifiers(event->modifiers()), event->timestamp());
279}
280#endif
281
282} // namespace WebKit
283