1 /*
2 * Copyright © 2011 Collabra Ltd.
3 * Copyright © 2011 Red Hat, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Author: Daniel Stone <daniel@fooishbar.org>
25 */
26
27 #ifdef HAVE_DIX_CONFIG_H
28 #include <dix-config.h>
29 #endif
30
31 #include "inputstr.h"
32 #include "scrnintstr.h"
33 #include "dixgrabs.h"
34
35 #include "eventstr.h"
36 #include "exevents.h"
37 #include "exglobals.h"
38 #include "inpututils.h"
39 #include "eventconvert.h"
40 #include "windowstr.h"
41 #include "mi.h"
42
43 #define TOUCH_HISTORY_SIZE 100
44
45 /**
46 * Some documentation about touch points:
47 * The driver submits touch events with it's own (unique) touch point ID.
48 * The driver may re-use those IDs, the DDX doesn't care. It just passes on
49 * the data to the DIX. In the server, the driver's ID is referred to as the
50 * DDX id anyway.
51 *
52 * On a TouchBegin, we create a DDXTouchPointInfo that contains the DDX id
53 * and the client ID that this touchpoint will have. The client ID is the
54 * one visible on the protocol.
55 *
56 * TouchUpdate and TouchEnd will only be processed if there is an active
57 * touchpoint with the same DDX id.
58 *
59 * The DDXTouchPointInfo struct is stored dev->last.touches. When the event
60 * being processed, it becomes a TouchPointInfo in dev->touch-touches which
61 * contains amongst other things the sprite trace and delivery information.
62 */
63
64 /**
65 * Check which devices need a bigger touch event queue and grow their
66 * last.touches by half it's current size.
67 *
68 * @param client Always the serverClient
69 * @param closure Always NULL
70 *
71 * @return Always True. If we fail to grow we probably will topple over soon
72 * anyway and re-executing this won't help.
73 */
74
75 static Bool
TouchResizeQueue(DeviceIntPtr dev)76 TouchResizeQueue(DeviceIntPtr dev)
77 {
78 DDXTouchPointInfoPtr tmp;
79 size_t size;
80
81 /* Grow sufficiently so we don't need to do it often */
82 size = dev->last.num_touches + dev->last.num_touches / 2 + 1;
83
84 tmp = reallocarray(dev->last.touches, size, sizeof(*dev->last.touches));
85 if (tmp) {
86 int j;
87
88 dev->last.touches = tmp;
89 for (j = dev->last.num_touches; j < size; j++)
90 TouchInitDDXTouchPoint(dev, &dev->last.touches[j]);
91 dev->last.num_touches = size;
92 return TRUE;
93 }
94 return FALSE;
95 }
96
97 /**
98 * Given the DDX-facing ID (which is _not_ DeviceEvent::detail.touch), find the
99 * associated DDXTouchPointInfoRec.
100 *
101 * @param dev The device to create the touch point for
102 * @param ddx_id Touch id assigned by the driver/ddx
103 * @param create Create the touchpoint if it cannot be found
104 */
105 DDXTouchPointInfoPtr
TouchFindByDDXID(DeviceIntPtr dev,uint32_t ddx_id,Bool create)106 TouchFindByDDXID(DeviceIntPtr dev, uint32_t ddx_id, Bool create)
107 {
108 DDXTouchPointInfoPtr ti;
109 int i;
110
111 if (!dev->touch)
112 return NULL;
113
114 for (i = 0; i < dev->last.num_touches; i++) {
115 ti = &dev->last.touches[i];
116 if (ti->active && ti->ddx_id == ddx_id)
117 return ti;
118 }
119
120 return create ? TouchBeginDDXTouch(dev, ddx_id) : NULL;
121 }
122
123 /**
124 * Given a unique DDX ID for a touchpoint, create a touchpoint record and
125 * return it.
126 *
127 * If no other touch points are active, mark new touchpoint for pointer
128 * emulation.
129 *
130 * Returns NULL on failure (i.e. if another touch with that ID is already active,
131 * allocation failure).
132 */
133 DDXTouchPointInfoPtr
TouchBeginDDXTouch(DeviceIntPtr dev,uint32_t ddx_id)134 TouchBeginDDXTouch(DeviceIntPtr dev, uint32_t ddx_id)
135 {
136 static int next_client_id = 1;
137 int i;
138 TouchClassPtr t = dev->touch;
139 DDXTouchPointInfoPtr ti = NULL;
140 Bool emulate_pointer;
141
142 if (!t)
143 return NULL;
144
145 emulate_pointer = (t->mode == XIDirectTouch);
146
147 /* Look for another active touchpoint with the same DDX ID. DDX
148 * touchpoints must be unique. */
149 if (TouchFindByDDXID(dev, ddx_id, FALSE))
150 return NULL;
151
152 for (;;) {
153 for (i = 0; i < dev->last.num_touches; i++) {
154 /* Only emulate pointer events on the first touch */
155 if (dev->last.touches[i].active)
156 emulate_pointer = FALSE;
157 else if (!ti) /* ti is now first non-active touch rec */
158 ti = &dev->last.touches[i];
159
160 if (!emulate_pointer && ti)
161 break;
162 }
163 if (ti)
164 break;
165 if (!TouchResizeQueue(dev))
166 break;
167 }
168
169 if (ti) {
170 int client_id;
171
172 ti->active = TRUE;
173 ti->ddx_id = ddx_id;
174 client_id = next_client_id;
175 next_client_id++;
176 if (next_client_id == 0)
177 next_client_id = 1;
178 ti->client_id = client_id;
179 ti->emulate_pointer = emulate_pointer;
180 }
181 return ti;
182 }
183
184 void
TouchEndDDXTouch(DeviceIntPtr dev,DDXTouchPointInfoPtr ti)185 TouchEndDDXTouch(DeviceIntPtr dev, DDXTouchPointInfoPtr ti)
186 {
187 TouchClassPtr t = dev->touch;
188
189 if (!t)
190 return;
191
192 ti->active = FALSE;
193 }
194
195 void
TouchInitDDXTouchPoint(DeviceIntPtr dev,DDXTouchPointInfoPtr ddxtouch)196 TouchInitDDXTouchPoint(DeviceIntPtr dev, DDXTouchPointInfoPtr ddxtouch)
197 {
198 memset(ddxtouch, 0, sizeof(*ddxtouch));
199 ddxtouch->valuators = valuator_mask_new(dev->valuator->numAxes);
200 }
201
202 Bool
TouchInitTouchPoint(TouchClassPtr t,ValuatorClassPtr v,int index)203 TouchInitTouchPoint(TouchClassPtr t, ValuatorClassPtr v, int index)
204 {
205 TouchPointInfoPtr ti;
206
207 if (index >= t->num_touches)
208 return FALSE;
209 ti = &t->touches[index];
210
211 memset(ti, 0, sizeof(*ti));
212
213 ti->valuators = valuator_mask_new(v->numAxes);
214 if (!ti->valuators)
215 return FALSE;
216
217 ti->sprite.spriteTrace = calloc(32, sizeof(*ti->sprite.spriteTrace));
218 if (!ti->sprite.spriteTrace) {
219 valuator_mask_free(&ti->valuators);
220 return FALSE;
221 }
222 ti->sprite.spriteTraceSize = 32;
223 ti->sprite.spriteTrace[0] = screenInfo.screens[0]->root;
224 ti->sprite.hot.pScreen = screenInfo.screens[0];
225 ti->sprite.hotPhys.pScreen = screenInfo.screens[0];
226
227 ti->client_id = -1;
228
229 return TRUE;
230 }
231
232 void
TouchFreeTouchPoint(DeviceIntPtr device,int index)233 TouchFreeTouchPoint(DeviceIntPtr device, int index)
234 {
235 TouchPointInfoPtr ti;
236 int i;
237
238 if (!device->touch || index >= device->touch->num_touches)
239 return;
240 ti = &device->touch->touches[index];
241
242 if (ti->active)
243 TouchEndTouch(device, ti);
244
245 for (i = 0; i < ti->num_listeners; i++)
246 TouchRemoveListener(ti, ti->listeners[0].listener);
247
248 valuator_mask_free(&ti->valuators);
249 free(ti->sprite.spriteTrace);
250 ti->sprite.spriteTrace = NULL;
251 free(ti->listeners);
252 ti->listeners = NULL;
253 free(ti->history);
254 ti->history = NULL;
255 ti->history_size = 0;
256 ti->history_elements = 0;
257 }
258
259 /**
260 * Given a client-facing ID (e.g. DeviceEvent::detail.touch), find the
261 * associated TouchPointInfoRec.
262 */
263 TouchPointInfoPtr
TouchFindByClientID(DeviceIntPtr dev,uint32_t client_id)264 TouchFindByClientID(DeviceIntPtr dev, uint32_t client_id)
265 {
266 TouchClassPtr t = dev->touch;
267 TouchPointInfoPtr ti;
268 int i;
269
270 if (!t)
271 return NULL;
272
273 for (i = 0; i < t->num_touches; i++) {
274 ti = &t->touches[i];
275 if (ti->active && ti->client_id == client_id)
276 return ti;
277 }
278
279 return NULL;
280 }
281
282 /**
283 * Given a unique ID for a touchpoint, create a touchpoint record in the
284 * server.
285 *
286 * Returns NULL on failure (i.e. if another touch with that ID is already active,
287 * allocation failure).
288 */
289 TouchPointInfoPtr
TouchBeginTouch(DeviceIntPtr dev,int sourceid,uint32_t touchid,Bool emulate_pointer)290 TouchBeginTouch(DeviceIntPtr dev, int sourceid, uint32_t touchid,
291 Bool emulate_pointer)
292 {
293 int i;
294 TouchClassPtr t = dev->touch;
295 TouchPointInfoPtr ti;
296 void *tmp;
297
298 if (!t)
299 return NULL;
300
301 /* Look for another active touchpoint with the same client ID. It's
302 * technically legitimate for a touchpoint to still exist with the same
303 * ID but only once the 32 bits wrap over and you've used up 4 billion
304 * touch ids without lifting that one finger off once. In which case
305 * you deserve a medal or something, but not error handling code. */
306 if (TouchFindByClientID(dev, touchid))
307 return NULL;
308
309 try_find_touch:
310 for (i = 0; i < t->num_touches; i++) {
311 ti = &t->touches[i];
312 if (!ti->active) {
313 ti->active = TRUE;
314 ti->client_id = touchid;
315 ti->sourceid = sourceid;
316 ti->emulate_pointer = emulate_pointer;
317 return ti;
318 }
319 }
320
321 /* If we get here, then we've run out of touches: enlarge dev->touch and
322 * try again. */
323 tmp = reallocarray(t->touches, t->num_touches + 1, sizeof(*ti));
324 if (tmp) {
325 t->touches = tmp;
326 t->num_touches++;
327 if (TouchInitTouchPoint(t, dev->valuator, t->num_touches - 1))
328 goto try_find_touch;
329 }
330
331 return NULL;
332 }
333
334 /**
335 * Releases a touchpoint for use: this must only be called after all events
336 * related to that touchpoint have been sent and finalised. Called from
337 * ProcessTouchEvent and friends. Not by you.
338 */
339 void
TouchEndTouch(DeviceIntPtr dev,TouchPointInfoPtr ti)340 TouchEndTouch(DeviceIntPtr dev, TouchPointInfoPtr ti)
341 {
342 int i;
343
344 if (ti->emulate_pointer) {
345 GrabPtr grab;
346
347 if ((grab = dev->deviceGrab.grab)) {
348 if (dev->deviceGrab.fromPassiveGrab &&
349 !dev->button->buttonsDown &&
350 !dev->touch->buttonsDown && GrabIsPointerGrab(grab))
351 (*dev->deviceGrab.DeactivateGrab) (dev);
352 }
353 }
354
355 for (i = 0; i < ti->num_listeners; i++)
356 TouchRemoveListener(ti, ti->listeners[0].listener);
357
358 ti->active = FALSE;
359 ti->pending_finish = FALSE;
360 ti->sprite.spriteTraceGood = 0;
361 free(ti->listeners);
362 ti->listeners = NULL;
363 ti->num_listeners = 0;
364 ti->num_grabs = 0;
365 ti->client_id = 0;
366
367 TouchEventHistoryFree(ti);
368
369 valuator_mask_zero(ti->valuators);
370 }
371
372 /**
373 * Allocate the event history for this touch pointer. Calling this on a
374 * touchpoint that already has an event history does nothing but counts as
375 * as success.
376 *
377 * @return TRUE on success, FALSE on allocation errors
378 */
379 Bool
TouchEventHistoryAllocate(TouchPointInfoPtr ti)380 TouchEventHistoryAllocate(TouchPointInfoPtr ti)
381 {
382 if (ti->history)
383 return TRUE;
384
385 ti->history = calloc(TOUCH_HISTORY_SIZE, sizeof(*ti->history));
386 ti->history_elements = 0;
387 if (ti->history)
388 ti->history_size = TOUCH_HISTORY_SIZE;
389 return ti->history != NULL;
390 }
391
392 void
TouchEventHistoryFree(TouchPointInfoPtr ti)393 TouchEventHistoryFree(TouchPointInfoPtr ti)
394 {
395 free(ti->history);
396 ti->history = NULL;
397 ti->history_size = 0;
398 ti->history_elements = 0;
399 }
400
401 /**
402 * Store the given event on the event history (if one exists)
403 * A touch event history consists of one TouchBegin and several TouchUpdate
404 * events (if applicable) but no TouchEnd event.
405 * If more than one TouchBegin is pushed onto the stack, the push is
406 * ignored, calling this function multiple times for the TouchBegin is
407 * valid.
408 */
409 void
TouchEventHistoryPush(TouchPointInfoPtr ti,const DeviceEvent * ev)410 TouchEventHistoryPush(TouchPointInfoPtr ti, const DeviceEvent *ev)
411 {
412 if (!ti->history)
413 return;
414
415 switch (ev->type) {
416 case ET_TouchBegin:
417 /* don't store the same touchbegin twice */
418 if (ti->history_elements > 0)
419 return;
420 break;
421 case ET_TouchUpdate:
422 break;
423 case ET_TouchEnd:
424 return; /* no TouchEnd events in the history */
425 default:
426 return;
427 }
428
429 /* We only store real events in the history */
430 if (ev->flags & (TOUCH_CLIENT_ID | TOUCH_REPLAYING))
431 return;
432
433 ti->history[ti->history_elements++] = *ev;
434 /* FIXME: proper overflow fixes */
435 if (ti->history_elements > ti->history_size - 1) {
436 ti->history_elements = ti->history_size - 1;
437 DebugF("source device %d: history size %zu overflowing for touch %u\n",
438 ti->sourceid, ti->history_size, ti->client_id);
439 }
440 }
441
442 void
TouchEventHistoryReplay(TouchPointInfoPtr ti,DeviceIntPtr dev,XID resource)443 TouchEventHistoryReplay(TouchPointInfoPtr ti, DeviceIntPtr dev, XID resource)
444 {
445 int i;
446
447 if (!ti->history)
448 return;
449
450 TouchDeliverDeviceClassesChangedEvent(ti, ti->history[0].time, resource);
451
452 for (i = 0; i < ti->history_elements; i++) {
453 DeviceEvent *ev = &ti->history[i];
454
455 ev->flags |= TOUCH_REPLAYING;
456 ev->resource = resource;
457 /* FIXME:
458 We're replaying ti->history which contains the TouchBegin +
459 all TouchUpdates for ti. This needs to be passed on to the next
460 listener. If that is a touch listener, everything is dandy.
461 If the TouchBegin however triggers a sync passive grab, the
462 TouchUpdate events must be sent to EnqueueEvent so the events end
463 up in syncEvents.pending to be forwarded correctly in a
464 subsequent ComputeFreeze().
465
466 However, if we just send them to EnqueueEvent the sync'ing device
467 prevents handling of touch events for ownership listeners who
468 want the events right here, right now.
469 */
470 dev->public.processInputProc((InternalEvent*)ev, dev);
471 }
472 }
473
474 void
TouchDeliverDeviceClassesChangedEvent(TouchPointInfoPtr ti,Time time,XID resource)475 TouchDeliverDeviceClassesChangedEvent(TouchPointInfoPtr ti, Time time,
476 XID resource)
477 {
478 DeviceIntPtr dev;
479 int num_events = 0;
480 InternalEvent dcce;
481
482 dixLookupDevice(&dev, ti->sourceid, serverClient, DixWriteAccess);
483
484 if (!dev)
485 return;
486
487 /* UpdateFromMaster generates at most one event */
488 UpdateFromMaster(&dcce, dev, DEVCHANGE_POINTER_EVENT, &num_events);
489 BUG_WARN(num_events > 1);
490
491 if (num_events) {
492 dcce.any.time = time;
493 /* FIXME: This doesn't do anything */
494 dev->public.processInputProc(&dcce, dev);
495 }
496 }
497
498 Bool
TouchBuildDependentSpriteTrace(DeviceIntPtr dev,SpritePtr sprite)499 TouchBuildDependentSpriteTrace(DeviceIntPtr dev, SpritePtr sprite)
500 {
501 int i;
502 TouchClassPtr t = dev->touch;
503 WindowPtr *trace;
504 SpritePtr srcsprite;
505
506 /* All touches should have the same sprite trace, so find and reuse an
507 * existing touch's sprite if possible, else use the device's sprite. */
508 for (i = 0; i < t->num_touches; i++)
509 if (!t->touches[i].pending_finish &&
510 t->touches[i].sprite.spriteTraceGood > 0)
511 break;
512 if (i < t->num_touches)
513 srcsprite = &t->touches[i].sprite;
514 else if (dev->spriteInfo->sprite)
515 srcsprite = dev->spriteInfo->sprite;
516 else
517 return FALSE;
518
519 if (srcsprite->spriteTraceGood > sprite->spriteTraceSize) {
520 trace = reallocarray(sprite->spriteTrace,
521 srcsprite->spriteTraceSize, sizeof(*trace));
522 if (!trace) {
523 sprite->spriteTraceGood = 0;
524 return FALSE;
525 }
526 sprite->spriteTrace = trace;
527 sprite->spriteTraceSize = srcsprite->spriteTraceGood;
528 }
529 memcpy(sprite->spriteTrace, srcsprite->spriteTrace,
530 srcsprite->spriteTraceGood * sizeof(*trace));
531 sprite->spriteTraceGood = srcsprite->spriteTraceGood;
532
533 return TRUE;
534 }
535
536 /**
537 * Ensure a window trace is present in ti->sprite, constructing one for
538 * TouchBegin events.
539 */
540 Bool
TouchBuildSprite(DeviceIntPtr sourcedev,TouchPointInfoPtr ti,InternalEvent * ev)541 TouchBuildSprite(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
542 InternalEvent *ev)
543 {
544 TouchClassPtr t = sourcedev->touch;
545 SpritePtr sprite = &ti->sprite;
546
547 if (t->mode == XIDirectTouch) {
548 /* Focus immediately under the touchpoint in direct touch mode.
549 * XXX: Do we need to handle crossing screens here? */
550 sprite->spriteTrace[0] =
551 sourcedev->spriteInfo->sprite->hotPhys.pScreen->root;
552 XYToWindow(sprite, ev->device_event.root_x, ev->device_event.root_y);
553 }
554 else if (!TouchBuildDependentSpriteTrace(sourcedev, sprite))
555 return FALSE;
556
557 if (sprite->spriteTraceGood <= 0)
558 return FALSE;
559
560 /* Mark which grabs/event selections we're delivering to: max one grab per
561 * window plus the bottom-most event selection, plus any active grab. */
562 ti->listeners = calloc(sprite->spriteTraceGood + 2, sizeof(*ti->listeners));
563 if (!ti->listeners) {
564 sprite->spriteTraceGood = 0;
565 return FALSE;
566 }
567 ti->num_listeners = 0;
568
569 return TRUE;
570 }
571
572 /**
573 * Copy the touch event into the pointer_event, switching the required
574 * fields to make it a correct pointer event.
575 *
576 * @param event The original touch event
577 * @param[in] motion_event The respective motion event
578 * @param[in] button_event The respective button event (if any)
579 *
580 * @returns The number of converted events.
581 * @retval 0 An error occured
582 * @retval 1 only the motion event is valid
583 * @retval 2 motion and button event are valid
584 */
585 int
TouchConvertToPointerEvent(const InternalEvent * event,InternalEvent * motion_event,InternalEvent * button_event)586 TouchConvertToPointerEvent(const InternalEvent *event,
587 InternalEvent *motion_event,
588 InternalEvent *button_event)
589 {
590 int ptrtype;
591 int nevents = 0;
592
593 BUG_RETURN_VAL(!event, 0);
594 BUG_RETURN_VAL(!motion_event, 0);
595
596 switch (event->any.type) {
597 case ET_TouchUpdate:
598 nevents = 1;
599 break;
600 case ET_TouchBegin:
601 nevents = 2; /* motion + press */
602 ptrtype = ET_ButtonPress;
603 break;
604 case ET_TouchEnd:
605 nevents = 2; /* motion + release */
606 ptrtype = ET_ButtonRelease;
607 break;
608 default:
609 BUG_WARN_MSG(1, "Invalid event type %d\n", event->any.type);
610 return 0;
611 }
612
613 BUG_WARN_MSG(!(event->device_event.flags & TOUCH_POINTER_EMULATED),
614 "Non-emulating touch event\n");
615
616 motion_event->device_event = event->device_event;
617 motion_event->any.type = ET_Motion;
618 motion_event->device_event.detail.button = 0;
619 motion_event->device_event.flags = XIPointerEmulated;
620
621 if (nevents > 1) {
622 BUG_RETURN_VAL(!button_event, 0);
623 button_event->device_event = event->device_event;
624 button_event->any.type = ptrtype;
625 button_event->device_event.flags = XIPointerEmulated;
626 /* detail is already correct */
627 }
628
629 return nevents;
630 }
631
632 /**
633 * Return the corresponding pointer emulation internal event type for the given
634 * touch event or 0 if no such event type exists.
635 */
636 int
TouchGetPointerEventType(const InternalEvent * event)637 TouchGetPointerEventType(const InternalEvent *event)
638 {
639 int type = 0;
640
641 switch (event->any.type) {
642 case ET_TouchBegin:
643 type = ET_ButtonPress;
644 break;
645 case ET_TouchUpdate:
646 type = ET_Motion;
647 break;
648 case ET_TouchEnd:
649 type = ET_ButtonRelease;
650 break;
651 default:
652 break;
653 }
654 return type;
655 }
656
657 /**
658 * @returns TRUE if the specified grab or selection is the current owner of
659 * the touch sequence.
660 */
661 Bool
TouchResourceIsOwner(TouchPointInfoPtr ti,XID resource)662 TouchResourceIsOwner(TouchPointInfoPtr ti, XID resource)
663 {
664 return (ti->listeners[0].listener == resource);
665 }
666
667 /**
668 * Add the resource to this touch's listeners.
669 */
670 void
TouchAddListener(TouchPointInfoPtr ti,XID resource,int resource_type,enum InputLevel level,enum TouchListenerType type,enum TouchListenerState state,WindowPtr window,const GrabPtr grab)671 TouchAddListener(TouchPointInfoPtr ti, XID resource, int resource_type,
672 enum InputLevel level, enum TouchListenerType type,
673 enum TouchListenerState state, WindowPtr window,
674 const GrabPtr grab)
675 {
676 GrabPtr g = NULL;
677
678 /* We need a copy of the grab, not the grab itself since that may be
679 * deleted by a UngrabButton request and leaves us with a dangling
680 * pointer */
681 if (grab)
682 g = AllocGrab(grab);
683
684 ti->listeners[ti->num_listeners].listener = resource;
685 ti->listeners[ti->num_listeners].resource_type = resource_type;
686 ti->listeners[ti->num_listeners].level = level;
687 ti->listeners[ti->num_listeners].state = state;
688 ti->listeners[ti->num_listeners].type = type;
689 ti->listeners[ti->num_listeners].window = window;
690 ti->listeners[ti->num_listeners].grab = g;
691 if (grab)
692 ti->num_grabs++;
693 ti->num_listeners++;
694 }
695
696 /**
697 * Remove the resource from this touch's listeners.
698 *
699 * @return TRUE if the resource was removed, FALSE if the resource was not
700 * in the list
701 */
702 Bool
TouchRemoveListener(TouchPointInfoPtr ti,XID resource)703 TouchRemoveListener(TouchPointInfoPtr ti, XID resource)
704 {
705 int i;
706
707 for (i = 0; i < ti->num_listeners; i++) {
708 int j;
709 TouchListener *listener = &ti->listeners[i];
710
711 if (listener->listener != resource)
712 continue;
713
714 if (listener->grab) {
715 FreeGrab(listener->grab);
716 listener->grab = NULL;
717 ti->num_grabs--;
718 }
719
720 for (j = i; j < ti->num_listeners - 1; j++)
721 ti->listeners[j] = ti->listeners[j + 1];
722 ti->num_listeners--;
723 ti->listeners[ti->num_listeners].listener = 0;
724 ti->listeners[ti->num_listeners].state = LISTENER_AWAITING_BEGIN;
725
726 return TRUE;
727 }
728 return FALSE;
729 }
730
731 static void
TouchAddGrabListener(DeviceIntPtr dev,TouchPointInfoPtr ti,InternalEvent * ev,GrabPtr grab)732 TouchAddGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
733 InternalEvent *ev, GrabPtr grab)
734 {
735 enum TouchListenerType type = LISTENER_GRAB;
736
737 /* FIXME: owner_events */
738
739 if (grab->grabtype == XI2) {
740 if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchOwnership))
741 TouchEventHistoryAllocate(ti);
742 if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin))
743 type = LISTENER_POINTER_GRAB;
744 }
745 else if (grab->grabtype == XI || grab->grabtype == CORE) {
746 TouchEventHistoryAllocate(ti);
747 type = LISTENER_POINTER_GRAB;
748 }
749
750 /* grab listeners are always RT_NONE since we keep the grab pointer */
751 TouchAddListener(ti, grab->resource, RT_NONE, grab->grabtype,
752 type, LISTENER_AWAITING_BEGIN, grab->window, grab);
753 }
754
755 /**
756 * Add one listener if there is a grab on the given window.
757 */
758 static void
TouchAddPassiveGrabListener(DeviceIntPtr dev,TouchPointInfoPtr ti,WindowPtr win,InternalEvent * ev)759 TouchAddPassiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
760 WindowPtr win, InternalEvent *ev)
761 {
762 GrabPtr grab;
763 Bool check_core = IsMaster(dev) && ti->emulate_pointer;
764
765 /* FIXME: make CheckPassiveGrabsOnWindow only trigger on TouchBegin */
766 grab = CheckPassiveGrabsOnWindow(win, dev, ev, check_core, FALSE);
767 if (!grab)
768 return;
769
770 TouchAddGrabListener(dev, ti, ev, grab);
771 }
772
773 static Bool
TouchAddRegularListener(DeviceIntPtr dev,TouchPointInfoPtr ti,WindowPtr win,InternalEvent * ev)774 TouchAddRegularListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
775 WindowPtr win, InternalEvent *ev)
776 {
777 InputClients *iclients = NULL;
778 OtherInputMasks *inputMasks = NULL;
779 uint16_t evtype = 0; /* may be event type or emulated event type */
780 enum TouchListenerType type = LISTENER_REGULAR;
781 int mask;
782
783 evtype = GetXI2Type(ev->any.type);
784 mask = EventIsDeliverable(dev, ev->any.type, win);
785 if (!mask && !ti->emulate_pointer)
786 return FALSE;
787 else if (!mask) { /* now try for pointer event */
788 mask = EventIsDeliverable(dev, TouchGetPointerEventType(ev), win);
789 if (mask) {
790 evtype = GetXI2Type(TouchGetPointerEventType(ev));
791 type = LISTENER_POINTER_REGULAR;
792 }
793 }
794 if (!mask)
795 return FALSE;
796
797 inputMasks = wOtherInputMasks(win);
798
799 if (mask & EVENT_XI2_MASK) {
800 nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
801 if (!xi2mask_isset(iclients->xi2mask, dev, evtype))
802 continue;
803
804 if (!xi2mask_isset(iclients->xi2mask, dev, XI_TouchOwnership))
805 TouchEventHistoryAllocate(ti);
806
807 TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI2,
808 type, LISTENER_AWAITING_BEGIN, win, NULL);
809 return TRUE;
810 }
811 }
812
813 if (mask & EVENT_XI1_MASK) {
814 int xitype = GetXIType(TouchGetPointerEventType(ev));
815 Mask xi_filter = event_get_filter_from_type(dev, xitype);
816
817 nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
818 if (!(iclients->mask[dev->id] & xi_filter))
819 continue;
820
821 TouchEventHistoryAllocate(ti);
822 TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI,
823 LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN,
824 win, NULL);
825 return TRUE;
826 }
827 }
828
829 if (mask & EVENT_CORE_MASK) {
830 int coretype = GetCoreType(TouchGetPointerEventType(ev));
831 Mask core_filter = event_get_filter_from_type(dev, coretype);
832 OtherClients *oclients;
833
834 /* window owner */
835 if (IsMaster(dev) && (win->eventMask & core_filter)) {
836 TouchEventHistoryAllocate(ti);
837 TouchAddListener(ti, win->drawable.id, RT_WINDOW, CORE,
838 LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN,
839 win, NULL);
840 return TRUE;
841 }
842
843 /* all others */
844 nt_list_for_each_entry(oclients, wOtherClients(win), next) {
845 if (!(oclients->mask & core_filter))
846 continue;
847
848 TouchEventHistoryAllocate(ti);
849 TouchAddListener(ti, oclients->resource, RT_OTHERCLIENT, CORE,
850 type, LISTENER_AWAITING_BEGIN, win, NULL);
851 return TRUE;
852 }
853 }
854
855 return FALSE;
856 }
857
858 static void
TouchAddActiveGrabListener(DeviceIntPtr dev,TouchPointInfoPtr ti,InternalEvent * ev,GrabPtr grab)859 TouchAddActiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
860 InternalEvent *ev, GrabPtr grab)
861 {
862 if (!ti->emulate_pointer &&
863 (grab->grabtype == CORE || grab->grabtype == XI))
864 return;
865
866 if (!ti->emulate_pointer &&
867 grab->grabtype == XI2 &&
868 !xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin))
869 return;
870
871 TouchAddGrabListener(dev, ti, ev, grab);
872 }
873
874 void
TouchSetupListeners(DeviceIntPtr dev,TouchPointInfoPtr ti,InternalEvent * ev)875 TouchSetupListeners(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev)
876 {
877 int i;
878 SpritePtr sprite = &ti->sprite;
879 WindowPtr win;
880
881 if (dev->deviceGrab.grab && !dev->deviceGrab.fromPassiveGrab)
882 TouchAddActiveGrabListener(dev, ti, ev, dev->deviceGrab.grab);
883
884 /* We set up an active touch listener for existing touches, but not any
885 * passive grab or regular listeners. */
886 if (ev->any.type != ET_TouchBegin)
887 return;
888
889 /* First, find all grabbing clients from the root window down
890 * to the deepest child window. */
891 for (i = 0; i < sprite->spriteTraceGood; i++) {
892 win = sprite->spriteTrace[i];
893 TouchAddPassiveGrabListener(dev, ti, win, ev);
894 }
895
896 /* Find the first client with an applicable event selection,
897 * going from deepest child window back up to the root window. */
898 for (i = sprite->spriteTraceGood - 1; i >= 0; i--) {
899 Bool delivered;
900
901 win = sprite->spriteTrace[i];
902 delivered = TouchAddRegularListener(dev, ti, win, ev);
903 if (delivered)
904 return;
905 }
906 }
907
908 /**
909 * Remove the touch pointer grab from the device. Called from
910 * DeactivatePointerGrab()
911 */
912 void
TouchRemovePointerGrab(DeviceIntPtr dev)913 TouchRemovePointerGrab(DeviceIntPtr dev)
914 {
915 TouchPointInfoPtr ti;
916 GrabPtr grab;
917 DeviceEvent *ev;
918
919 if (!dev->touch)
920 return;
921
922 grab = dev->deviceGrab.grab;
923 if (!grab)
924 return;
925
926 ev = dev->deviceGrab.sync.event;
927 if (!IsTouchEvent((InternalEvent *) ev))
928 return;
929
930 ti = TouchFindByClientID(dev, ev->touchid);
931 if (!ti)
932 return;
933
934 /* FIXME: missing a bit of code here... */
935 }
936
937 /* As touch grabs don't turn into active grabs with their own resources, we
938 * need to walk all the touches and remove this grab from any delivery
939 * lists. */
940 void
TouchListenerGone(XID resource)941 TouchListenerGone(XID resource)
942 {
943 TouchPointInfoPtr ti;
944 DeviceIntPtr dev;
945 InternalEvent *events = InitEventList(GetMaximumEventsNum());
946 int i, j, k, nev;
947
948 if (!events)
949 FatalError("TouchListenerGone: couldn't allocate events\n");
950
951 for (dev = inputInfo.devices; dev; dev = dev->next) {
952 if (!dev->touch)
953 continue;
954
955 for (i = 0; i < dev->touch->num_touches; i++) {
956 ti = &dev->touch->touches[i];
957 if (!ti->active)
958 continue;
959
960 for (j = 0; j < ti->num_listeners; j++) {
961 if (CLIENT_BITS(ti->listeners[j].listener) != resource)
962 continue;
963
964 nev = GetTouchOwnershipEvents(events, dev, ti, XIRejectTouch,
965 ti->listeners[j].listener, 0);
966 for (k = 0; k < nev; k++)
967 mieqProcessDeviceEvent(dev, events + k, NULL);
968
969 break;
970 }
971 }
972 }
973
974 FreeEventList(events, GetMaximumEventsNum());
975 }
976
977 int
TouchListenerAcceptReject(DeviceIntPtr dev,TouchPointInfoPtr ti,int listener,int mode)978 TouchListenerAcceptReject(DeviceIntPtr dev, TouchPointInfoPtr ti, int listener,
979 int mode)
980 {
981 InternalEvent *events;
982 int nev;
983 int i;
984
985 BUG_RETURN_VAL(listener < 0, BadMatch);
986 BUG_RETURN_VAL(listener >= ti->num_listeners, BadMatch);
987
988 if (listener > 0) {
989 if (mode == XIRejectTouch)
990 TouchRejected(dev, ti, ti->listeners[listener].listener, NULL);
991 else
992 ti->listeners[listener].state = LISTENER_EARLY_ACCEPT;
993
994 return Success;
995 }
996
997 events = InitEventList(GetMaximumEventsNum());
998 BUG_RETURN_VAL_MSG(!events, BadAlloc, "Failed to allocate touch ownership events\n");
999
1000 nev = GetTouchOwnershipEvents(events, dev, ti, mode,
1001 ti->listeners[0].listener, 0);
1002 BUG_WARN_MSG(nev == 0, "Failed to get touch ownership events\n");
1003
1004 for (i = 0; i < nev; i++)
1005 mieqProcessDeviceEvent(dev, events + i, NULL);
1006
1007 FreeEventList(events, GetMaximumEventsNum());
1008
1009 return nev ? Success : BadMatch;
1010 }
1011
1012 int
TouchAcceptReject(ClientPtr client,DeviceIntPtr dev,int mode,uint32_t touchid,Window grab_window,XID * error)1013 TouchAcceptReject(ClientPtr client, DeviceIntPtr dev, int mode,
1014 uint32_t touchid, Window grab_window, XID *error)
1015 {
1016 TouchPointInfoPtr ti;
1017 int i;
1018
1019 if (!dev->touch) {
1020 *error = dev->id;
1021 return BadDevice;
1022 }
1023
1024 ti = TouchFindByClientID(dev, touchid);
1025 if (!ti) {
1026 *error = touchid;
1027 return BadValue;
1028 }
1029
1030 for (i = 0; i < ti->num_listeners; i++) {
1031 if (CLIENT_ID(ti->listeners[i].listener) == client->index &&
1032 ti->listeners[i].window->drawable.id == grab_window)
1033 break;
1034 }
1035 if (i == ti->num_listeners)
1036 return BadAccess;
1037
1038 return TouchListenerAcceptReject(dev, ti, i, mode);
1039 }
1040
1041 /**
1042 * End physically active touches for a device.
1043 */
1044 void
TouchEndPhysicallyActiveTouches(DeviceIntPtr dev)1045 TouchEndPhysicallyActiveTouches(DeviceIntPtr dev)
1046 {
1047 InternalEvent *eventlist = InitEventList(GetMaximumEventsNum());
1048 int i;
1049
1050 input_lock();
1051 mieqProcessInputEvents();
1052 for (i = 0; i < dev->last.num_touches; i++) {
1053 DDXTouchPointInfoPtr ddxti = dev->last.touches + i;
1054
1055 if (ddxti->active) {
1056 int j;
1057 int nevents = GetTouchEvents(eventlist, dev, ddxti->ddx_id,
1058 XI_TouchEnd, 0, NULL);
1059
1060 for (j = 0; j < nevents; j++)
1061 mieqProcessDeviceEvent(dev, eventlist + j, NULL);
1062 }
1063 }
1064 input_unlock();
1065
1066 FreeEventList(eventlist, GetMaximumEventsNum());
1067 }
1068
1069 /**
1070 * Generate and deliver a TouchEnd event.
1071 *
1072 * @param dev The device to deliver the event for.
1073 * @param ti The touch point record to deliver the event for.
1074 * @param flags Internal event flags. The called does not need to provide
1075 * TOUCH_CLIENT_ID and TOUCH_POINTER_EMULATED, this function will ensure
1076 * they are set appropriately.
1077 * @param resource The client resource to deliver to, or 0 for all clients.
1078 */
1079 void
TouchEmitTouchEnd(DeviceIntPtr dev,TouchPointInfoPtr ti,int flags,XID resource)1080 TouchEmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource)
1081 {
1082 InternalEvent event;
1083
1084 /* We're not processing a touch end for a frozen device */
1085 if (dev->deviceGrab.sync.frozen)
1086 return;
1087
1088 flags |= TOUCH_CLIENT_ID;
1089 if (ti->emulate_pointer)
1090 flags |= TOUCH_POINTER_EMULATED;
1091 TouchDeliverDeviceClassesChangedEvent(ti, GetTimeInMillis(), resource);
1092 GetDixTouchEnd(&event, dev, ti, flags);
1093 DeliverTouchEvents(dev, ti, &event, resource);
1094 if (ti->num_grabs == 0)
1095 UpdateDeviceState(dev, &event.device_event);
1096 }
1097
1098 void
TouchAcceptAndEnd(DeviceIntPtr dev,int touchid)1099 TouchAcceptAndEnd(DeviceIntPtr dev, int touchid)
1100 {
1101 TouchPointInfoPtr ti = TouchFindByClientID(dev, touchid);
1102 if (!ti)
1103 return;
1104
1105 TouchListenerAcceptReject(dev, ti, 0, XIAcceptTouch);
1106 if (ti->pending_finish)
1107 TouchEmitTouchEnd(dev, ti, 0, 0);
1108 if (ti->num_listeners <= 1)
1109 TouchEndTouch(dev, ti);
1110 }
1111