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/GtkVersioning.h> |
33 | #include <WebCore/PlatformKeyboardEvent.h> |
34 | #include <WebCore/Scrollbar.h> |
35 | #include <WebCore/WindowsKeyboardCodes.h> |
36 | #include <gdk/gdk.h> |
37 | #include <gdk/gdkkeysyms.h> |
38 | #include <gtk/gtk.h> |
39 | #include <wtf/ASCIICType.h> |
40 | |
41 | namespace WebKit { |
42 | |
43 | using namespace WebCore; |
44 | |
45 | static inline bool isGdkKeyCodeFromKeyPad(unsigned keyval) |
46 | { |
47 | return keyval >= GDK_KP_Space && keyval <= GDK_KP_9; |
48 | } |
49 | |
50 | static inline OptionSet<WebEvent::Modifier> modifiersForEvent(const GdkEvent* event) |
51 | { |
52 | OptionSet<WebEvent::Modifier> modifiers; |
53 | GdkModifierType state; |
54 | |
55 | // Check for a valid state in GdkEvent. |
56 | if (!gdk_event_get_state(event, &state)) |
57 | return modifiers; |
58 | |
59 | if (state & GDK_CONTROL_MASK) |
60 | modifiers.add(WebEvent::Modifier::ControlKey); |
61 | if (state & GDK_SHIFT_MASK) |
62 | modifiers.add(WebEvent::Modifier::ShiftKey); |
63 | if (state & GDK_MOD1_MASK) |
64 | modifiers.add(WebEvent::Modifier::AltKey); |
65 | if (state & GDK_META_MASK) |
66 | modifiers.add(WebEvent::Modifier::MetaKey); |
67 | if (PlatformKeyboardEvent::modifiersContainCapsLock(state)) |
68 | modifiers.add(WebEvent::Modifier::CapsLockKey); |
69 | |
70 | return modifiers; |
71 | } |
72 | |
73 | static inline WebMouseEvent::Button buttonForEvent(const GdkEvent* event) |
74 | { |
75 | unsigned button = 0; |
76 | |
77 | switch (event->type) { |
78 | case GDK_ENTER_NOTIFY: |
79 | case GDK_LEAVE_NOTIFY: |
80 | case GDK_MOTION_NOTIFY: { |
81 | button = WebMouseEvent::NoButton; |
82 | GdkModifierType state; |
83 | gdk_event_get_state(event, &state); |
84 | if (state & GDK_BUTTON1_MASK) |
85 | button = WebMouseEvent::LeftButton; |
86 | else if (state & GDK_BUTTON2_MASK) |
87 | button = WebMouseEvent::MiddleButton; |
88 | else if (state & GDK_BUTTON3_MASK) |
89 | button = WebMouseEvent::RightButton; |
90 | break; |
91 | } |
92 | case GDK_BUTTON_PRESS: |
93 | case GDK_2BUTTON_PRESS: |
94 | case GDK_3BUTTON_PRESS: |
95 | case GDK_BUTTON_RELEASE: |
96 | if (event->button.button == 1) |
97 | button = WebMouseEvent::LeftButton; |
98 | else if (event->button.button == 2) |
99 | button = WebMouseEvent::MiddleButton; |
100 | else if (event->button.button == 3) |
101 | button = WebMouseEvent::RightButton; |
102 | break; |
103 | default: |
104 | ASSERT_NOT_REACHED(); |
105 | } |
106 | |
107 | return static_cast<WebMouseEvent::Button>(button); |
108 | } |
109 | |
110 | static inline short pressedMouseButtons(GdkModifierType state) |
111 | { |
112 | // MouseEvent.buttons |
113 | // https://www.w3.org/TR/uievents/#ref-for-dom-mouseevent-buttons-1 |
114 | |
115 | // 0 MUST indicate no button is currently active. |
116 | short buttons = 0; |
117 | |
118 | // 1 MUST indicate the primary button of the device (in general, the left button or the only button on |
119 | // single-button devices, used to activate a user interface control or select text). |
120 | if (state & GDK_BUTTON1_MASK) |
121 | buttons |= 1; |
122 | |
123 | // 4 MUST indicate the auxiliary button (in general, the middle button, often combined with a mouse wheel). |
124 | if (state & GDK_BUTTON2_MASK) |
125 | buttons |= 4; |
126 | |
127 | // 2 MUST indicate the secondary button (in general, the right button, often used to display a context menu), |
128 | // if present. |
129 | if (state & GDK_BUTTON3_MASK) |
130 | buttons |= 2; |
131 | |
132 | return buttons; |
133 | } |
134 | |
135 | WebMouseEvent WebEventFactory::createWebMouseEvent(const GdkEvent* event, int currentClickCount) |
136 | { |
137 | double x, y, xRoot, yRoot; |
138 | gdk_event_get_coords(event, &x, &y); |
139 | gdk_event_get_root_coords(event, &xRoot, &yRoot); |
140 | |
141 | GdkModifierType state = static_cast<GdkModifierType>(0); |
142 | gdk_event_get_state(event, &state); |
143 | |
144 | WebEvent::Type type = static_cast<WebEvent::Type>(0); |
145 | switch (event->type) { |
146 | case GDK_MOTION_NOTIFY: |
147 | case GDK_ENTER_NOTIFY: |
148 | case GDK_LEAVE_NOTIFY: |
149 | type = WebEvent::MouseMove; |
150 | break; |
151 | case GDK_BUTTON_PRESS: |
152 | case GDK_2BUTTON_PRESS: |
153 | case GDK_3BUTTON_PRESS: { |
154 | type = WebEvent::MouseDown; |
155 | auto modifier = stateModifierForGdkButton(event->button.button); |
156 | state = static_cast<GdkModifierType>(state | modifier); |
157 | break; |
158 | } |
159 | case GDK_BUTTON_RELEASE: { |
160 | type = WebEvent::MouseUp; |
161 | auto modifier = stateModifierForGdkButton(event->button.button); |
162 | state = static_cast<GdkModifierType>(state & ~modifier); |
163 | break; |
164 | } |
165 | default : |
166 | ASSERT_NOT_REACHED(); |
167 | } |
168 | |
169 | return WebMouseEvent(type, |
170 | buttonForEvent(event), |
171 | pressedMouseButtons(state), |
172 | IntPoint(x, y), |
173 | IntPoint(xRoot, yRoot), |
174 | 0 /* deltaX */, |
175 | 0 /* deltaY */, |
176 | 0 /* deltaZ */, |
177 | currentClickCount, |
178 | modifiersForEvent(event), |
179 | wallTimeForEvent(event)); |
180 | } |
181 | |
182 | WebWheelEvent WebEventFactory::createWebWheelEvent(const GdkEvent* event) |
183 | { |
184 | #ifndef GTK_API_VERSION_2 |
185 | #if GTK_CHECK_VERSION(3, 20, 0) |
186 | WebWheelEvent::Phase phase = gdk_event_is_scroll_stop_event(event) ? |
187 | WebWheelEvent::Phase::PhaseEnded : |
188 | WebWheelEvent::Phase::PhaseChanged; |
189 | #else |
190 | double deltaX, deltaY; |
191 | gdk_event_get_scroll_deltas(event, &deltaX, &deltaY); |
192 | WebWheelEvent::Phase phase = event->scroll.direction == GDK_SCROLL_SMOOTH && !deltaX && !deltaY ? |
193 | WebWheelEvent::Phase::PhaseEnded : |
194 | WebWheelEvent::Phase::PhaseChanged; |
195 | #endif |
196 | #else |
197 | WebWheelEvent::Phase phase = WebWheelEvent::Phase::PhaseChanged; |
198 | #endif // GTK_API_VERSION_2 |
199 | |
200 | return createWebWheelEvent(event, phase, WebWheelEvent::Phase::PhaseNone); |
201 | } |
202 | |
203 | WebWheelEvent WebEventFactory::createWebWheelEvent(const GdkEvent* event, WebWheelEvent::Phase phase, WebWheelEvent::Phase momentumPhase) |
204 | { |
205 | double x, y, xRoot, yRoot; |
206 | gdk_event_get_coords(event, &x, &y); |
207 | gdk_event_get_root_coords(event, &xRoot, &yRoot); |
208 | |
209 | FloatSize wheelTicks; |
210 | switch (event->scroll.direction) { |
211 | case GDK_SCROLL_UP: |
212 | wheelTicks = FloatSize(0, 1); |
213 | break; |
214 | case GDK_SCROLL_DOWN: |
215 | wheelTicks = FloatSize(0, -1); |
216 | break; |
217 | case GDK_SCROLL_LEFT: |
218 | wheelTicks = FloatSize(1, 0); |
219 | break; |
220 | case GDK_SCROLL_RIGHT: |
221 | wheelTicks = FloatSize(-1, 0); |
222 | break; |
223 | #if GTK_CHECK_VERSION(3, 3, 18) |
224 | case GDK_SCROLL_SMOOTH: { |
225 | double deltaX, deltaY; |
226 | gdk_event_get_scroll_deltas(event, &deltaX, &deltaY); |
227 | wheelTicks = FloatSize(-deltaX, -deltaY); |
228 | } |
229 | break; |
230 | #endif |
231 | default: |
232 | ASSERT_NOT_REACHED(); |
233 | } |
234 | |
235 | // FIXME: [GTK] Add a setting to change the pixels per line used for scrolling |
236 | // https://bugs.webkit.org/show_bug.cgi?id=54826 |
237 | float step = static_cast<float>(Scrollbar::pixelsPerLineStep()); |
238 | FloatSize delta(wheelTicks.width() * step, wheelTicks.height() * step); |
239 | |
240 | return WebWheelEvent(WebEvent::Wheel, |
241 | IntPoint(x, y), |
242 | IntPoint(xRoot, yRoot), |
243 | delta, |
244 | wheelTicks, |
245 | phase, |
246 | momentumPhase, |
247 | WebWheelEvent::ScrollByPixelWheelEvent, |
248 | modifiersForEvent(event), |
249 | wallTimeForEvent(event)); |
250 | } |
251 | |
252 | WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(const GdkEvent* event, const WebCore::CompositionResults& compositionResults, Vector<String>&& commands) |
253 | { |
254 | return WebKeyboardEvent( |
255 | event->type == GDK_KEY_RELEASE ? WebEvent::KeyUp : WebEvent::KeyDown, |
256 | compositionResults.simpleString.length() ? compositionResults.simpleString : PlatformKeyboardEvent::singleCharacterString(event->key.keyval), |
257 | PlatformKeyboardEvent::keyValueForGdkKeyCode(event->key.keyval), |
258 | PlatformKeyboardEvent::keyCodeForHardwareKeyCode(event->key.hardware_keycode), |
259 | PlatformKeyboardEvent::keyIdentifierForGdkKeyCode(event->key.keyval), |
260 | PlatformKeyboardEvent::windowsKeyCodeForGdkKeyCode(event->key.keyval), |
261 | static_cast<int>(event->key.keyval), |
262 | compositionResults.compositionUpdated(), |
263 | WTFMove(commands), |
264 | isGdkKeyCodeFromKeyPad(event->key.keyval), |
265 | modifiersForEvent(event), |
266 | wallTimeForEvent(event)); |
267 | } |
268 | |
269 | #if ENABLE(TOUCH_EVENTS) |
270 | WebTouchEvent WebEventFactory::createWebTouchEvent(const GdkEvent* event, Vector<WebPlatformTouchPoint>&& touchPoints) |
271 | { |
272 | #ifndef GTK_API_VERSION_2 |
273 | WebEvent::Type type = WebEvent::NoType; |
274 | switch (event->type) { |
275 | case GDK_TOUCH_BEGIN: |
276 | type = WebEvent::TouchStart; |
277 | break; |
278 | case GDK_TOUCH_UPDATE: |
279 | type = WebEvent::TouchMove; |
280 | break; |
281 | case GDK_TOUCH_END: |
282 | type = WebEvent::TouchEnd; |
283 | break; |
284 | case GDK_TOUCH_CANCEL: |
285 | type = WebEvent::TouchCancel; |
286 | break; |
287 | default: |
288 | ASSERT_NOT_REACHED(); |
289 | } |
290 | |
291 | return WebTouchEvent(type, WTFMove(touchPoints), modifiersForEvent(event), wallTimeForEvent(event)); |
292 | #else |
293 | return WebTouchEvent(); |
294 | #endif // GTK_API_VERSION_2 |
295 | } |
296 | #endif |
297 | |
298 | } // namespace WebKit |
299 | |