xref: /OK3568_Linux_fs/external/xserver/Xi/xibarriers.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 
62 RESTYPE PointerBarrierType;
63 
64 static DevPrivateKeyRec BarrierScreenPrivateKeyRec;
65 
66 #define BarrierScreenPrivateKey (&BarrierScreenPrivateKeyRec)
67 
68 typedef struct PointerBarrierClient *PointerBarrierClientPtr;
69 
70 struct 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 
80 struct 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 
94 typedef 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 
AllocBarrierDevice(void)102 static 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 
FreePointerBarrierClient(struct PointerBarrierClient * c)120 static 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 
GetBarrierDevice(struct PointerBarrierClient * c,int deviceid)130 static 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 
143 static BOOL
barrier_is_horizontal(const struct PointerBarrier * barrier)144 barrier_is_horizontal(const struct PointerBarrier *barrier)
145 {
146     return barrier->y1 == barrier->y2;
147 }
148 
149 static BOOL
barrier_is_vertical(const struct PointerBarrier * barrier)150 barrier_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/y1x2/y2 represents.
158  */
159 int
barrier_get_direction(int x1,int y1,int x2,int y2)160 barrier_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/y1x2/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  */
185 BOOL
barrier_is_blocking_direction(const struct PointerBarrier * barrier,int direction)186 barrier_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 
193 static BOOL
inside_segment(int v,int v1,int v2)194 inside_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/y1x2/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  */
222 BOOL
barrier_is_blocking(const struct PointerBarrier * barrier,int x1,int y1,int x2,int y2,double * distance)223 barrier_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
263 static BOOL
barrier_inside_hit_box(struct PointerBarrier * barrier,int x,int y)264 barrier_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 
291 static BOOL
barrier_blocks_device(struct PointerBarrierClient * client,DeviceIntPtr dev)292 barrier_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  */
326 static struct PointerBarrierClient *
barrier_find_nearest(BarrierScreenPtr cs,DeviceIntPtr dev,int dir,int x1,int y1,int x2,int y2)327 barrier_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  */
368 void
barrier_clamp_to_barrier(struct PointerBarrier * barrier,int dir,int * x,int * y)369 barrier_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 
386 void
input_constrain_cursor(DeviceIntPtr dev,ScreenPtr screen,int current_x,int current_y,int dest_x,int dest_y,int * out_x,int * out_y,int * nevents,InternalEvent * events)387 input_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 
526 static void
sort_min_max(INT16 * a,INT16 * b)527 sort_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 
538 static int
CreatePointerBarrierClient(ClientPtr client,xXFixesCreatePointerBarrierReq * stuff,PointerBarrierClientPtr * client_out)539 CreatePointerBarrierClient(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         input_lock();
615         xorg_list_add(&pbd->entry, &ret->per_device);
616         input_unlock();
617     }
618 
619     ret->id = stuff->barrier;
620     ret->barrier.x1 = stuff->x1;
621     ret->barrier.x2 = stuff->x2;
622     ret->barrier.y1 = stuff->y1;
623     ret->barrier.y2 = stuff->y2;
624     sort_min_max(&ret->barrier.x1, &ret->barrier.x2);
625     sort_min_max(&ret->barrier.y1, &ret->barrier.y2);
626     ret->barrier.directions = stuff->directions & 0x0f;
627     if (barrier_is_horizontal(&ret->barrier))
628         ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
629     if (barrier_is_vertical(&ret->barrier))
630         ret->barrier.directions &= ~(BarrierPositiveY | BarrierNegativeY);
631     input_lock();
632     xorg_list_add(&ret->entry, &cs->barriers);
633     input_unlock();
634 
635     *client_out = ret;
636     return Success;
637 
638  error:
639     *client_out = NULL;
640     FreePointerBarrierClient(ret);
641     return err;
642 }
643 
644 static int
BarrierFreeBarrier(void * data,XID id)645 BarrierFreeBarrier(void *data, XID id)
646 {
647     struct PointerBarrierClient *c;
648     Time ms = GetTimeInMillis();
649     DeviceIntPtr dev = NULL;
650     ScreenPtr screen;
651 
652     c = container_of(data, struct PointerBarrierClient, barrier);
653     screen = c->screen;
654 
655     for (dev = inputInfo.devices; dev; dev = dev->next) {
656         struct PointerBarrierDevice *pbd;
657         int root_x, root_y;
658         BarrierEvent ev = {
659             .header = ET_Internal,
660             .type = ET_BarrierLeave,
661             .length = sizeof (BarrierEvent),
662             .time = ms,
663             /* .deviceid */
664             .sourceid = 0,
665             .barrierid = c->id,
666             .window = c->window,
667             .root = screen->root->drawable.id,
668             .dx = 0,
669             .dy = 0,
670             /* .root_x */
671             /* .root_y */
672             /* .dt */
673             /* .event_id */
674             .flags = XIBarrierPointerReleased,
675         };
676 
677 
678         if (dev->type != MASTER_POINTER)
679             continue;
680 
681         pbd = GetBarrierDevice(c, dev->id);
682         if (!pbd->hit)
683             continue;
684 
685         ev.deviceid = dev->id;
686         ev.event_id = pbd->barrier_event_id;
687         ev.dt = ms - pbd->last_timestamp;
688 
689         GetSpritePosition(dev, &root_x, &root_y);
690         ev.root_x = root_x;
691         ev.root_y = root_y;
692 
693         mieqEnqueue(dev, (InternalEvent *) &ev);
694     }
695 
696     input_lock();
697     xorg_list_del(&c->entry);
698     input_unlock();
699 
700     FreePointerBarrierClient(c);
701     return Success;
702 }
703 
add_master_func(void * res,XID id,void * devid)704 static void add_master_func(void *res, XID id, void *devid)
705 {
706     struct PointerBarrier *b;
707     struct PointerBarrierClient *barrier;
708     struct PointerBarrierDevice *pbd;
709     int *deviceid = devid;
710 
711     b = res;
712     barrier = container_of(b, struct PointerBarrierClient, barrier);
713 
714 
715     pbd = AllocBarrierDevice();
716     pbd->deviceid = *deviceid;
717 
718     input_lock();
719     xorg_list_add(&pbd->entry, &barrier->per_device);
720     input_unlock();
721 }
722 
remove_master_func(void * res,XID id,void * devid)723 static void remove_master_func(void *res, XID id, void *devid)
724 {
725     struct PointerBarrierDevice *pbd;
726     struct PointerBarrierClient *barrier;
727     struct PointerBarrier *b;
728     DeviceIntPtr dev;
729     int *deviceid = devid;
730     int rc;
731     Time ms = GetTimeInMillis();
732 
733     rc = dixLookupDevice(&dev, *deviceid, serverClient, DixSendAccess);
734     if (rc != Success)
735         return;
736 
737     b = res;
738     barrier = container_of(b, struct PointerBarrierClient, barrier);
739 
740     pbd = GetBarrierDevice(barrier, *deviceid);
741 
742     if (pbd->hit) {
743         BarrierEvent ev = {
744             .header = ET_Internal,
745             .type =ET_BarrierLeave,
746             .length = sizeof (BarrierEvent),
747             .time = ms,
748             .deviceid = *deviceid,
749             .sourceid = 0,
750             .dx = 0,
751             .dy = 0,
752             .root = barrier->screen->root->drawable.id,
753             .window = barrier->window,
754             .dt = ms - pbd->last_timestamp,
755             .flags = XIBarrierPointerReleased,
756             .event_id = pbd->barrier_event_id,
757             .barrierid = barrier->id,
758         };
759 
760         mieqEnqueue(dev, (InternalEvent *) &ev);
761     }
762 
763     input_lock();
764     xorg_list_del(&pbd->entry);
765     input_unlock();
766     free(pbd);
767 }
768 
XIBarrierNewMasterDevice(ClientPtr client,int deviceid)769 void XIBarrierNewMasterDevice(ClientPtr client, int deviceid)
770 {
771     FindClientResourcesByType(client, PointerBarrierType, add_master_func, &deviceid);
772 }
773 
XIBarrierRemoveMasterDevice(ClientPtr client,int deviceid)774 void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid)
775 {
776     FindClientResourcesByType(client, PointerBarrierType, remove_master_func, &deviceid);
777 }
778 
779 int
XICreatePointerBarrier(ClientPtr client,xXFixesCreatePointerBarrierReq * stuff)780 XICreatePointerBarrier(ClientPtr client,
781                        xXFixesCreatePointerBarrierReq * stuff)
782 {
783     int err;
784     struct PointerBarrierClient *barrier;
785     struct PointerBarrier b;
786 
787     b.x1 = stuff->x1;
788     b.x2 = stuff->x2;
789     b.y1 = stuff->y1;
790     b.y2 = stuff->y2;
791 
792     if (!barrier_is_horizontal(&b) && !barrier_is_vertical(&b))
793         return BadValue;
794 
795     /* no 0-sized barriers */
796     if (barrier_is_horizontal(&b) && barrier_is_vertical(&b))
797         return BadValue;
798 
799     /* no infinite barriers on the wrong axis */
800     if (barrier_is_horizontal(&b) && (b.y1 < 0 || b.y2 < 0))
801         return BadValue;
802 
803     if (barrier_is_vertical(&b) && (b.x1 < 0 || b.x2 < 0))
804         return BadValue;
805 
806     if ((err = CreatePointerBarrierClient(client, stuff, &barrier)))
807         return err;
808 
809     if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier))
810         return BadAlloc;
811 
812     return Success;
813 }
814 
815 int
XIDestroyPointerBarrier(ClientPtr client,xXFixesDestroyPointerBarrierReq * stuff)816 XIDestroyPointerBarrier(ClientPtr client,
817                         xXFixesDestroyPointerBarrierReq * stuff)
818 {
819     int err;
820     void *barrier;
821 
822     err = dixLookupResourceByType((void **) &barrier, stuff->barrier,
823                                   PointerBarrierType, client, DixDestroyAccess);
824     if (err != Success) {
825         client->errorValue = stuff->barrier;
826         return err;
827     }
828 
829     if (CLIENT_ID(stuff->barrier) != client->index)
830         return BadAccess;
831 
832     FreeResource(stuff->barrier, RT_NONE);
833     return Success;
834 }
835 
836 int _X_COLD
SProcXIBarrierReleasePointer(ClientPtr client)837 SProcXIBarrierReleasePointer(ClientPtr client)
838 {
839     xXIBarrierReleasePointerInfo *info;
840     REQUEST(xXIBarrierReleasePointerReq);
841     int i;
842 
843     swaps(&stuff->length);
844     REQUEST_AT_LEAST_SIZE(xXIBarrierReleasePointerReq);
845 
846     swapl(&stuff->num_barriers);
847     if (stuff->num_barriers > UINT32_MAX / sizeof(xXIBarrierReleasePointerInfo))
848         return BadLength;
849     REQUEST_FIXED_SIZE(xXIBarrierReleasePointerReq, stuff->num_barriers * sizeof(xXIBarrierReleasePointerInfo));
850 
851     info = (xXIBarrierReleasePointerInfo*) &stuff[1];
852     for (i = 0; i < stuff->num_barriers; i++, info++) {
853         swaps(&info->deviceid);
854         swapl(&info->barrier);
855         swapl(&info->eventid);
856     }
857 
858     return (ProcXIBarrierReleasePointer(client));
859 }
860 
861 int
ProcXIBarrierReleasePointer(ClientPtr client)862 ProcXIBarrierReleasePointer(ClientPtr client)
863 {
864     int i;
865     int err;
866     struct PointerBarrierClient *barrier;
867     struct PointerBarrier *b;
868     xXIBarrierReleasePointerInfo *info;
869 
870     REQUEST(xXIBarrierReleasePointerReq);
871     REQUEST_AT_LEAST_SIZE(xXIBarrierReleasePointerReq);
872     if (stuff->num_barriers > UINT32_MAX / sizeof(xXIBarrierReleasePointerInfo))
873         return BadLength;
874     REQUEST_FIXED_SIZE(xXIBarrierReleasePointerReq, stuff->num_barriers * sizeof(xXIBarrierReleasePointerInfo));
875 
876     info = (xXIBarrierReleasePointerInfo*) &stuff[1];
877     for (i = 0; i < stuff->num_barriers; i++, info++) {
878         struct PointerBarrierDevice *pbd;
879         DeviceIntPtr dev;
880         CARD32 barrier_id, event_id;
881         _X_UNUSED CARD32 device_id;
882 
883         barrier_id = info->barrier;
884         event_id = info->eventid;
885 
886         err = dixLookupDevice(&dev, info->deviceid, client, DixReadAccess);
887         if (err != Success) {
888             client->errorValue = BadDevice;
889             return err;
890         }
891 
892         err = dixLookupResourceByType((void **) &b, barrier_id,
893                                       PointerBarrierType, client, DixReadAccess);
894         if (err != Success) {
895             client->errorValue = barrier_id;
896             return err;
897         }
898 
899         if (CLIENT_ID(barrier_id) != client->index)
900             return BadAccess;
901 
902 
903         barrier = container_of(b, struct PointerBarrierClient, barrier);
904 
905         pbd = GetBarrierDevice(barrier, dev->id);
906 
907         if (pbd->barrier_event_id == event_id)
908             pbd->release_event_id = event_id;
909     }
910 
911     return Success;
912 }
913 
914 Bool
XIBarrierInit(void)915 XIBarrierInit(void)
916 {
917     int i;
918 
919     if (!dixRegisterPrivateKey(&BarrierScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
920         return FALSE;
921 
922     for (i = 0; i < screenInfo.numScreens; i++) {
923         ScreenPtr pScreen = screenInfo.screens[i];
924         BarrierScreenPtr cs;
925 
926         cs = (BarrierScreenPtr) calloc(1, sizeof(BarrierScreenRec));
927         if (!cs)
928             return FALSE;
929         xorg_list_init(&cs->barriers);
930         SetBarrierScreen(pScreen, cs);
931     }
932 
933     PointerBarrierType = CreateNewResourceType(BarrierFreeBarrier,
934                                                "XIPointerBarrier");
935 
936     return PointerBarrierType;
937 }
938 
939 void
XIBarrierReset(void)940 XIBarrierReset(void)
941 {
942     int i;
943     for (i = 0; i < screenInfo.numScreens; i++) {
944         ScreenPtr pScreen = screenInfo.screens[i];
945         BarrierScreenPtr cs = GetBarrierScreen(pScreen);
946         free(cs);
947         SetBarrierScreen(pScreen, NULL);
948     }
949 }
950