Imported Upstream version 1.15.1
[deb_xorg-server.git] / Xi / xibarriers.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright 2012 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Copyright © 2002 Keith Packard
24 *
25 * Permission to use, copy, modify, distribute, and sell this software and its
26 * documentation for any purpose is hereby granted without fee, provided that
27 * the above copyright notice appear in all copies and that both that
28 * copyright notice and this permission notice appear in supporting
29 * documentation, and that the name of Keith Packard not be used in
30 * advertising or publicity pertaining to distribution of the software without
31 * specific, written prior permission. Keith Packard makes no
32 * representations about the suitability of this software for any purpose. It
33 * is provided "as is" without express or implied warranty.
34 *
35 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
36 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
37 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
38 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
39 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
40 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
41 * PERFORMANCE OF THIS SOFTWARE.
42 */
43
44#ifdef HAVE_DIX_CONFIG_H
45#include <dix-config.h>
46#endif
47
48#include "xibarriers.h"
49#include "scrnintstr.h"
50#include "cursorstr.h"
51#include "dixevents.h"
52#include "servermd.h"
53#include "mipointer.h"
54#include "inputstr.h"
55#include "windowstr.h"
56#include "xace.h"
57#include "list.h"
58#include "exglobals.h"
59#include "eventstr.h"
60#include "mi.h"
61
62RESTYPE PointerBarrierType;
63
64static DevPrivateKeyRec BarrierScreenPrivateKeyRec;
65
66#define BarrierScreenPrivateKey (&BarrierScreenPrivateKeyRec)
67
68typedef struct PointerBarrierClient *PointerBarrierClientPtr;
69
70struct PointerBarrierDevice {
71 struct xorg_list entry;
72 int deviceid;
73 Time last_timestamp;
74 int barrier_event_id;
75 int release_event_id;
76 Bool hit;
77 Bool seen;
78};
79
80struct PointerBarrierClient {
81 XID id;
82 ScreenPtr screen;
83 Window window;
84 struct PointerBarrier barrier;
85 struct xorg_list entry;
86 /* num_devices/device_ids are devices the barrier applies to */
87 int num_devices;
88 int *device_ids; /* num_devices */
89
90 /* per_device keeps track of devices actually blocked by barriers */
91 struct xorg_list per_device;
92};
93
94typedef struct _BarrierScreen {
95 struct xorg_list barriers;
96} BarrierScreenRec, *BarrierScreenPtr;
97
98#define GetBarrierScreen(s) ((BarrierScreenPtr)dixLookupPrivate(&(s)->devPrivates, BarrierScreenPrivateKey))
99#define GetBarrierScreenIfSet(s) GetBarrierScreen(s)
100#define SetBarrierScreen(s,p) dixSetPrivate(&(s)->devPrivates, BarrierScreenPrivateKey, p)
101
102static struct PointerBarrierDevice *AllocBarrierDevice(void)
103{
104 struct PointerBarrierDevice *pbd = NULL;
105
106 pbd = malloc(sizeof(struct PointerBarrierDevice));
107 if (!pbd)
108 return NULL;
109
110 pbd->deviceid = -1; /* must be set by caller */
111 pbd->barrier_event_id = 1;
112 pbd->release_event_id = 0;
113 pbd->hit = FALSE;
114 pbd->seen = FALSE;
115 xorg_list_init(&pbd->entry);
116
117 return pbd;
118}
119
120static void FreePointerBarrierClient(struct PointerBarrierClient *c)
121{
122 struct PointerBarrierDevice *pbd = NULL, *tmp = NULL;
123
124 xorg_list_for_each_entry_safe(pbd, tmp, &c->per_device, entry) {
125 free(pbd);
126 }
127 free(c);
128}
129
130static struct PointerBarrierDevice *GetBarrierDevice(struct PointerBarrierClient *c, int deviceid)
131{
132 struct PointerBarrierDevice *pbd = NULL;
133
134 xorg_list_for_each_entry(pbd, &c->per_device, entry) {
135 if (pbd->deviceid == deviceid)
136 break;
137 }
138
139 BUG_WARN(!pbd);
140 return pbd;
141}
142
143static BOOL
144barrier_is_horizontal(const struct PointerBarrier *barrier)
145{
146 return barrier->y1 == barrier->y2;
147}
148
149static BOOL
150barrier_is_vertical(const struct PointerBarrier *barrier)
151{
152 return barrier->x1 == barrier->x2;
153}
154
155/**
156 * @return The set of barrier movement directions the movement vector
157 * x1/y1 → x2/y2 represents.
158 */
159int
160barrier_get_direction(int x1, int y1, int x2, int y2)
161{
162 int direction = 0;
163
164 /* which way are we trying to go */
165 if (x2 > x1)
166 direction |= BarrierPositiveX;
167 if (x2 < x1)
168 direction |= BarrierNegativeX;
169 if (y2 > y1)
170 direction |= BarrierPositiveY;
171 if (y2 < y1)
172 direction |= BarrierNegativeY;
173
174 return direction;
175}
176
177/**
178 * Test if the barrier may block movement in the direction defined by
179 * x1/y1 → x2/y2. This function only tests whether the directions could be
180 * blocked, it does not test if the barrier actually blocks the movement.
181 *
182 * @return TRUE if the barrier blocks the direction of movement or FALSE
183 * otherwise.
184 */
185BOOL
186barrier_is_blocking_direction(const struct PointerBarrier * barrier,
187 int direction)
188{
189 /* Barriers define which way is ok, not which way is blocking */
190 return (barrier->directions & direction) != direction;
191}
192
193static BOOL
194inside_segment(int v, int v1, int v2)
195{
196 if (v1 < 0 && v2 < 0) /* line */
197 return TRUE;
198 else if (v1 < 0) /* ray */
199 return v <= v2;
200 else if (v2 < 0) /* ray */
201 return v >= v1;
202 else /* line segment */
203 return v >= v1 && v <= v2;
204}
205
206#define T(v, a, b) (((float)v) - (a)) / ((b) - (a))
207#define F(t, a, b) ((t) * ((a) - (b)) + (a))
208
209/**
210 * Test if the movement vector x1/y1 → x2/y2 is intersecting with the
211 * barrier. A movement vector with the startpoint or endpoint adjacent to
212 * the barrier itself counts as intersecting.
213 *
214 * @param x1 X start coordinate of movement vector
215 * @param y1 Y start coordinate of movement vector
216 * @param x2 X end coordinate of movement vector
217 * @param y2 Y end coordinate of movement vector
218 * @param[out] distance The distance between the start point and the
219 * intersection with the barrier (if applicable).
220 * @return TRUE if the barrier intersects with the given vector
221 */
222BOOL
223barrier_is_blocking(const struct PointerBarrier * barrier,
224 int x1, int y1, int x2, int y2, double *distance)
225{
226 if (barrier_is_vertical(barrier)) {
227 float t, y;
228 t = T(barrier->x1, x1, x2);
229 if (t < 0 || t > 1)
230 return FALSE;
231
232 /* Edge case: moving away from barrier. */
233 if (x2 > x1 && t == 0)
234 return FALSE;
235
236 y = F(t, y1, y2);
237 if (!inside_segment(y, barrier->y1, barrier->y2))
238 return FALSE;
239
240 *distance = sqrt((pow(y - y1, 2) + pow(barrier->x1 - x1, 2)));
241 return TRUE;
242 }
243 else {
244 float t, x;
245 t = T(barrier->y1, y1, y2);
246 if (t < 0 || t > 1)
247 return FALSE;
248
249 /* Edge case: moving away from barrier. */
250 if (y2 > y1 && t == 0)
251 return FALSE;
252
253 x = F(t, x1, x2);
254 if (!inside_segment(x, barrier->x1, barrier->x2))
255 return FALSE;
256
257 *distance = sqrt((pow(x - x1, 2) + pow(barrier->y1 - y1, 2)));
258 return TRUE;
259 }
260}
261
262#define HIT_EDGE_EXTENTS 2
263static BOOL
264barrier_inside_hit_box(struct PointerBarrier *barrier, int x, int y)
265{
266 int x1, x2, y1, y2;
267 int dir;
268
269 x1 = barrier->x1;
270 x2 = barrier->x2;
271 y1 = barrier->y1;
272 y2 = barrier->y2;
273 dir = ~(barrier->directions);
274
275 if (barrier_is_vertical(barrier)) {
276 if (dir & BarrierPositiveX)
277 x1 -= HIT_EDGE_EXTENTS;
278 if (dir & BarrierNegativeX)
279 x2 += HIT_EDGE_EXTENTS;
280 }
281 if (barrier_is_horizontal(barrier)) {
282 if (dir & BarrierPositiveY)
283 y1 -= HIT_EDGE_EXTENTS;
284 if (dir & BarrierNegativeY)
285 y2 += HIT_EDGE_EXTENTS;
286 }
287
288 return x >= x1 && x <= x2 && y >= y1 && y <= y2;
289}
290
291static BOOL
292barrier_blocks_device(struct PointerBarrierClient *client,
293 DeviceIntPtr dev)
294{
295 int i;
296 int master_id;
297
298 /* Clients with no devices are treated as
299 * if they specified XIAllDevices. */
300 if (client->num_devices == 0)
301 return TRUE;
302
303 master_id = GetMaster(dev, POINTER_OR_FLOAT)->id;
304
305 for (i = 0; i < client->num_devices; i++) {
306 int device_id = client->device_ids[i];
307 if (device_id == XIAllDevices ||
308 device_id == XIAllMasterDevices ||
309 device_id == master_id)
310 return TRUE;
311 }
312
313 return FALSE;
314}
315
316/**
317 * Find the nearest barrier client that is blocking movement from x1/y1 to x2/y2.
318 *
319 * @param dir Only barriers blocking movement in direction dir are checked
320 * @param x1 X start coordinate of movement vector
321 * @param y1 Y start coordinate of movement vector
322 * @param x2 X end coordinate of movement vector
323 * @param y2 Y end coordinate of movement vector
324 * @return The barrier nearest to the movement origin that blocks this movement.
325 */
326static struct PointerBarrierClient *
327barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev,
328 int dir,
329 int x1, int y1, int x2, int y2)
330{
331 struct PointerBarrierClient *c, *nearest = NULL;
332 double min_distance = INT_MAX; /* can't get higher than that in X anyway */
333
334 xorg_list_for_each_entry(c, &cs->barriers, entry) {
335 struct PointerBarrier *b = &c->barrier;
336 struct PointerBarrierDevice *pbd;
337 double distance;
338
339 pbd = GetBarrierDevice(c, dev->id);
340 if (pbd->seen)
341 continue;
342
343 if (!barrier_is_blocking_direction(b, dir))
344 continue;
345
346 if (!barrier_blocks_device(c, dev))
347 continue;
348
349 if (barrier_is_blocking(b, x1, y1, x2, y2, &distance)) {
350 if (min_distance > distance) {
351 min_distance = distance;
352 nearest = c;
353 }
354 }
355 }
356
357 return nearest;
358}
359
360/**
361 * Clamp to the given barrier given the movement direction specified in dir.
362 *
363 * @param barrier The barrier to clamp to
364 * @param dir The movement direction
365 * @param[out] x The clamped x coordinate.
366 * @param[out] y The clamped x coordinate.
367 */
368void
369barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x,
370 int *y)
371{
372 if (barrier_is_vertical(barrier)) {
373 if ((dir & BarrierNegativeX) & ~barrier->directions)
374 *x = barrier->x1;
375 if ((dir & BarrierPositiveX) & ~barrier->directions)
376 *x = barrier->x1 - 1;
377 }
378 if (barrier_is_horizontal(barrier)) {
379 if ((dir & BarrierNegativeY) & ~barrier->directions)
380 *y = barrier->y1;
381 if ((dir & BarrierPositiveY) & ~barrier->directions)
382 *y = barrier->y1 - 1;
383 }
384}
385
386void
387input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
388 int current_x, int current_y,
389 int dest_x, int dest_y,
390 int *out_x, int *out_y,
391 int *nevents, InternalEvent* events)
392{
393 /* Clamped coordinates here refer to screen edge clamping. */
394 BarrierScreenPtr cs = GetBarrierScreen(screen);
395 int x = dest_x,
396 y = dest_y;
397 int dir;
398 struct PointerBarrier *nearest = NULL;
399 PointerBarrierClientPtr c;
400 Time ms = GetTimeInMillis();
401 BarrierEvent ev = {
402 .header = ET_Internal,
403 .type = 0,
404 .length = sizeof (BarrierEvent),
405 .time = ms,
406 .deviceid = dev->id,
407 .sourceid = dev->id,
408 .dx = dest_x - current_x,
409 .dy = dest_y - current_y,
410 .root = screen->root->drawable.id,
411 };
412 InternalEvent *barrier_events = events;
413 DeviceIntPtr master;
414
415 if (nevents)
416 *nevents = 0;
417
418 if (xorg_list_is_empty(&cs->barriers) || IsFloating(dev))
419 goto out;
420
421 /**
422 * This function is only called for slave devices, but pointer-barriers
423 * are for master-devices only. Flip the device to the master here,
424 * continue with that.
425 */
426 master = GetMaster(dev, MASTER_POINTER);
427
428 /* How this works:
429 * Given the origin and the movement vector, get the nearest barrier
430 * to the origin that is blocking the movement.
431 * Clamp to that barrier.
432 * Then, check from the clamped intersection to the original
433 * destination, again finding the nearest barrier and clamping.
434 */
435 dir = barrier_get_direction(current_x, current_y, x, y);
436
437 while (dir != 0) {
438 int new_sequence;
439 struct PointerBarrierDevice *pbd;
440
441 c = barrier_find_nearest(cs, master, dir, current_x, current_y, x, y);
442 if (!c)
443 break;
444
445 nearest = &c->barrier;
446
447 pbd = GetBarrierDevice(c, master->id);
448 new_sequence = !pbd->hit;
449
450 pbd->seen = TRUE;
451 pbd->hit = TRUE;
452
453 if (pbd->barrier_event_id == pbd->release_event_id)
454 continue;
455
456 ev.type = ET_BarrierHit;
457 barrier_clamp_to_barrier(nearest, dir, &x, &y);
458
459 if (barrier_is_vertical(nearest)) {
460 dir &= ~(BarrierNegativeX | BarrierPositiveX);
461 current_x = x;
462 }
463 else if (barrier_is_horizontal(nearest)) {
464 dir &= ~(BarrierNegativeY | BarrierPositiveY);
465 current_y = y;
466 }
467
468 ev.flags = 0;
469 ev.event_id = pbd->barrier_event_id;
470 ev.barrierid = c->id;
471
472 ev.dt = new_sequence ? 0 : ms - pbd->last_timestamp;
473 ev.window = c->window;
474 pbd->last_timestamp = ms;
475
476 /* root x/y is filled in later */
477
478 barrier_events->barrier_event = ev;
479 barrier_events++;
480 *nevents += 1;
481 }
482
483 xorg_list_for_each_entry(c, &cs->barriers, entry) {
484 struct PointerBarrierDevice *pbd;
485 int flags = 0;
486
487 pbd = GetBarrierDevice(c, master->id);
488 pbd->seen = FALSE;
489 if (!pbd->hit)
490 continue;
491
492 if (barrier_inside_hit_box(&c->barrier, x, y))
493 continue;
494
495 pbd->hit = FALSE;
496
497 ev.type = ET_BarrierLeave;
498
499 if (pbd->barrier_event_id == pbd->release_event_id)
500 flags |= XIBarrierPointerReleased;
501
502 ev.flags = flags;
503 ev.event_id = pbd->barrier_event_id;
504 ev.barrierid = c->id;
505
506 ev.dt = ms - pbd->last_timestamp;
507 ev.window = c->window;
508 pbd->last_timestamp = ms;
509
510 /* root x/y is filled in later */
511
512 barrier_events->barrier_event = ev;
513 barrier_events++;
514 *nevents += 1;
515
516 /* If we've left the hit box, this is the
517 * start of a new event ID. */
518 pbd->barrier_event_id++;
519 }
520
521 out:
522 *out_x = x;
523 *out_y = y;
524}
525
526static void
527sort_min_max(INT16 *a, INT16 *b)
528{
529 INT16 A, B;
530 if (*a < 0 || *b < 0)
531 return;
532 A = *a;
533 B = *b;
534 *a = min(A, B);
535 *b = max(A, B);
536}
537
538static int
539CreatePointerBarrierClient(ClientPtr client,
540 xXFixesCreatePointerBarrierReq * stuff,
541 PointerBarrierClientPtr *client_out)
542{
543 WindowPtr pWin;
544 ScreenPtr screen;
545 BarrierScreenPtr cs;
546 int err;
547 int size;
548 int i;
549 struct PointerBarrierClient *ret;
550 CARD16 *in_devices;
551 DeviceIntPtr dev;
552
553 size = sizeof(*ret) + sizeof(DeviceIntPtr) * stuff->num_devices;
554 ret = malloc(size);
555
556 if (!ret) {
557 return BadAlloc;
558 }
559
560 xorg_list_init(&ret->per_device);
561
562 err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
563 if (err != Success) {
564 client->errorValue = stuff->window;
565 goto error;
566 }
567
568 screen = pWin->drawable.pScreen;
569 cs = GetBarrierScreen(screen);
570
571 ret->screen = screen;
572 ret->window = stuff->window;
573 ret->num_devices = stuff->num_devices;
574 if (ret->num_devices > 0)
575 ret->device_ids = (int*)&ret[1];
576 else
577 ret->device_ids = NULL;
578
579 in_devices = (CARD16 *) &stuff[1];
580 for (i = 0; i < stuff->num_devices; i++) {
581 int device_id = in_devices[i];
582 DeviceIntPtr device;
583
584 if ((err = dixLookupDevice (&device, device_id,
585 client, DixReadAccess))) {
586 client->errorValue = device_id;
587 goto error;
588 }
589
590 if (!IsMaster (device)) {
591 client->errorValue = device_id;
592 err = BadDevice;
593 goto error;
594 }
595
596 ret->device_ids[i] = device_id;
597 }
598
599 /* Alloc one per master pointer, they're the ones that can be blocked */
600 xorg_list_init(&ret->per_device);
601 nt_list_for_each_entry(dev, inputInfo.devices, next) {
602 struct PointerBarrierDevice *pbd;
603
604 if (dev->type != MASTER_POINTER)
605 continue;
606
607 pbd = AllocBarrierDevice();
608 if (!pbd) {
609 err = BadAlloc;
610 goto error;
611 }
612 pbd->deviceid = dev->id;
613
614 xorg_list_add(&pbd->entry, &ret->per_device);
615 }
616
617 ret->id = stuff->barrier;
618 ret->barrier.x1 = stuff->x1;
619 ret->barrier.x2 = stuff->x2;
620 ret->barrier.y1 = stuff->y1;
621 ret->barrier.y2 = stuff->y2;
622 sort_min_max(&ret->barrier.x1, &ret->barrier.x2);
623 sort_min_max(&ret->barrier.y1, &ret->barrier.y2);
624 ret->barrier.directions = stuff->directions & 0x0f;
625 if (barrier_is_horizontal(&ret->barrier))
626 ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
627 if (barrier_is_vertical(&ret->barrier))
628 ret->barrier.directions &= ~(BarrierPositiveY | BarrierNegativeY);
629 xorg_list_add(&ret->entry, &cs->barriers);
630
631 *client_out = ret;
632 return Success;
633
634 error:
635 *client_out = NULL;
636 FreePointerBarrierClient(ret);
637 return err;
638}
639
640static int
641BarrierFreeBarrier(void *data, XID id)
642{
643 struct PointerBarrierClient *c;
644 Time ms = GetTimeInMillis();
645 DeviceIntPtr dev = NULL;
646 ScreenPtr screen;
647
648 c = container_of(data, struct PointerBarrierClient, barrier);
649 screen = c->screen;
650
651 for (dev = inputInfo.devices; dev; dev = dev->next) {
652 struct PointerBarrierDevice *pbd;
653 int root_x, root_y;
654 BarrierEvent ev = {
655 .header = ET_Internal,
656 .type = ET_BarrierLeave,
657 .length = sizeof (BarrierEvent),
658 .time = ms,
659 /* .deviceid */
660 .sourceid = 0,
661 .barrierid = c->id,
662 .window = c->window,
663 .root = screen->root->drawable.id,
664 .dx = 0,
665 .dy = 0,
666 /* .root_x */
667 /* .root_y */
668 /* .dt */
669 /* .event_id */
670 .flags = XIBarrierPointerReleased,
671 };
672
673
674 if (dev->type != MASTER_POINTER)
675 continue;
676
677 pbd = GetBarrierDevice(c, dev->id);
678 if (!pbd->hit)
679 continue;
680
681 ev.deviceid = dev->id;
682 ev.event_id = pbd->barrier_event_id;
683 ev.dt = ms - pbd->last_timestamp;
684
685 GetSpritePosition(dev, &root_x, &root_y);
686 ev.root_x = root_x;
687 ev.root_y = root_y;
688
689 mieqEnqueue(dev, (InternalEvent *) &ev);
690 }
691
692 xorg_list_del(&c->entry);
693
694 FreePointerBarrierClient(c);
695 return Success;
696}
697
698static void add_master_func(pointer res, XID id, pointer devid)
699{
700 struct PointerBarrier *b;
701 struct PointerBarrierClient *barrier;
702 struct PointerBarrierDevice *pbd;
703 int *deviceid = devid;
704
705 b = res;
706 barrier = container_of(b, struct PointerBarrierClient, barrier);
707
708
709 pbd = AllocBarrierDevice();
710 pbd->deviceid = *deviceid;
711
712 xorg_list_add(&pbd->entry, &barrier->per_device);
713}
714
715static void remove_master_func(pointer res, XID id, pointer devid)
716{
717 struct PointerBarrierDevice *pbd;
718 struct PointerBarrierClient *barrier;
719 struct PointerBarrier *b;
720 DeviceIntPtr dev;
721 int *deviceid = devid;
722 int rc;
723 Time ms = GetTimeInMillis();
724
725 rc = dixLookupDevice(&dev, *deviceid, serverClient, DixSendAccess);
726 if (rc != Success)
727 return;
728
729 b = res;
730 barrier = container_of(b, struct PointerBarrierClient, barrier);
731
732 pbd = GetBarrierDevice(barrier, *deviceid);
733
734 if (pbd->hit) {
735 BarrierEvent ev = {
736 .header = ET_Internal,
737 .type =ET_BarrierLeave,
738 .length = sizeof (BarrierEvent),
739 .time = ms,
740 .deviceid = *deviceid,
741 .sourceid = 0,
742 .dx = 0,
743 .dy = 0,
744 .root = barrier->screen->root->drawable.id,
745 .window = barrier->window,
746 .dt = ms - pbd->last_timestamp,
747 .flags = XIBarrierPointerReleased,
748 .event_id = pbd->barrier_event_id,
749 .barrierid = barrier->id,
750 };
751
752 mieqEnqueue(dev, (InternalEvent *) &ev);
753 }
754
755 xorg_list_del(&pbd->entry);
756 free(pbd);
757}
758
759void XIBarrierNewMasterDevice(ClientPtr client, int deviceid)
760{
761 FindClientResourcesByType(client, PointerBarrierType, add_master_func, &deviceid);
762}
763
764void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid)
765{
766 FindClientResourcesByType(client, PointerBarrierType, remove_master_func, &deviceid);
767}
768
769int
770XICreatePointerBarrier(ClientPtr client,
771 xXFixesCreatePointerBarrierReq * stuff)
772{
773 int err;
774 struct PointerBarrierClient *barrier;
775 struct PointerBarrier b;
776
777 b.x1 = stuff->x1;
778 b.x2 = stuff->x2;
779 b.y1 = stuff->y1;
780 b.y2 = stuff->y2;
781
782 if (!barrier_is_horizontal(&b) && !barrier_is_vertical(&b))
783 return BadValue;
784
785 /* no 0-sized barriers */
786 if (barrier_is_horizontal(&b) && barrier_is_vertical(&b))
787 return BadValue;
788
789 /* no infinite barriers on the wrong axis */
790 if (barrier_is_horizontal(&b) && (b.y1 < 0 || b.y2 < 0))
791 return BadValue;
792
793 if (barrier_is_vertical(&b) && (b.x1 < 0 || b.x2 < 0))
794 return BadValue;
795
796 if ((err = CreatePointerBarrierClient(client, stuff, &barrier)))
797 return err;
798
799 if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier))
800 return BadAlloc;
801
802 return Success;
803}
804
805int
806XIDestroyPointerBarrier(ClientPtr client,
807 xXFixesDestroyPointerBarrierReq * stuff)
808{
809 int err;
810 void *barrier;
811
812 err = dixLookupResourceByType((void **) &barrier, stuff->barrier,
813 PointerBarrierType, client, DixDestroyAccess);
814 if (err != Success) {
815 client->errorValue = stuff->barrier;
816 return err;
817 }
818
819 if (CLIENT_ID(stuff->barrier) != client->index)
820 return BadAccess;
821
822 FreeResource(stuff->barrier, RT_NONE);
823 return Success;
824}
825
826int
827SProcXIBarrierReleasePointer(ClientPtr client)
828{
829 xXIBarrierReleasePointerInfo *info;
830 REQUEST(xXIBarrierReleasePointerReq);
831 int i;
832
833 info = (xXIBarrierReleasePointerInfo*) &stuff[1];
834
835 swaps(&stuff->length);
836 swapl(&stuff->num_barriers);
837 for (i = 0; i < stuff->num_barriers; i++, info++) {
838 swaps(&info->deviceid);
839 swapl(&info->barrier);
840 swapl(&info->eventid);
841 }
842
843 return (ProcXIBarrierReleasePointer(client));
844}
845
846int
847ProcXIBarrierReleasePointer(ClientPtr client)
848{
849 int i;
850 int err;
851 struct PointerBarrierClient *barrier;
852 struct PointerBarrier *b;
853 xXIBarrierReleasePointerInfo *info;
854
855 REQUEST(xXIBarrierReleasePointerReq);
856 REQUEST_AT_LEAST_SIZE(xXIBarrierReleasePointerReq);
857
858 info = (xXIBarrierReleasePointerInfo*) &stuff[1];
859 for (i = 0; i < stuff->num_barriers; i++, info++) {
860 struct PointerBarrierDevice *pbd;
861 DeviceIntPtr dev;
862 CARD32 barrier_id, event_id;
863 _X_UNUSED CARD32 device_id;
864
865 barrier_id = info->barrier;
866 event_id = info->eventid;
867
868 err = dixLookupDevice(&dev, info->deviceid, client, DixReadAccess);
869 if (err != Success) {
870 client->errorValue = BadDevice;
871 return err;
872 }
873
874 err = dixLookupResourceByType((void **) &b, barrier_id,
875 PointerBarrierType, client, DixReadAccess);
876 if (err != Success) {
877 client->errorValue = barrier_id;
878 return err;
879 }
880
881 if (CLIENT_ID(barrier_id) != client->index)
882 return BadAccess;
883
884
885 barrier = container_of(b, struct PointerBarrierClient, barrier);
886
887 pbd = GetBarrierDevice(barrier, dev->id);
888
889 if (pbd->barrier_event_id == event_id)
890 pbd->release_event_id = event_id;
891 }
892
893 return Success;
894}
895
896Bool
897XIBarrierInit(void)
898{
899 int i;
900
901 if (!dixRegisterPrivateKey(&BarrierScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
902 return FALSE;
903
904 for (i = 0; i < screenInfo.numScreens; i++) {
905 ScreenPtr pScreen = screenInfo.screens[i];
906 BarrierScreenPtr cs;
907
908 cs = (BarrierScreenPtr) calloc(1, sizeof(BarrierScreenRec));
909 if (!cs)
910 return FALSE;
911 xorg_list_init(&cs->barriers);
912 SetBarrierScreen(pScreen, cs);
913 }
914
915 PointerBarrierType = CreateNewResourceType(BarrierFreeBarrier,
916 "XIPointerBarrier");
917
918 return PointerBarrierType;
919}
920
921void
922XIBarrierReset(void)
923{
924 int i;
925 for (i = 0; i < screenInfo.numScreens; i++) {
926 ScreenPtr pScreen = screenInfo.screens[i];
927 BarrierScreenPtr cs = GetBarrierScreen(pScreen);
928 free(cs);
929 SetBarrierScreen(pScreen, NULL);
930 }
931}