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/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 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