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