1 | /* |
2 | * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
3 | * Copyright (C) 2009 Zan Dobersek <zandobersek@gmail.com> |
4 | * Copyright (C) 2009 Holger Hans Peter Freyther |
5 | * Copyright (C) 2010 Igalia S.L. |
6 | * Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * |
12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer in the |
16 | * documentation and/or other materials provided with the distribution. |
17 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
18 | * its contributors may be used to endorse or promote products derived |
19 | * from this software without specific prior written permission. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
22 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
23 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
24 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
25 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
28 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
30 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | #include "config.h" |
34 | #include "EventSenderProxy.h" |
35 | |
36 | #include "PlatformWebView.h" |
37 | #include "TestController.h" |
38 | #include <WebCore/GtkUtilities.h> |
39 | #include <WebCore/NotImplemented.h> |
40 | #include <gdk/gdkkeysyms.h> |
41 | #include <gtk/gtk.h> |
42 | #include <wtf/StdLibExtras.h> |
43 | #include <wtf/glib/GUniquePtr.h> |
44 | #include <wtf/text/WTFString.h> |
45 | |
46 | namespace WTR { |
47 | |
48 | // WebCore and layout tests assume this value |
49 | static const float pixelsPerScrollTick = 40; |
50 | |
51 | // Key event location code defined in DOM Level 3. |
52 | enum KeyLocationCode { |
53 | DOMKeyLocationStandard = 0x00, |
54 | DOMKeyLocationLeft = 0x01, |
55 | DOMKeyLocationRight = 0x02, |
56 | DOMKeyLocationNumpad = 0x03 |
57 | }; |
58 | |
59 | |
60 | struct WTREventQueueItem { |
61 | GdkEvent* event; |
62 | gulong delay; |
63 | |
64 | WTREventQueueItem() |
65 | : event(0) |
66 | , delay(0) |
67 | { |
68 | } |
69 | WTREventQueueItem(GdkEvent* event, gulong delay) |
70 | : event(event) |
71 | , delay(delay) |
72 | { |
73 | } |
74 | }; |
75 | |
76 | EventSenderProxy::EventSenderProxy(TestController* testController) |
77 | : m_testController(testController) |
78 | , m_time(0) |
79 | , m_leftMouseButtonDown(false) |
80 | , m_clickCount(0) |
81 | , m_clickTime(0) |
82 | , m_clickButton(kWKEventMouseButtonNoButton) |
83 | { |
84 | } |
85 | |
86 | EventSenderProxy::~EventSenderProxy() |
87 | { |
88 | } |
89 | |
90 | static unsigned eventSenderButtonToGDKButton(unsigned button) |
91 | { |
92 | int mouseButton = 3; |
93 | if (button <= 2) |
94 | mouseButton = button + 1; |
95 | // fast/events/mouse-click-events expects the 4th button to be treated as the middle button. |
96 | else if (button == 3) |
97 | mouseButton = 2; |
98 | |
99 | return mouseButton; |
100 | } |
101 | |
102 | static guint webkitModifiersToGDKModifiers(WKEventModifiers wkModifiers) |
103 | { |
104 | guint modifiers = 0; |
105 | |
106 | if (wkModifiers & kWKEventModifiersControlKey) |
107 | modifiers |= GDK_CONTROL_MASK; |
108 | if (wkModifiers & kWKEventModifiersShiftKey) |
109 | modifiers |= GDK_SHIFT_MASK; |
110 | if (wkModifiers & kWKEventModifiersAltKey) |
111 | modifiers |= GDK_MOD1_MASK; |
112 | if (wkModifiers & kWKEventModifiersMetaKey) |
113 | modifiers |= GDK_META_MASK; |
114 | if (wkModifiers & kWKEventModifiersCapsLockKey) |
115 | modifiers |= GDK_LOCK_MASK; |
116 | |
117 | return modifiers; |
118 | } |
119 | |
120 | GdkEvent* EventSenderProxy::createMouseButtonEvent(GdkEventType eventType, unsigned button, WKEventModifiers modifiers) |
121 | { |
122 | GdkEvent* mouseEvent = gdk_event_new(eventType); |
123 | |
124 | mouseEvent->button.button = button; |
125 | mouseEvent->button.x = m_position.x; |
126 | mouseEvent->button.y = m_position.y; |
127 | mouseEvent->button.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())); |
128 | g_object_ref(mouseEvent->button.window); |
129 | gdk_event_set_device(mouseEvent, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(mouseEvent->button.window)))); |
130 | mouseEvent->button.state = webkitModifiersToGDKModifiers(modifiers) | m_mouseButtonsCurrentlyDown; |
131 | mouseEvent->button.time = GDK_CURRENT_TIME; |
132 | mouseEvent->button.axes = 0; |
133 | |
134 | int xRoot, yRoot; |
135 | gdk_window_get_root_coords(mouseEvent->button.window, m_position.x, m_position.y, &xRoot, &yRoot); |
136 | mouseEvent->button.x_root = xRoot; |
137 | mouseEvent->button.y_root = yRoot; |
138 | |
139 | return mouseEvent; |
140 | } |
141 | |
142 | void EventSenderProxy::updateClickCountForButton(int button) |
143 | { |
144 | if (m_time - m_clickTime < 1 && m_position == m_clickPosition && button == m_clickButton) { |
145 | ++m_clickCount; |
146 | m_clickTime = m_time; |
147 | return; |
148 | } |
149 | |
150 | m_clickCount = 1; |
151 | m_clickTime = m_time; |
152 | m_clickPosition = m_position; |
153 | m_clickButton = button; |
154 | } |
155 | |
156 | void EventSenderProxy::dispatchEvent(GdkEvent* event) |
157 | { |
158 | ASSERT(m_testController->mainWebView()); |
159 | gtk_main_do_event(event); |
160 | gdk_event_free(event); |
161 | } |
162 | |
163 | void EventSenderProxy::replaySavedEvents() |
164 | { |
165 | while (!m_eventQueue.isEmpty()) { |
166 | WTREventQueueItem item = m_eventQueue.takeFirst(); |
167 | if (item.delay) |
168 | g_usleep(item.delay * 1000); |
169 | |
170 | dispatchEvent(item.event); |
171 | } |
172 | } |
173 | |
174 | void EventSenderProxy::sendOrQueueEvent(GdkEvent* event) |
175 | { |
176 | if (m_eventQueue.isEmpty() || !m_eventQueue.last().delay) { |
177 | dispatchEvent(event); |
178 | return; |
179 | } |
180 | |
181 | m_eventQueue.last().event = event; |
182 | replaySavedEvents(); |
183 | } |
184 | |
185 | int getGDKKeySymForKeyRef(WKStringRef keyRef, unsigned location, guint* modifiers) |
186 | { |
187 | if (location == DOMKeyLocationNumpad) { |
188 | if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow" )) |
189 | return GDK_KEY_KP_Left; |
190 | if (WKStringIsEqualToUTF8CString(keyRef, "rightArror" )) |
191 | return GDK_KEY_KP_Right; |
192 | if (WKStringIsEqualToUTF8CString(keyRef, "upArrow" )) |
193 | return GDK_KEY_KP_Up; |
194 | if (WKStringIsEqualToUTF8CString(keyRef, "downArrow" )) |
195 | return GDK_KEY_KP_Down; |
196 | if (WKStringIsEqualToUTF8CString(keyRef, "pageUp" )) |
197 | return GDK_KEY_KP_Page_Up; |
198 | if (WKStringIsEqualToUTF8CString(keyRef, "pageDown" )) |
199 | return GDK_KEY_KP_Page_Down; |
200 | if (WKStringIsEqualToUTF8CString(keyRef, "home" )) |
201 | return GDK_KEY_KP_Home; |
202 | if (WKStringIsEqualToUTF8CString(keyRef, "end" )) |
203 | return GDK_KEY_KP_End; |
204 | if (WKStringIsEqualToUTF8CString(keyRef, "insert" )) |
205 | return GDK_KEY_KP_Insert; |
206 | if (WKStringIsEqualToUTF8CString(keyRef, "delete" )) |
207 | return GDK_KEY_KP_Delete; |
208 | |
209 | return GDK_KEY_VoidSymbol; |
210 | } |
211 | |
212 | if (WKStringIsEqualToUTF8CString(keyRef, "leftControl" )) |
213 | return GDK_KEY_Control_L; |
214 | if (WKStringIsEqualToUTF8CString(keyRef, "rightControl" )) |
215 | return GDK_KEY_Control_R; |
216 | if (WKStringIsEqualToUTF8CString(keyRef, "leftShift" )) |
217 | return GDK_KEY_Shift_L; |
218 | if (WKStringIsEqualToUTF8CString(keyRef, "rightShift" )) |
219 | return GDK_KEY_Shift_R; |
220 | if (WKStringIsEqualToUTF8CString(keyRef, "leftAlt" )) |
221 | return GDK_KEY_Alt_L; |
222 | if (WKStringIsEqualToUTF8CString(keyRef, "rightAlt" )) |
223 | return GDK_KEY_Alt_R; |
224 | if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow" )) |
225 | return GDK_KEY_Left; |
226 | if (WKStringIsEqualToUTF8CString(keyRef, "rightArrow" )) |
227 | return GDK_KEY_Right; |
228 | if (WKStringIsEqualToUTF8CString(keyRef, "upArrow" )) |
229 | return GDK_KEY_Up; |
230 | if (WKStringIsEqualToUTF8CString(keyRef, "downArrow" )) |
231 | return GDK_KEY_Down; |
232 | if (WKStringIsEqualToUTF8CString(keyRef, "pageUp" )) |
233 | return GDK_KEY_Page_Up; |
234 | if (WKStringIsEqualToUTF8CString(keyRef, "pageDown" )) |
235 | return GDK_KEY_Page_Down; |
236 | if (WKStringIsEqualToUTF8CString(keyRef, "home" )) |
237 | return GDK_KEY_Home; |
238 | if (WKStringIsEqualToUTF8CString(keyRef, "end" )) |
239 | return GDK_KEY_End; |
240 | if (WKStringIsEqualToUTF8CString(keyRef, "insert" )) |
241 | return GDK_KEY_Insert; |
242 | if (WKStringIsEqualToUTF8CString(keyRef, "delete" )) |
243 | return GDK_KEY_Delete; |
244 | if (WKStringIsEqualToUTF8CString(keyRef, "printScreen" )) |
245 | return GDK_KEY_Print; |
246 | if (WKStringIsEqualToUTF8CString(keyRef, "menu" )) |
247 | return GDK_KEY_Menu; |
248 | if (WKStringIsEqualToUTF8CString(keyRef, "F1" )) |
249 | return GDK_KEY_F1; |
250 | if (WKStringIsEqualToUTF8CString(keyRef, "F2" )) |
251 | return GDK_KEY_F2; |
252 | if (WKStringIsEqualToUTF8CString(keyRef, "F3" )) |
253 | return GDK_KEY_F3; |
254 | if (WKStringIsEqualToUTF8CString(keyRef, "F4" )) |
255 | return GDK_KEY_F4; |
256 | if (WKStringIsEqualToUTF8CString(keyRef, "F5" )) |
257 | return GDK_KEY_F5; |
258 | if (WKStringIsEqualToUTF8CString(keyRef, "F6" )) |
259 | return GDK_KEY_F6; |
260 | if (WKStringIsEqualToUTF8CString(keyRef, "F7" )) |
261 | return GDK_KEY_F7; |
262 | if (WKStringIsEqualToUTF8CString(keyRef, "F8" )) |
263 | return GDK_KEY_F8; |
264 | if (WKStringIsEqualToUTF8CString(keyRef, "F9" )) |
265 | return GDK_KEY_F9; |
266 | if (WKStringIsEqualToUTF8CString(keyRef, "F10" )) |
267 | return GDK_KEY_F10; |
268 | if (WKStringIsEqualToUTF8CString(keyRef, "F11" )) |
269 | return GDK_KEY_F11; |
270 | if (WKStringIsEqualToUTF8CString(keyRef, "F12" )) |
271 | return GDK_KEY_F12; |
272 | |
273 | size_t bufferSize = WKStringGetMaximumUTF8CStringSize(keyRef); |
274 | auto buffer = std::make_unique<char[]>(bufferSize); |
275 | WKStringGetUTF8CString(keyRef, buffer.get(), bufferSize); |
276 | char charCode = buffer.get()[0]; |
277 | |
278 | if (charCode == '\n' || charCode == '\r') |
279 | return GDK_KEY_Return; |
280 | if (charCode == '\t') |
281 | return GDK_KEY_Tab; |
282 | if (charCode == '\x8') |
283 | return GDK_KEY_BackSpace; |
284 | if (charCode == 0x001B) |
285 | return GDK_KEY_Escape; |
286 | |
287 | if (WTF::isASCIIUpper(charCode)) |
288 | *modifiers |= GDK_SHIFT_MASK; |
289 | |
290 | return gdk_unicode_to_keyval(static_cast<guint32>(buffer.get()[0])); |
291 | } |
292 | |
293 | void EventSenderProxy::keyDown(WKStringRef keyRef, WKEventModifiers wkModifiers, unsigned location) |
294 | { |
295 | guint modifiers = webkitModifiersToGDKModifiers(wkModifiers); |
296 | int gdkKeySym = getGDKKeySymForKeyRef(keyRef, location, &modifiers); |
297 | |
298 | GdkEvent* pressEvent = gdk_event_new(GDK_KEY_PRESS); |
299 | pressEvent->key.keyval = gdkKeySym; |
300 | pressEvent->key.state = modifiers; |
301 | pressEvent->key.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformWindow())); |
302 | g_object_ref(pressEvent->key.window); |
303 | gdk_event_set_device(pressEvent, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(pressEvent->key.window)))); |
304 | |
305 | GUniqueOutPtr<GdkKeymapKey> keys; |
306 | gint nKeys; |
307 | if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys.outPtr(), &nKeys) && nKeys) |
308 | pressEvent->key.hardware_keycode = keys.get()[0].keycode; |
309 | |
310 | GdkEvent* releaseEvent = gdk_event_copy(pressEvent); |
311 | dispatchEvent(pressEvent); |
312 | releaseEvent->key.type = GDK_KEY_RELEASE; |
313 | dispatchEvent(releaseEvent); |
314 | } |
315 | |
316 | void EventSenderProxy::mouseDown(unsigned button, WKEventModifiers wkModifiers) |
317 | { |
318 | unsigned gdkButton = eventSenderButtonToGDKButton(button); |
319 | auto modifier = WebCore::stateModifierForGdkButton(gdkButton); |
320 | |
321 | // If the same mouse button is already in the down position don't |
322 | // send another event as it may confuse Xvfb. |
323 | if (m_mouseButtonsCurrentlyDown & modifier) |
324 | return; |
325 | |
326 | // Normally GDK will send both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS for |
327 | // the second button press during double-clicks. WebKit GTK+ selectively |
328 | // ignores the first GDK_BUTTON_PRESS of that pair using gdk_event_peek. |
329 | // Since our events aren't ever going onto the GDK event queue, WebKit won't |
330 | // be able to filter out the first GDK_BUTTON_PRESS, so we just don't send |
331 | // it here. Eventually this code should probably figure out a way to get all |
332 | // appropriate events onto the event queue and this work-around should be |
333 | // removed. |
334 | updateClickCountForButton(button); |
335 | |
336 | GdkEventType eventType; |
337 | if (m_clickCount == 2) |
338 | eventType = GDK_2BUTTON_PRESS; |
339 | else if (m_clickCount == 3) |
340 | eventType = GDK_3BUTTON_PRESS; |
341 | else |
342 | eventType = GDK_BUTTON_PRESS; |
343 | |
344 | GdkEvent* event = createMouseButtonEvent(eventType, gdkButton, wkModifiers); |
345 | m_mouseButtonsCurrentlyDown |= modifier; |
346 | sendOrQueueEvent(event); |
347 | } |
348 | |
349 | void EventSenderProxy::mouseUp(unsigned button, WKEventModifiers wkModifiers) |
350 | { |
351 | m_clickButton = kWKEventMouseButtonNoButton; |
352 | unsigned gdkButton = eventSenderButtonToGDKButton(button); |
353 | GdkEvent* event = createMouseButtonEvent(GDK_BUTTON_RELEASE, gdkButton, wkModifiers); |
354 | auto modifier = WebCore::stateModifierForGdkButton(gdkButton); |
355 | m_mouseButtonsCurrentlyDown &= ~modifier; |
356 | sendOrQueueEvent(event); |
357 | |
358 | m_clickPosition = m_position; |
359 | m_clickTime = GDK_CURRENT_TIME; |
360 | } |
361 | |
362 | void EventSenderProxy::mouseMoveTo(double x, double y) |
363 | { |
364 | m_position.x = x; |
365 | m_position.y = y; |
366 | |
367 | GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY); |
368 | event->motion.x = m_position.x; |
369 | event->motion.y = m_position.y; |
370 | |
371 | event->motion.time = GDK_CURRENT_TIME; |
372 | event->motion.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())); |
373 | g_object_ref(event->motion.window); |
374 | gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->motion.window)))); |
375 | event->motion.state = m_mouseButtonsCurrentlyDown; |
376 | event->motion.axes = 0; |
377 | |
378 | int xRoot, yRoot; |
379 | gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())), m_position.x, m_position.y , &xRoot, &yRoot); |
380 | event->motion.x_root = xRoot; |
381 | event->motion.y_root = yRoot; |
382 | |
383 | sendOrQueueEvent(event); |
384 | } |
385 | |
386 | void EventSenderProxy::mouseScrollBy(int horizontal, int vertical) |
387 | { |
388 | // Copy behaviour of Qt and EFL - just return in case of (0,0) mouse scroll |
389 | if (!horizontal && !vertical) |
390 | return; |
391 | |
392 | GdkEvent* event = gdk_event_new(GDK_SCROLL); |
393 | event->scroll.x = m_position.x; |
394 | event->scroll.y = m_position.y; |
395 | event->scroll.time = GDK_CURRENT_TIME; |
396 | event->scroll.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())); |
397 | g_object_ref(event->scroll.window); |
398 | gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->scroll.window)))); |
399 | |
400 | // For more than one tick in a scroll, we need smooth scroll event |
401 | if ((horizontal && vertical) || horizontal > 1 || horizontal < -1 || vertical > 1 || vertical < -1) { |
402 | event->scroll.direction = GDK_SCROLL_SMOOTH; |
403 | event->scroll.delta_x = -horizontal; |
404 | event->scroll.delta_y = -vertical; |
405 | |
406 | sendOrQueueEvent(event); |
407 | return; |
408 | } |
409 | |
410 | if (horizontal < 0) |
411 | event->scroll.direction = GDK_SCROLL_RIGHT; |
412 | else if (horizontal > 0) |
413 | event->scroll.direction = GDK_SCROLL_LEFT; |
414 | else if (vertical < 0) |
415 | event->scroll.direction = GDK_SCROLL_DOWN; |
416 | else if (vertical > 0) |
417 | event->scroll.direction = GDK_SCROLL_UP; |
418 | else |
419 | g_assert_not_reached(); |
420 | |
421 | sendOrQueueEvent(event); |
422 | } |
423 | |
424 | void EventSenderProxy::continuousMouseScrollBy(int horizontal, int vertical, bool paged) |
425 | { |
426 | // Gtk+ does not support paged scroll events. |
427 | if (paged) { |
428 | WTFLogAlways("EventSenderProxy::continuousMouseScrollBy not implemented for paged scroll events" ); |
429 | return; |
430 | } |
431 | |
432 | GdkEvent* event = gdk_event_new(GDK_SCROLL); |
433 | event->scroll.x = m_position.x; |
434 | event->scroll.y = m_position.y; |
435 | event->scroll.time = GDK_CURRENT_TIME; |
436 | event->scroll.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())); |
437 | g_object_ref(event->scroll.window); |
438 | gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->scroll.window)))); |
439 | |
440 | event->scroll.direction = GDK_SCROLL_SMOOTH; |
441 | event->scroll.delta_x = -horizontal / pixelsPerScrollTick; |
442 | event->scroll.delta_y = -vertical / pixelsPerScrollTick; |
443 | |
444 | sendOrQueueEvent(event); |
445 | } |
446 | |
447 | void EventSenderProxy::mouseScrollByWithWheelAndMomentumPhases(int x, int y, int /*phase*/, int /*momentum*/) |
448 | { |
449 | // Gtk+ does not have the concept of wheel gesture phases or momentum. Just relay to |
450 | // the mouse wheel handler. |
451 | mouseScrollBy(x, y); |
452 | } |
453 | |
454 | void EventSenderProxy::leapForward(int milliseconds) |
455 | { |
456 | if (m_eventQueue.isEmpty()) |
457 | m_eventQueue.append(WTREventQueueItem()); |
458 | |
459 | m_eventQueue.last().delay = milliseconds; |
460 | m_time += milliseconds / 1000.0; |
461 | } |
462 | |
463 | void updateEventCoordinates(GdkEvent* touchEvent, int x, int y) |
464 | { |
465 | touchEvent->touch.x = x; |
466 | touchEvent->touch.y = y; |
467 | |
468 | int xRoot, yRoot; |
469 | gdk_window_get_root_coords(touchEvent->touch.window, x, y, &xRoot, &yRoot); |
470 | touchEvent->touch.x_root = xRoot; |
471 | touchEvent->touch.y_root = yRoot; |
472 | } |
473 | |
474 | GUniquePtr<GdkEvent> EventSenderProxy::createTouchEvent(GdkEventType eventType, int id) |
475 | { |
476 | GUniquePtr<GdkEvent> touchEvent(gdk_event_new(eventType)); |
477 | |
478 | touchEvent->touch.sequence = static_cast<GdkEventSequence*>(GINT_TO_POINTER(id)); |
479 | touchEvent->touch.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())); |
480 | g_object_ref(touchEvent->touch.window); |
481 | gdk_event_set_device(touchEvent.get(), gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(touchEvent->button.window)))); |
482 | touchEvent->touch.time = GDK_CURRENT_TIME; |
483 | |
484 | return touchEvent; |
485 | } |
486 | |
487 | #if ENABLE(TOUCH_EVENTS) |
488 | void EventSenderProxy::addTouchPoint(int x, int y) |
489 | { |
490 | // Touch ID is array index plus one, so 0 is skipped. |
491 | GUniquePtr<GdkEvent> event = createTouchEvent(static_cast<GdkEventType>(GDK_TOUCH_BEGIN), m_touchEvents.size() + 1); |
492 | updateEventCoordinates(event.get(), x, y); |
493 | m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence)); |
494 | m_touchEvents.append(std::move(event)); |
495 | } |
496 | |
497 | void EventSenderProxy::updateTouchPoint(int index, int x, int y) |
498 | { |
499 | ASSERT(index >= 0 && static_cast<size_t>(index) < m_touchEvents.size()); |
500 | |
501 | const auto& event = m_touchEvents[index]; |
502 | ASSERT(event); |
503 | |
504 | event->type = GDK_TOUCH_UPDATE; |
505 | updateEventCoordinates(event.get(), x, y); |
506 | m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence)); |
507 | } |
508 | |
509 | void EventSenderProxy::sendUpdatedTouchEvents() |
510 | { |
511 | for (auto id : m_updatedTouchEvents) |
512 | sendOrQueueEvent(gdk_event_copy(m_touchEvents[id - 1].get())); |
513 | |
514 | m_updatedTouchEvents.clear(); |
515 | } |
516 | |
517 | void EventSenderProxy::touchStart() |
518 | { |
519 | sendUpdatedTouchEvents(); |
520 | } |
521 | |
522 | void EventSenderProxy::touchMove() |
523 | { |
524 | sendUpdatedTouchEvents(); |
525 | } |
526 | |
527 | void EventSenderProxy::touchEnd() |
528 | { |
529 | sendUpdatedTouchEvents(); |
530 | } |
531 | |
532 | void EventSenderProxy::touchCancel() |
533 | { |
534 | notImplemented(); |
535 | } |
536 | |
537 | void EventSenderProxy::clearTouchPoints() |
538 | { |
539 | m_updatedTouchEvents.clear(); |
540 | m_touchEvents.clear(); |
541 | } |
542 | |
543 | void EventSenderProxy::releaseTouchPoint(int index) |
544 | { |
545 | ASSERT(index >= 0 && static_cast<size_t>(index) < m_touchEvents.size()); |
546 | |
547 | const auto& event = m_touchEvents[index]; |
548 | event->type = GDK_TOUCH_END; |
549 | m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence)); |
550 | } |
551 | |
552 | void EventSenderProxy::cancelTouchPoint(int index) |
553 | { |
554 | notImplemented(); |
555 | } |
556 | |
557 | void EventSenderProxy::setTouchPointRadius(int radiusX, int radiusY) |
558 | { |
559 | notImplemented(); |
560 | } |
561 | |
562 | void EventSenderProxy::setTouchModifier(WKEventModifiers modifier, bool enable) |
563 | { |
564 | guint state = webkitModifiersToGDKModifiers(modifier); |
565 | |
566 | for (const auto& event : m_touchEvents) { |
567 | if (event->type == GDK_TOUCH_END) |
568 | continue; |
569 | |
570 | if (enable) |
571 | event->touch.state |= state; |
572 | else |
573 | event->touch.state &= ~(state); |
574 | |
575 | m_updatedTouchEvents.add(GPOINTER_TO_INT(event->touch.sequence)); |
576 | } |
577 | } |
578 | #endif // ENABLE(TOUCH_EVENTS) |
579 | |
580 | } // namespace WTR |
581 | |