xibarriers.c revision bf8e669c
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/y1x2/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/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 */
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/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 */
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        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
644static int
645BarrierFreeBarrier(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
704static 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
723static 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
769void XIBarrierNewMasterDevice(ClientPtr client, int deviceid)
770{
771    FindClientResourcesByType(client, PointerBarrierType, add_master_func, &deviceid);
772}
773
774void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid)
775{
776    FindClientResourcesByType(client, PointerBarrierType, remove_master_func, &deviceid);
777}
778
779int
780XICreatePointerBarrier(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
815int
816XIDestroyPointerBarrier(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
836int _X_COLD
837SProcXIBarrierReleasePointer(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
861int
862ProcXIBarrierReleasePointer(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
914Bool
915XIBarrierInit(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
939void
940XIBarrierReset(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