aab75a59091589a3de665e551fa6c330d03742cc
[WebKit-https.git] / Source / WebKit / Shared / gtk / WebEventFactory.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
4  * Copyright (C) 2011 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25  * THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "WebEventFactory.h"
30
31 #include <WebCore/GtkUtilities.h>
32 #include <WebCore/PlatformKeyboardEvent.h>
33 #include <WebCore/Scrollbar.h>
34 #include <WebCore/WindowsKeyboardCodes.h>
35 #include <gdk/gdk.h>
36 #include <gdk/gdkkeysyms.h>
37 #include <gtk/gtk.h>
38 #include <wtf/ASCIICType.h>
39
40 namespace WebKit {
41
42 using namespace WebCore;
43
44 static inline bool isGdkKeyCodeFromKeyPad(unsigned keyval)
45 {
46     return keyval >= GDK_KEY_KP_Space && keyval <= GDK_KEY_KP_9;
47 }
48
49 static inline OptionSet<WebEvent::Modifier> modifiersForEvent(const GdkEvent* event)
50 {
51     OptionSet<WebEvent::Modifier> modifiers;
52     GdkModifierType state;
53
54     // Check for a valid state in GdkEvent.
55     if (!gdk_event_get_state(event, &state))
56         return modifiers;
57
58     if (state & GDK_CONTROL_MASK)
59         modifiers.add(WebEvent::Modifier::ControlKey);
60     if (state & GDK_SHIFT_MASK)
61         modifiers.add(WebEvent::Modifier::ShiftKey);
62     if (state & GDK_MOD1_MASK)
63         modifiers.add(WebEvent::Modifier::AltKey);
64     if (state & GDK_META_MASK)
65         modifiers.add(WebEvent::Modifier::MetaKey);
66     if (PlatformKeyboardEvent::modifiersContainCapsLock(state))
67         modifiers.add(WebEvent::Modifier::CapsLockKey);
68
69     return modifiers;
70 }
71
72 static inline WebMouseEvent::Button buttonForEvent(const GdkEvent* event)
73 {
74     unsigned button = 0;
75 #if GTK_CHECK_VERSION(3, 10, 0)
76     GdkEventType type = gdk_event_get_event_type(event);
77 #else
78     GdkEventType type = event->type;
79 #endif
80     switch (type) {
81     case GDK_ENTER_NOTIFY:
82     case GDK_LEAVE_NOTIFY:
83     case GDK_MOTION_NOTIFY: {
84         button = WebMouseEvent::NoButton;
85         GdkModifierType state;
86         gdk_event_get_state(event, &state);
87         if (state & GDK_BUTTON1_MASK)
88             button = WebMouseEvent::LeftButton;
89         else if (state & GDK_BUTTON2_MASK)
90             button = WebMouseEvent::MiddleButton;
91         else if (state & GDK_BUTTON3_MASK)
92             button = WebMouseEvent::RightButton;
93         break;
94     }
95     case GDK_BUTTON_PRESS:
96     case GDK_2BUTTON_PRESS:
97     case GDK_3BUTTON_PRESS:
98     case GDK_BUTTON_RELEASE: {
99         guint eventButton;
100         gdk_event_get_button(event, &eventButton);
101
102         if (eventButton == 1)
103             button = WebMouseEvent::LeftButton;
104         else if (eventButton == 2)
105             button = WebMouseEvent::MiddleButton;
106         else if (eventButton == 3)
107             button = WebMouseEvent::RightButton;
108         break;
109     }
110     default:
111         ASSERT_NOT_REACHED();
112     }
113
114     return static_cast<WebMouseEvent::Button>(button);
115 }
116
117 static inline short pressedMouseButtons(GdkModifierType state)
118 {
119     // MouseEvent.buttons
120     // https://www.w3.org/TR/uievents/#ref-for-dom-mouseevent-buttons-1
121
122     // 0 MUST indicate no button is currently active.
123     short buttons = 0;
124
125     // 1 MUST indicate the primary button of the device (in general, the left button or the only button on
126     // single-button devices, used to activate a user interface control or select text).
127     if (state & GDK_BUTTON1_MASK)
128         buttons |= 1;
129
130     // 4 MUST indicate the auxiliary button (in general, the middle button, often combined with a mouse wheel).
131     if (state & GDK_BUTTON2_MASK)
132         buttons |= 4;
133
134     // 2 MUST indicate the secondary button (in general, the right button, often used to display a context menu),
135     // if present.
136     if (state & GDK_BUTTON3_MASK)
137         buttons |= 2;
138
139     return buttons;
140 }
141
142 WebMouseEvent WebEventFactory::createWebMouseEvent(const GdkEvent* event, int currentClickCount)
143 {
144     double x, y, xRoot, yRoot;
145     gdk_event_get_coords(event, &x, &y);
146     gdk_event_get_root_coords(event, &xRoot, &yRoot);
147
148     GdkModifierType state = static_cast<GdkModifierType>(0);
149     gdk_event_get_state(event, &state);
150
151     guint eventButton;
152     gdk_event_get_button(event, &eventButton);
153
154     WebEvent::Type type = static_cast<WebEvent::Type>(0);
155
156 #if GTK_CHECK_VERSION(3, 10, 0)
157     GdkEventType eventType = gdk_event_get_event_type(event);
158 #else
159     GdkEventType eventType = event->type;
160 #endif
161     switch (eventType) {
162     case GDK_MOTION_NOTIFY:
163     case GDK_ENTER_NOTIFY:
164     case GDK_LEAVE_NOTIFY:
165         type = WebEvent::MouseMove;
166         break;
167     case GDK_BUTTON_PRESS:
168     case GDK_2BUTTON_PRESS:
169     case GDK_3BUTTON_PRESS: {
170         type = WebEvent::MouseDown;
171         auto modifier = stateModifierForGdkButton(eventButton);
172         state = static_cast<GdkModifierType>(state | modifier);
173         break;
174     }
175     case GDK_BUTTON_RELEASE: {
176         type = WebEvent::MouseUp;
177         auto modifier = stateModifierForGdkButton(eventButton);
178         state = static_cast<GdkModifierType>(state & ~modifier);
179         break;
180     }
181     default :
182         ASSERT_NOT_REACHED();
183     }
184
185     return WebMouseEvent(type,
186         buttonForEvent(event),
187         pressedMouseButtons(state),
188         IntPoint(x, y),
189         IntPoint(xRoot, yRoot),
190         0 /* deltaX */,
191         0 /* deltaY */,
192         0 /* deltaZ */,
193         currentClickCount,
194         modifiersForEvent(event),
195         wallTimeForEvent(event));
196 }
197
198 WebWheelEvent WebEventFactory::createWebWheelEvent(const GdkEvent* event)
199 {
200 #if GTK_CHECK_VERSION(3, 20, 0)
201     WebWheelEvent::Phase phase = gdk_event_is_scroll_stop_event(event) ?
202         WebWheelEvent::Phase::PhaseEnded :
203         WebWheelEvent::Phase::PhaseChanged;
204 #else
205     double deltaX, deltaY;
206     gdk_event_get_scroll_deltas(event, &deltaX, &deltaY);
207     WebWheelEvent::Phase phase = event->scroll.direction == GDK_SCROLL_SMOOTH && !deltaX && !deltaY ?
208         WebWheelEvent::Phase::PhaseEnded :
209         WebWheelEvent::Phase::PhaseChanged;
210 #endif
211
212     return createWebWheelEvent(event, phase, WebWheelEvent::Phase::PhaseNone);
213 }
214
215 WebWheelEvent WebEventFactory::createWebWheelEvent(const GdkEvent* event, WebWheelEvent::Phase phase, WebWheelEvent::Phase momentumPhase)
216 {
217     FloatSize wheelTicks = FloatSize(0, 0);
218     double x, y;
219     gdk_event_get_coords(event, &x, &y);
220     double xRoot, yRoot;
221     gdk_event_get_root_coords(event, &xRoot, &yRoot);
222
223     GdkScrollDirection direction;
224     if (!gdk_event_get_scroll_direction(event, &direction)) {
225         double deltaX, deltaY;
226         if (gdk_event_get_scroll_deltas(event, &deltaX, &deltaY))
227             wheelTicks = FloatSize(-deltaX, -deltaY);
228     }
229
230     if (wheelTicks.isZero()) {
231         switch (direction) {
232         case GDK_SCROLL_UP:
233             wheelTicks = FloatSize(0, 1);
234             break;
235         case GDK_SCROLL_DOWN:
236             wheelTicks = FloatSize(0, -1);
237             break;
238         case GDK_SCROLL_LEFT:
239             wheelTicks = FloatSize(1, 0);
240             break;
241         case GDK_SCROLL_RIGHT:
242             wheelTicks = FloatSize(-1, 0);
243             break;
244         default:
245             ASSERT_NOT_REACHED();
246         }
247     }
248
249     // FIXME: [GTK] Add a setting to change the pixels per line used for scrolling
250     // https://bugs.webkit.org/show_bug.cgi?id=54826
251     float step = static_cast<float>(Scrollbar::pixelsPerLineStep());
252     FloatSize delta(wheelTicks.width() * step, wheelTicks.height() * step);
253
254     return WebWheelEvent(WebEvent::Wheel,
255         IntPoint(x, y),
256         IntPoint(xRoot, yRoot),
257         delta,
258         wheelTicks,
259         phase,
260         momentumPhase,
261         WebWheelEvent::ScrollByPixelWheelEvent,
262         modifiersForEvent(event),
263         wallTimeForEvent(event));
264 }
265
266 WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(const GdkEvent* event, const WebCore::CompositionResults& compositionResults, Vector<String>&& commands)
267 {
268     guint keyval;
269     gdk_event_get_keyval(event, &keyval);
270     guint16 keycode;
271     gdk_event_get_keycode(event, &keycode);
272
273 #if GTK_CHECK_VERSION(3, 10, 0)
274     GdkEventType type = gdk_event_get_event_type(event);
275 #else
276     GdkEventType type = event->type;
277 #endif
278
279     return WebKeyboardEvent(
280         type == GDK_KEY_RELEASE ? WebEvent::KeyUp : WebEvent::KeyDown,
281         compositionResults.simpleString.length() ? compositionResults.simpleString : PlatformKeyboardEvent::singleCharacterString(keyval),
282         PlatformKeyboardEvent::keyValueForGdkKeyCode(keyval),
283         PlatformKeyboardEvent::keyCodeForHardwareKeyCode(keycode),
284         PlatformKeyboardEvent::keyIdentifierForGdkKeyCode(keyval),
285         PlatformKeyboardEvent::windowsKeyCodeForGdkKeyCode(keyval),
286         static_cast<int>(keyval),
287         compositionResults.compositionUpdated(),
288         WTFMove(commands),
289         isGdkKeyCodeFromKeyPad(keyval),
290         modifiersForEvent(event),
291         wallTimeForEvent(event));
292 }
293
294 #if ENABLE(TOUCH_EVENTS)
295 WebTouchEvent WebEventFactory::createWebTouchEvent(const GdkEvent* event, Vector<WebPlatformTouchPoint>&& touchPoints)
296 {
297     WebEvent::Type type = WebEvent::NoType;
298 #if GTK_CHECK_VERSION(3, 10, 0)
299     GdkEventType eventType = gdk_event_get_event_type(event);
300 #else
301     GdkEventType eventType = event->type;
302 #endif
303     switch (eventType) {
304     case GDK_TOUCH_BEGIN:
305         type = WebEvent::TouchStart;
306         break;
307     case GDK_TOUCH_UPDATE:
308         type = WebEvent::TouchMove;
309         break;
310     case GDK_TOUCH_END:
311         type = WebEvent::TouchEnd;
312         break;
313     case GDK_TOUCH_CANCEL:
314         type = WebEvent::TouchCancel;
315         break;
316     default:
317         ASSERT_NOT_REACHED();
318     }
319
320     return WebTouchEvent(type, WTFMove(touchPoints), modifiersForEvent(event), wallTimeForEvent(event));
321 }
322 #endif
323
324 } // namespace WebKit