enterleave.c revision 706f2543
1/* 2 * Copyright © 2008 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 * Authors: Peter Hutterer 24 * 25 */ 26 27#ifdef HAVE_DIX_CONFIG_H 28#include <dix-config.h> 29#endif 30 31#include <X11/X.h> 32#include <X11/extensions/XI2.h> 33#include "inputstr.h" 34#include "windowstr.h" 35#include "scrnintstr.h" 36#include "exglobals.h" 37#include "enterleave.h" 38 39/** 40 * @file 41 * This file describes the model for sending core enter/leave events and 42 * focus in/out in the case of multiple pointers/keyboard foci. 43 * 44 * Since we can't send more than one Enter or Leave/Focus in or out event per 45 * window to a core client without confusing it, this is a rather complicated 46 * approach. 47 * 48 * For a full description of the enter/leave model from a window's 49 * perspective, see 50 * http://lists.freedesktop.org/archives/xorg/2008-August/037606.html 51 * 52 * For a full description of the focus in/out model from a window's 53 * perspective, see 54 * http://lists.freedesktop.org/archives/xorg/2008-December/041740.html 55 * 56 * Additional notes: 57 * - The core protocol spec says that "In a LeaveNotify event, if a child of the 58 * event window contains the initial position of the pointer, then the child 59 * component is set to that child. Otherwise, it is None. For an EnterNotify 60 * event, if a child of the event window contains the final pointer position, 61 * then the child component is set to that child. Otherwise, it is None." 62 * 63 * By inference, this means that only NotifyVirtual or NotifyNonlinearVirtual 64 * events may have a subwindow set to other than None. 65 * 66 * - NotifyPointer events may be sent if the focus changes from window A to 67 * B. The assumption used in this model is that NotifyPointer events are only 68 * sent for the pointer paired with the keyboard that is involved in the focus 69 * events. For example, if F(W) changes because of keyboard 2, then 70 * NotifyPointer events are only sent for pointer 2. 71 */ 72 73static WindowPtr PointerWindows[MAXDEVICES]; 74static WindowPtr FocusWindows[MAXDEVICES]; 75 76/** 77 * Return TRUE if 'win' has a pointer within its boundaries, excluding child 78 * window. 79 */ 80static BOOL 81HasPointer(DeviceIntPtr dev, WindowPtr win) 82{ 83 int i; 84 85 /* FIXME: The enter/leave model does not cater for grabbed devices. For 86 * now, a quickfix: if the device about to send an enter/leave event to 87 * a window is grabbed, assume there is no pointer in that window. 88 * Fixes fdo 27804. 89 * There isn't enough beer in my fridge to fix this properly. 90 */ 91 if (dev->deviceGrab.grab) 92 return FALSE; 93 94 for (i = 0; i < MAXDEVICES; i++) 95 if (PointerWindows[i] == win) 96 return TRUE; 97 98 return FALSE; 99} 100 101/** 102 * Return TRUE if at least one keyboard focus is set to 'win' (excluding 103 * descendants of win). 104 */ 105static BOOL 106HasFocus(WindowPtr win) 107{ 108 int i; 109 for (i = 0; i < MAXDEVICES; i++) 110 if (FocusWindows[i] == win) 111 return TRUE; 112 113 return FALSE; 114} 115 116/** 117 * Return the window the device dev is currently on. 118 */ 119static WindowPtr 120PointerWin(DeviceIntPtr dev) 121{ 122 return PointerWindows[dev->id]; 123} 124 125/** 126 * Search for the first window below 'win' that has a pointer directly within 127 * it's boundaries (excluding boundaries of its own descendants). 128 * 129 * @return The child window that has the pointer within its boundaries or 130 * NULL. 131 */ 132static WindowPtr 133FirstPointerChild(WindowPtr win) 134{ 135 int i; 136 for (i = 0; i < MAXDEVICES; i++) 137 { 138 if (PointerWindows[i] && IsParent(win, PointerWindows[i])) 139 return PointerWindows[i]; 140 } 141 142 return NULL; 143} 144 145/** 146 * Search for the first window below 'win' that has a focus directly within 147 * it's boundaries (excluding boundaries of its own descendants). 148 * 149 * @return The child window that has the pointer within its boundaries or 150 * NULL. 151 */ 152static WindowPtr 153FirstFocusChild(WindowPtr win) 154{ 155 int i; 156 for (i = 0; i < MAXDEVICES; i++) 157 { 158 if (FocusWindows[i] && FocusWindows[i] != PointerRootWin && 159 IsParent(win, FocusWindows[i])) 160 return FocusWindows[i]; 161 } 162 163 return NULL; 164} 165 166/** 167 * Set the presence flag for dev to mark that it is now in 'win'. 168 */ 169void 170EnterWindow(DeviceIntPtr dev, WindowPtr win, int mode) 171{ 172 PointerWindows[dev->id] = win; 173} 174 175/** 176 * Unset the presence flag for dev to mark that it is not in 'win' anymore. 177 */ 178void 179LeaveWindow(DeviceIntPtr dev) 180{ 181 PointerWindows[dev->id] = NULL; 182} 183 184/** 185 * Set the presence flag for dev to mark that it is now in 'win'. 186 */ 187void 188SetFocusIn(DeviceIntPtr dev, WindowPtr win) 189{ 190 FocusWindows[dev->id] = win; 191} 192 193/** 194 * Unset the presence flag for dev to mark that it is not in 'win' anymore. 195 */ 196void 197SetFocusOut(DeviceIntPtr dev) 198{ 199 FocusWindows[dev->id] = NULL; 200} 201 202 203 204 205/** 206 * Return the common ancestor of 'a' and 'b' (if one exists). 207 * @param a A window with the same ancestor as b. 208 * @param b A window with the same ancestor as a. 209 * @return The window that is the first ancestor of both 'a' and 'b', or the 210 * NullWindow if they do not have a common ancestor. 211 */ 212WindowPtr 213CommonAncestor( 214 WindowPtr a, 215 WindowPtr b) 216{ 217 for (b = b->parent; b; b = b->parent) 218 if (IsParent(b, a)) return b; 219 return NullWindow; 220} 221 222 223/** 224 * Send enter notifies to all windows between 'ancestor' and 'child' (excluding 225 * both). Events are sent running up the window hierarchy. This function 226 * recurses. 227 */ 228static void 229DeviceEnterNotifies(DeviceIntPtr dev, 230 int sourceid, 231 WindowPtr ancestor, 232 WindowPtr child, 233 int mode, 234 int detail) 235{ 236 WindowPtr parent = child->parent; 237 238 if (ancestor == parent) 239 return; 240 DeviceEnterNotifies(dev, sourceid, ancestor, parent, mode, detail); 241 DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, detail, parent, 242 child->drawable.id); 243} 244 245/** 246 * Send enter notifies to all windows between 'ancestor' and 'child' (excluding 247 * both). Events are sent running down the window hierarchy. This function 248 * recurses. 249 */ 250static void 251CoreEnterNotifies(DeviceIntPtr dev, 252 WindowPtr ancestor, 253 WindowPtr child, 254 int mode, 255 int detail) 256{ 257 WindowPtr parent = child->parent; 258 if (ancestor == parent) 259 return; 260 CoreEnterNotifies(dev, ancestor, parent, mode, detail); 261 262 263 /* Case 3: 264 A is above W, B is a descendant 265 266 Classically: The move generates an EnterNotify on W with a detail of 267 Virtual or NonlinearVirtual 268 269 MPX: 270 Case 3A: There is at least one other pointer on W itself 271 P(W) doesn't change, so the event should be suppressed 272 Case 3B: Otherwise, if there is at least one other pointer in a 273 descendant 274 P(W) stays on the same descendant, or changes to a different 275 descendant. The event should be suppressed. 276 Case 3C: Otherwise: 277 P(W) moves from a window above W to a descendant. The subwindow 278 field is set to the child containing the descendant. The detail 279 may need to be changed from Virtual to NonlinearVirtual depending 280 on the previous P(W). */ 281 282 if (!HasPointer(dev, parent) && !FirstPointerChild(parent)) 283 CoreEnterLeaveEvent(dev, EnterNotify, mode, detail, parent, 284 child->drawable.id); 285} 286 287static void 288CoreLeaveNotifies(DeviceIntPtr dev, 289 WindowPtr child, 290 WindowPtr ancestor, 291 int mode, 292 int detail) 293{ 294 WindowPtr win; 295 296 if (ancestor == child) 297 return; 298 299 for (win = child->parent; win != ancestor; win = win->parent) 300 { 301 /*Case 7: 302 A is a descendant of W, B is above W 303 304 Classically: A LeaveNotify is generated on W with a detail of Virtual 305 or NonlinearVirtual. 306 307 MPX: 308 Case 3A: There is at least one other pointer on W itself 309 P(W) doesn't change, the event should be suppressed. 310 Case 3B: Otherwise, if there is at least one other pointer in a 311 descendant 312 P(W) stays on the same descendant, or changes to a different 313 descendant. The event should be suppressed. 314 Case 3C: Otherwise: 315 P(W) changes from the descendant of W to a window above W. 316 The detail may need to be changed from Virtual to NonlinearVirtual 317 or vice-versa depending on the new P(W).*/ 318 319 /* If one window has a pointer or a child with a pointer, skip some 320 * work and exit. */ 321 if (HasPointer(dev, win) || FirstPointerChild(win)) 322 return; 323 324 CoreEnterLeaveEvent(dev, LeaveNotify, mode, detail, win, child->drawable.id); 325 326 child = win; 327 } 328} 329 330/** 331 * Send leave notifies to all windows between 'child' and 'ancestor'. 332 * Events are sent running up the hierarchy. 333 */ 334static void 335DeviceLeaveNotifies(DeviceIntPtr dev, 336 int sourceid, 337 WindowPtr child, 338 WindowPtr ancestor, 339 int mode, 340 int detail) 341{ 342 WindowPtr win; 343 344 if (ancestor == child) 345 return; 346 for (win = child->parent; win != ancestor; win = win->parent) 347 { 348 DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, detail, win, 349 child->drawable.id); 350 child = win; 351 } 352} 353 354/** 355 * Pointer dev moves from A to B and A neither a descendant of B nor is 356 * B a descendant of A. 357 */ 358static void 359CoreEnterLeaveNonLinear(DeviceIntPtr dev, 360 WindowPtr A, 361 WindowPtr B, 362 int mode) 363{ 364 WindowPtr X = CommonAncestor(A, B); 365 /* Case 4: 366 A is W, B is above W 367 368 Classically: The move generates a LeaveNotify on W with a detail of 369 Ancestor or Nonlinear 370 371 MPX: 372 Case 3A: There is at least one other pointer on W itself 373 P(W) doesn't change, the event should be suppressed 374 Case 3B: Otherwise, if there is at least one other pointer in a 375 descendant of W 376 P(W) changes from W to a descendant of W. The subwindow field 377 is set to the child containing the new P(W), the detail field 378 is set to Inferior 379 Case 3C: Otherwise: 380 The pointer window moves from W to a window above W. 381 The detail may need to be changed from Ancestor to Nonlinear or 382 vice versa depending on the the new P(W) 383 */ 384 385 if (!HasPointer(dev, A)) 386 { 387 WindowPtr child = FirstPointerChild(A); 388 if (child) 389 CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A, None); 390 else 391 CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyNonlinear, A, None); 392 } 393 394 395 CoreLeaveNotifies(dev, A, X, mode, NotifyNonlinearVirtual); 396 397 /* 398 Case 9: 399 A is a descendant of W, B is a descendant of W 400 401 Classically: No events are generated on W 402 MPX: The pointer window stays the same or moves to a different 403 descendant of W. No events should be generated on W. 404 405 406 Therefore, no event to X. 407 */ 408 409 CoreEnterNotifies(dev, X, B, mode, NotifyNonlinearVirtual); 410 411 /* Case 2: 412 A is above W, B=W 413 414 Classically: The move generates an EnterNotify on W with a detail of 415 Ancestor or Nonlinear 416 417 MPX: 418 Case 2A: There is at least one other pointer on W itself 419 P(W) doesn't change, so the event should be suppressed 420 Case 2B: Otherwise, if there is at least one other pointer in a 421 descendant 422 P(W) moves from a descendant to W. detail is changed to Inferior, 423 subwindow is set to the child containing the previous P(W) 424 Case 2C: Otherwise: 425 P(W) changes from a window above W to W itself. 426 The detail may need to be changed from Ancestor to Nonlinear 427 or vice-versa depending on the previous P(W). */ 428 429 if (!HasPointer(dev, B)) 430 { 431 WindowPtr child = FirstPointerChild(B); 432 if (child) 433 CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B, None); 434 else 435 CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyNonlinear, B, None); 436 } 437} 438 439/** 440 * Pointer dev moves from A to B and A is a descendant of B. 441 */ 442static void 443CoreEnterLeaveToAncestor(DeviceIntPtr dev, 444 WindowPtr A, 445 WindowPtr B, 446 int mode) 447{ 448 /* Case 4: 449 A is W, B is above W 450 451 Classically: The move generates a LeaveNotify on W with a detail of 452 Ancestor or Nonlinear 453 454 MPX: 455 Case 3A: There is at least one other pointer on W itself 456 P(W) doesn't change, the event should be suppressed 457 Case 3B: Otherwise, if there is at least one other pointer in a 458 descendant of W 459 P(W) changes from W to a descendant of W. The subwindow field 460 is set to the child containing the new P(W), the detail field 461 is set to Inferior 462 Case 3C: Otherwise: 463 The pointer window moves from W to a window above W. 464 The detail may need to be changed from Ancestor to Nonlinear or 465 vice versa depending on the the new P(W) 466 */ 467 if (!HasPointer(dev, A)) 468 { 469 WindowPtr child = FirstPointerChild(A); 470 if (child) 471 CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A, None); 472 else 473 CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyAncestor, A, None); 474 } 475 476 CoreLeaveNotifies(dev, A, B, mode, NotifyVirtual); 477 478 /* Case 8: 479 A is a descendant of W, B is W 480 481 Classically: A EnterNotify is generated on W with a detail of 482 NotifyInferior 483 484 MPX: 485 Case 3A: There is at least one other pointer on W itself 486 P(W) doesn't change, the event should be suppressed 487 Case 3B: Otherwise: 488 P(W) changes from a descendant to W itself. The subwindow 489 field should be set to the child containing the old P(W) <<< WRONG */ 490 491 if (!HasPointer(dev, B)) 492 CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B, None); 493 494} 495 496 497/** 498 * Pointer dev moves from A to B and B is a descendant of A. 499 */ 500static void 501CoreEnterLeaveToDescendant(DeviceIntPtr dev, 502 WindowPtr A, 503 WindowPtr B, 504 int mode) 505{ 506 /* Case 6: 507 A is W, B is a descendant of W 508 509 Classically: A LeaveNotify is generated on W with a detail of 510 NotifyInferior 511 512 MPX: 513 Case 3A: There is at least one other pointer on W itself 514 P(W) doesn't change, the event should be suppressed 515 Case 3B: Otherwise: 516 P(W) changes from W to a descendant of W. The subwindow field 517 is set to the child containing the new P(W) <<< THIS IS WRONG */ 518 519 if (!HasPointer(dev, A)) 520 CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A, None); 521 522 523 CoreEnterNotifies(dev, A, B, mode, NotifyVirtual); 524 525 /* Case 2: 526 A is above W, B=W 527 528 Classically: The move generates an EnterNotify on W with a detail of 529 Ancestor or Nonlinear 530 531 MPX: 532 Case 2A: There is at least one other pointer on W itself 533 P(W) doesn't change, so the event should be suppressed 534 Case 2B: Otherwise, if there is at least one other pointer in a 535 descendant 536 P(W) moves from a descendant to W. detail is changed to Inferior, 537 subwindow is set to the child containing the previous P(W) 538 Case 2C: Otherwise: 539 P(W) changes from a window above W to W itself. 540 The detail may need to be changed from Ancestor to Nonlinear 541 or vice-versa depending on the previous P(W). */ 542 543 if (!HasPointer(dev, B)) 544 { 545 WindowPtr child = FirstPointerChild(B); 546 if (child) 547 CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B, None); 548 else 549 CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyAncestor, B, None); 550 } 551} 552 553static void 554CoreEnterLeaveEvents(DeviceIntPtr dev, 555 WindowPtr from, 556 WindowPtr to, 557 int mode) 558{ 559 if (!IsMaster(dev)) 560 return; 561 562 LeaveWindow(dev); 563 564 if (IsParent(from, to)) 565 CoreEnterLeaveToDescendant(dev, from, to, mode); 566 else if (IsParent(to, from)) 567 CoreEnterLeaveToAncestor(dev, from, to, mode); 568 else 569 CoreEnterLeaveNonLinear(dev, from, to, mode); 570 571 EnterWindow(dev, to, mode); 572} 573 574static void 575DeviceEnterLeaveEvents(DeviceIntPtr dev, 576 int sourceid, 577 WindowPtr from, 578 WindowPtr to, 579 int mode) 580{ 581 if (IsParent(from, to)) 582 { 583 DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyInferior, from, None); 584 DeviceEnterNotifies(dev, sourceid, from, to, mode, NotifyVirtual); 585 DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyAncestor, to, None); 586 } 587 else if (IsParent(to, from)) 588 { 589 DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyAncestor, from, None); 590 DeviceLeaveNotifies(dev, sourceid, from, to, mode, NotifyVirtual); 591 DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyInferior, to, None); 592 } 593 else 594 { /* neither from nor to is descendent of the other */ 595 WindowPtr common = CommonAncestor(to, from); 596 /* common == NullWindow ==> different screens */ 597 DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyNonlinear, from, None); 598 DeviceLeaveNotifies(dev, sourceid, from, common, mode, NotifyNonlinearVirtual); 599 DeviceEnterNotifies(dev, sourceid, common, to, mode, NotifyNonlinearVirtual); 600 DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyNonlinear, to, None); 601 } 602} 603 604/** 605 * Figure out if enter/leave events are necessary and send them to the 606 * appropriate windows. 607 * 608 * @param fromWin Window the sprite moved out of. 609 * @param toWin Window the sprite moved into. 610 */ 611void 612DoEnterLeaveEvents(DeviceIntPtr pDev, 613 int sourceid, 614 WindowPtr fromWin, 615 WindowPtr toWin, 616 int mode) 617{ 618 if (!IsPointerDevice(pDev)) 619 return; 620 621 if (fromWin == toWin) 622 return; 623 624 if (mode != XINotifyPassiveGrab && mode != XINotifyPassiveUngrab) 625 CoreEnterLeaveEvents(pDev, fromWin, toWin, mode); 626 DeviceEnterLeaveEvents(pDev, sourceid, fromWin, toWin, mode); 627} 628 629/** 630 * Send focus out events to all windows between 'child' and 'ancestor'. 631 * Events are sent running up the hierarchy. 632 */ 633static void 634DeviceFocusOutEvents(DeviceIntPtr dev, 635 WindowPtr child, 636 WindowPtr ancestor, 637 int mode, 638 int detail) 639{ 640 WindowPtr win; 641 642 if (ancestor == child) 643 return; 644 for (win = child->parent; win != ancestor; win = win->parent) 645 DeviceFocusEvent(dev, XI_FocusOut, mode, detail, win); 646} 647 648 649/** 650 * Send enter notifies to all windows between 'ancestor' and 'child' (excluding 651 * both). Events are sent running up the window hierarchy. This function 652 * recurses. 653 */ 654static void 655DeviceFocusInEvents(DeviceIntPtr dev, 656 WindowPtr ancestor, 657 WindowPtr child, 658 int mode, 659 int detail) 660{ 661 WindowPtr parent = child->parent; 662 663 if (ancestor == parent || !parent) 664 return; 665 DeviceFocusInEvents(dev, ancestor, parent, mode, detail); 666 DeviceFocusEvent(dev, XI_FocusIn, mode, detail, parent); 667} 668 669/** 670 * Send FocusIn events to all windows between 'ancestor' and 'child' (excluding 671 * both). Events are sent running down the window hierarchy. This function 672 * recurses. 673 */ 674static void 675CoreFocusInEvents(DeviceIntPtr dev, 676 WindowPtr ancestor, 677 WindowPtr child, 678 int mode, 679 int detail) 680{ 681 WindowPtr parent = child->parent; 682 if (ancestor == parent) 683 return; 684 CoreFocusInEvents(dev, ancestor, parent, mode, detail); 685 686 687 /* Case 3: 688 A is above W, B is a descendant 689 690 Classically: The move generates an FocusIn on W with a detail of 691 Virtual or NonlinearVirtual 692 693 MPX: 694 Case 3A: There is at least one other focus on W itself 695 F(W) doesn't change, so the event should be suppressed 696 Case 3B: Otherwise, if there is at least one other focus in a 697 descendant 698 F(W) stays on the same descendant, or changes to a different 699 descendant. The event should be suppressed. 700 Case 3C: Otherwise: 701 F(W) moves from a window above W to a descendant. The detail may 702 need to be changed from Virtual to NonlinearVirtual depending 703 on the previous F(W). */ 704 705 if (!HasFocus(parent) && !FirstFocusChild(parent)) 706 CoreFocusEvent(dev, FocusIn, mode, detail, parent); 707} 708 709static void 710CoreFocusOutEvents(DeviceIntPtr dev, 711 WindowPtr child, 712 WindowPtr ancestor, 713 int mode, 714 int detail) 715{ 716 WindowPtr win; 717 718 if (ancestor == child) 719 return; 720 721 for (win = child->parent; win != ancestor; win = win->parent) 722 { 723 /*Case 7: 724 A is a descendant of W, B is above W 725 726 Classically: A FocusOut is generated on W with a detail of Virtual 727 or NonlinearVirtual. 728 729 MPX: 730 Case 3A: There is at least one other focus on W itself 731 F(W) doesn't change, the event should be suppressed. 732 Case 3B: Otherwise, if there is at least one other focus in a 733 descendant 734 F(W) stays on the same descendant, or changes to a different 735 descendant. The event should be suppressed. 736 Case 3C: Otherwise: 737 F(W) changes from the descendant of W to a window above W. 738 The detail may need to be changed from Virtual to NonlinearVirtual 739 or vice-versa depending on the new P(W).*/ 740 741 /* If one window has a focus or a child with a focuspointer, skip some 742 * work and exit. */ 743 if (HasFocus(win) || FirstFocusChild(win)) 744 return; 745 746 CoreFocusEvent(dev, FocusOut, mode, detail, win); 747 } 748} 749 750/** 751 * Send FocusOut(NotifyPointer) events from the current pointer window (which 752 * is a descendant of pwin_parent) up to (excluding) pwin_parent. 753 * 754 * NotifyPointer events are only sent for the device paired with dev. 755 * 756 * If the current pointer window is a descendant of 'exclude' or an ancestor of 757 * 'exclude', no events are sent. If the current pointer IS 'exclude', events 758 * are sent! 759 */ 760static void 761CoreFocusOutNotifyPointerEvents(DeviceIntPtr dev, 762 WindowPtr pwin_parent, 763 WindowPtr exclude, 764 int mode, 765 int inclusive) 766{ 767 WindowPtr P, stopAt; 768 769 P = PointerWin(GetPairedDevice(dev)); 770 771 if (!P) 772 return; 773 if (!IsParent(pwin_parent, P)) 774 if (!(pwin_parent == P && inclusive)) 775 return; 776 777 if (exclude != None && exclude != PointerRootWin && 778 (IsParent(exclude, P) || IsParent(P, exclude))) 779 return; 780 781 stopAt = (inclusive) ? pwin_parent->parent : pwin_parent; 782 783 for (; P && P != stopAt; P = P->parent) 784 CoreFocusEvent(dev, FocusOut, mode, NotifyPointer, P); 785} 786 787/** 788 * DO NOT CALL DIRECTLY. 789 * Recursion helper for CoreFocusInNotifyPointerEvents. 790 */ 791static void 792CoreFocusInRecurse(DeviceIntPtr dev, 793 WindowPtr win, 794 WindowPtr stopAt, 795 int mode, 796 int inclusive) 797{ 798 if ((!inclusive && win == stopAt) || !win) 799 return; 800 801 CoreFocusInRecurse(dev, win->parent, stopAt, mode, inclusive); 802 CoreFocusEvent(dev, FocusIn, mode, NotifyPointer, win); 803} 804 805 806/** 807 * Send FocusIn(NotifyPointer) events from pwin_parent down to 808 * including the current pointer window (which is a descendant of pwin_parent). 809 * 810 * @param pwin The pointer window. 811 * @param exclude If the pointer window is a child of 'exclude', no events are 812 * sent. 813 * @param inclusive If TRUE, pwin_parent will receive the event too. 814 */ 815static void 816CoreFocusInNotifyPointerEvents(DeviceIntPtr dev, 817 WindowPtr pwin_parent, 818 WindowPtr exclude, 819 int mode, 820 int inclusive) 821{ 822 WindowPtr P; 823 824 P = PointerWin(GetPairedDevice(dev)); 825 826 if (!P || P == exclude || (pwin_parent != P && !IsParent(pwin_parent, P))) 827 return; 828 829 if (exclude != None && (IsParent(exclude, P) || IsParent(P, exclude))) 830 return; 831 832 CoreFocusInRecurse(dev, P, pwin_parent, mode, inclusive); 833} 834 835 836/** 837 * Focus of dev moves from A to B and A neither a descendant of B nor is 838 * B a descendant of A. 839 */ 840static void 841CoreFocusNonLinear(DeviceIntPtr dev, 842 WindowPtr A, 843 WindowPtr B, 844 int mode) 845{ 846 WindowPtr X = CommonAncestor(A, B); 847 848 /* Case 4: 849 A is W, B is above W 850 851 Classically: The change generates a FocusOut on W with a detail of 852 Ancestor or Nonlinear 853 854 MPX: 855 Case 3A: There is at least one other focus on W itself 856 F(W) doesn't change, the event should be suppressed 857 Case 3B: Otherwise, if there is at least one other focus in a 858 descendant of W 859 F(W) changes from W to a descendant of W. The detail field 860 is set to Inferior 861 Case 3C: Otherwise: 862 The focus window moves from W to a window above W. 863 The detail may need to be changed from Ancestor to Nonlinear or 864 vice versa depending on the the new F(W) 865 */ 866 867 if (!HasFocus(A)) 868 { 869 WindowPtr child = FirstFocusChild(A); 870 if (child) 871 { 872 /* NotifyPointer P-A unless P is child or below*/ 873 CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE); 874 CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A); 875 } else 876 { 877 /* NotifyPointer P-A */ 878 CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE); 879 CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A); 880 } 881 } 882 883 884 CoreFocusOutEvents(dev, A, X, mode, NotifyNonlinearVirtual); 885 886 /* 887 Case 9: 888 A is a descendant of W, B is a descendant of W 889 890 Classically: No events are generated on W 891 MPX: The focus window stays the same or moves to a different 892 descendant of W. No events should be generated on W. 893 894 895 Therefore, no event to X. 896 */ 897 898 CoreFocusInEvents(dev, X, B, mode, NotifyNonlinearVirtual); 899 900 /* Case 2: 901 A is above W, B=W 902 903 Classically: The move generates an EnterNotify on W with a detail of 904 Ancestor or Nonlinear 905 906 MPX: 907 Case 2A: There is at least one other focus on W itself 908 F(W) doesn't change, so the event should be suppressed 909 Case 2B: Otherwise, if there is at least one other focus in a 910 descendant 911 F(W) moves from a descendant to W. detail is changed to Inferior. 912 Case 2C: Otherwise: 913 F(W) changes from a window above W to W itself. 914 The detail may need to be changed from Ancestor to Nonlinear 915 or vice-versa depending on the previous F(W). */ 916 917 if (!HasFocus(B)) 918 { 919 WindowPtr child = FirstFocusChild(B); 920 if (child) 921 { 922 CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B); 923 /* NotifyPointer B-P unless P is child or below. */ 924 CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE); 925 } else { 926 CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B); 927 /* NotifyPointer B-P unless P is child or below. */ 928 CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE); 929 } 930 } 931} 932 933 934/** 935 * Focus of dev moves from A to B and A is a descendant of B. 936 */ 937static void 938CoreFocusToAncestor(DeviceIntPtr dev, 939 WindowPtr A, 940 WindowPtr B, 941 int mode) 942{ 943 /* Case 4: 944 A is W, B is above W 945 946 Classically: The change generates a FocusOut on W with a detail of 947 Ancestor or Nonlinear 948 949 MPX: 950 Case 3A: There is at least one other focus on W itself 951 F(W) doesn't change, the event should be suppressed 952 Case 3B: Otherwise, if there is at least one other focus in a 953 descendant of W 954 F(W) changes from W to a descendant of W. The detail field 955 is set to Inferior 956 Case 3C: Otherwise: 957 The focus window moves from W to a window above W. 958 The detail may need to be changed from Ancestor to Nonlinear or 959 vice versa depending on the the new F(W) 960 */ 961 if (!HasFocus(A)) 962 { 963 WindowPtr child = FirstFocusChild(A); 964 if (child) 965 { 966 /* NotifyPointer P-A unless P is child or below*/ 967 CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE); 968 CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A); 969 } else 970 CoreFocusEvent(dev, FocusOut, mode, NotifyAncestor, A); 971 } 972 973 CoreFocusOutEvents(dev, A, B, mode, NotifyVirtual); 974 975 /* Case 8: 976 A is a descendant of W, B is W 977 978 Classically: A FocusOut is generated on W with a detail of 979 NotifyInferior 980 981 MPX: 982 Case 3A: There is at least one other focus on W itself 983 F(W) doesn't change, the event should be suppressed 984 Case 3B: Otherwise: 985 F(W) changes from a descendant to W itself. */ 986 987 if (!HasFocus(B)) 988 { 989 CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B); 990 /* NotifyPointer B-P unless P is A or below. */ 991 CoreFocusInNotifyPointerEvents(dev, B, A, mode, FALSE); 992 } 993} 994 995/** 996 * Focus of dev moves from A to B and B is a descendant of A. 997 */ 998static void 999CoreFocusToDescendant(DeviceIntPtr dev, 1000 WindowPtr A, 1001 WindowPtr B, 1002 int mode) 1003{ 1004 /* Case 6: 1005 A is W, B is a descendant of W 1006 1007 Classically: A FocusOut is generated on W with a detail of 1008 NotifyInferior 1009 1010 MPX: 1011 Case 3A: There is at least one other focus on W itself 1012 F(W) doesn't change, the event should be suppressed 1013 Case 3B: Otherwise: 1014 F(W) changes from W to a descendant of W. */ 1015 1016 if (!HasFocus(A)) 1017 { 1018 /* NotifyPointer P-A unless P is B or below*/ 1019 CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE); 1020 CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A); 1021 } 1022 1023 1024 CoreFocusInEvents(dev, A, B, mode, NotifyVirtual); 1025 1026 /* Case 2: 1027 A is above W, B=W 1028 1029 Classically: The move generates an FocusIn on W with a detail of 1030 Ancestor or Nonlinear 1031 1032 MPX: 1033 Case 2A: There is at least one other focus on W itself 1034 F(W) doesn't change, so the event should be suppressed 1035 Case 2B: Otherwise, if there is at least one other focus in a 1036 descendant 1037 F(W) moves from a descendant to W. detail is changed to Inferior. 1038 Case 2C: Otherwise: 1039 F(W) changes from a window above W to W itself. 1040 The detail may need to be changed from Ancestor to Nonlinear 1041 or vice-versa depending on the previous F(W). */ 1042 1043 if (!HasFocus(B)) 1044 { 1045 WindowPtr child = FirstFocusChild(B); 1046 if (child) 1047 { 1048 CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B); 1049 /* NotifyPointer B-P unless P is child or below. */ 1050 CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE); 1051 } else 1052 CoreFocusEvent(dev, FocusIn, mode, NotifyAncestor, B); 1053 } 1054} 1055 1056static BOOL 1057HasOtherPointer(WindowPtr win, DeviceIntPtr exclude) 1058{ 1059 int i; 1060 1061 for (i = 0; i < MAXDEVICES; i++) 1062 if (i != exclude->id && PointerWindows[i] == win) 1063 return TRUE; 1064 1065 return FALSE; 1066} 1067 1068/** 1069 * Focus moves from PointerRoot to None or from None to PointerRoot. 1070 * Assumption: Neither A nor B are valid windows. 1071 */ 1072static void 1073CoreFocusPointerRootNoneSwitch(DeviceIntPtr dev, 1074 WindowPtr A, /* PointerRootWin or NoneWin */ 1075 WindowPtr B, /* NoneWin or PointerRootWin */ 1076 int mode) 1077{ 1078 WindowPtr root; 1079 int i; 1080 int nscreens = screenInfo.numScreens; 1081 1082#ifdef PANORAMIX 1083 if (!noPanoramiXExtension) 1084 nscreens = 1; 1085#endif 1086 1087 for (i = 0; i < nscreens; i++) 1088 { 1089 root = screenInfo.screens[i]->root; 1090 if (!HasOtherPointer(root, GetPairedDevice(dev)) && !FirstFocusChild(root)) 1091 { 1092 /* If pointer was on PointerRootWin and changes to NoneWin, and 1093 * the pointer paired with dev is below the current root window, 1094 * do a NotifyPointer run. */ 1095 if (dev->focus && dev->focus->win == PointerRootWin && 1096 B != PointerRootWin) 1097 { 1098 WindowPtr ptrwin = PointerWin(GetPairedDevice(dev)); 1099 if (ptrwin && IsParent(root, ptrwin)) 1100 CoreFocusOutNotifyPointerEvents(dev, root, None, mode, TRUE); 1101 } 1102 CoreFocusEvent(dev, FocusOut, mode, ((unsigned long)A) ? NotifyPointerRoot : NotifyDetailNone, root); 1103 CoreFocusEvent(dev, FocusIn, mode, ((unsigned long)B) ? NotifyPointerRoot : NotifyDetailNone, root); 1104 if (B == PointerRootWin) 1105 CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE); 1106 } 1107 1108 } 1109} 1110 1111/** 1112 * Focus moves from window A to PointerRoot or to None. 1113 * Assumption: A is a valid window and not PointerRoot or None. 1114 */ 1115static void 1116CoreFocusToPointerRootOrNone(DeviceIntPtr dev, 1117 WindowPtr A, 1118 WindowPtr B, /* PointerRootWin or NoneWin */ 1119 int mode) 1120{ 1121 WindowPtr root; 1122 int i; 1123 int nscreens = screenInfo.numScreens; 1124 1125#ifdef PANORAMIX 1126 if (!noPanoramiXExtension) 1127 nscreens = 1; 1128#endif 1129 1130 if (!HasFocus(A)) 1131 { 1132 WindowPtr child = FirstFocusChild(A); 1133 if (child) 1134 { 1135 /* NotifyPointer P-A unless P is B or below*/ 1136 CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE); 1137 CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A); 1138 } else { 1139 /* NotifyPointer P-A */ 1140 CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE); 1141 CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A); 1142 } 1143 } 1144 1145 /* NullWindow means we include the root window */ 1146 CoreFocusOutEvents(dev, A, NullWindow, mode, NotifyNonlinearVirtual); 1147 1148 for (i = 0; i < nscreens; i++) 1149 { 1150 root = screenInfo.screens[i]->root; 1151 if (!HasFocus(root) && !FirstFocusChild(root)) 1152 { 1153 CoreFocusEvent(dev, FocusIn, mode, ((unsigned long)B) ? NotifyPointerRoot : NotifyDetailNone, root); 1154 if (B == PointerRootWin) 1155 CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE); 1156 } 1157 } 1158} 1159 1160/** 1161 * Focus moves from PointerRoot or None to a window B. 1162 * Assumption: B is a valid window and not PointerRoot or None. 1163 */ 1164static void 1165CoreFocusFromPointerRootOrNone(DeviceIntPtr dev, 1166 WindowPtr A, /* PointerRootWin or NoneWin */ 1167 WindowPtr B, 1168 int mode) 1169{ 1170 WindowPtr root; 1171 int i; 1172 int nscreens = screenInfo.numScreens; 1173 1174#ifdef PANORAMIX 1175 if (!noPanoramiXExtension) 1176 nscreens = 1; 1177#endif 1178 1179 for (i = 0; i < nscreens; i++) 1180 { 1181 root = screenInfo.screens[i]->root; 1182 if (!HasFocus(root) && !FirstFocusChild(root)) 1183 { 1184 /* If pointer was on PointerRootWin and changes to NoneWin, and 1185 * the pointer paired with dev is below the current root window, 1186 * do a NotifyPointer run. */ 1187 if (dev->focus && dev->focus->win == PointerRootWin && 1188 B != PointerRootWin) 1189 { 1190 WindowPtr ptrwin = PointerWin(GetPairedDevice(dev)); 1191 if (ptrwin) 1192 CoreFocusOutNotifyPointerEvents(dev, root, None, mode, TRUE); 1193 } 1194 CoreFocusEvent(dev, FocusOut, mode, ((unsigned long)A) ? NotifyPointerRoot : NotifyDetailNone, root); 1195 } 1196 } 1197 1198 root = B; /* get B's root window */ 1199 while(root->parent) 1200 root = root->parent; 1201 1202 if (B != root) 1203 { 1204 CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinearVirtual, root); 1205 CoreFocusInEvents(dev, root, B, mode, NotifyNonlinearVirtual); 1206 } 1207 1208 1209 if (!HasFocus(B)) 1210 { 1211 WindowPtr child = FirstFocusChild(B); 1212 if (child) 1213 { 1214 CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B); 1215 /* NotifyPointer B-P unless P is child or below. */ 1216 CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE); 1217 } else { 1218 CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B); 1219 /* NotifyPointer B-P unless P is child or below. */ 1220 CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE); 1221 } 1222 } 1223 1224} 1225 1226static void 1227CoreFocusEvents(DeviceIntPtr dev, 1228 WindowPtr from, 1229 WindowPtr to, 1230 int mode) 1231{ 1232 if (!IsMaster(dev)) 1233 return; 1234 1235 SetFocusOut(dev); 1236 1237 if (((to == NullWindow) || (to == PointerRootWin)) && 1238 ((from == NullWindow) || (from == PointerRootWin))) 1239 CoreFocusPointerRootNoneSwitch(dev, from, to, mode); 1240 else if ((to == NullWindow) || (to == PointerRootWin)) 1241 CoreFocusToPointerRootOrNone(dev, from, to, mode); 1242 else if ((from == NullWindow) || (from == PointerRootWin)) 1243 CoreFocusFromPointerRootOrNone(dev, from, to, mode); 1244 else if (IsParent(from, to)) 1245 CoreFocusToDescendant(dev, from, to, mode); 1246 else if (IsParent(to, from)) 1247 CoreFocusToAncestor(dev, from, to, mode); 1248 else 1249 CoreFocusNonLinear(dev, from, to, mode); 1250 1251 SetFocusIn(dev, to); 1252} 1253 1254static void 1255DeviceFocusEvents(DeviceIntPtr dev, 1256 WindowPtr from, 1257 WindowPtr to, 1258 int mode) 1259{ 1260 int out, in; /* for holding details for to/from 1261 PointerRoot/None */ 1262 int i; 1263 int nscreens = screenInfo.numScreens; 1264 SpritePtr sprite = dev->spriteInfo->sprite; 1265 1266 if (from == to) 1267 return; 1268 out = (from == NoneWin) ? NotifyDetailNone : NotifyPointerRoot; 1269 in = (to == NoneWin) ? NotifyDetailNone : NotifyPointerRoot; 1270 /* wrong values if neither, but then not referenced */ 1271 1272#ifdef PANORAMIX 1273 if (!noPanoramiXExtension) 1274 nscreens = 1; 1275#endif 1276 1277 if ((to == NullWindow) || (to == PointerRootWin)) 1278 { 1279 if ((from == NullWindow) || (from == PointerRootWin)) 1280 { 1281 if (from == PointerRootWin) 1282 DeviceFocusOutEvents(dev, sprite->win, GetCurrentRootWindow(dev), mode, 1283 NotifyPointer); 1284 /* Notify all the roots */ 1285 for (i = 0; i < nscreens; i++) 1286 DeviceFocusEvent(dev, XI_FocusOut, mode, out, screenInfo.screens[i]->root); 1287 } 1288 else 1289 { 1290 if (IsParent(from, sprite->win)) 1291 DeviceFocusOutEvents(dev, sprite->win, from, mode, 1292 NotifyPointer); 1293 DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from); 1294 /* next call catches the root too, if the screen changed */ 1295 DeviceFocusOutEvents(dev, from->parent, NullWindow, mode, 1296 NotifyNonlinearVirtual); 1297 } 1298 /* Notify all the roots */ 1299 for (i = 0; i < nscreens; i++) 1300 DeviceFocusEvent(dev, XI_FocusIn, mode, in, screenInfo.screens[i]->root); 1301 if (to == PointerRootWin) 1302 DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), sprite->win, mode, NotifyPointer); 1303 } 1304 else 1305 { 1306 if ((from == NullWindow) || (from == PointerRootWin)) 1307 { 1308 if (from == PointerRootWin) 1309 DeviceFocusOutEvents(dev, sprite->win, GetCurrentRootWindow(dev), mode, 1310 NotifyPointer); 1311 for (i = 0; i < nscreens; i++) 1312 DeviceFocusEvent(dev, XI_FocusOut, mode, out, screenInfo.screens[i]->root); 1313 if (to->parent != NullWindow) 1314 DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), to, mode, NotifyNonlinearVirtual); 1315 DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to); 1316 if (IsParent(to, sprite->win)) 1317 DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer); 1318 } 1319 else 1320 { 1321 if (IsParent(to, from)) 1322 { 1323 DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyAncestor, from); 1324 DeviceFocusOutEvents(dev, from->parent, to, mode, 1325 NotifyVirtual); 1326 DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyInferior, to); 1327 if ((IsParent(to, sprite->win)) && 1328 (sprite->win != from) && 1329 (!IsParent(from, sprite->win)) && 1330 (!IsParent(sprite->win, from))) 1331 DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer); 1332 } 1333 else 1334 if (IsParent(from, to)) 1335 { 1336 if ((IsParent(from, sprite->win)) && 1337 (sprite->win != from) && 1338 (!IsParent(to, sprite->win)) && 1339 (!IsParent(sprite->win, to))) 1340 DeviceFocusOutEvents(dev, sprite->win, from, mode, 1341 NotifyPointer); 1342 DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyInferior, from); 1343 DeviceFocusInEvents(dev, from, to, mode, NotifyVirtual); 1344 DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyAncestor, to); 1345 } 1346 else 1347 { 1348 /* neither from or to is child of other */ 1349 WindowPtr common = CommonAncestor(to, from); 1350 /* common == NullWindow ==> different screens */ 1351 if (IsParent(from, sprite->win)) 1352 DeviceFocusOutEvents(dev, sprite->win, from, mode, 1353 NotifyPointer); 1354 DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from); 1355 if (from->parent != NullWindow) 1356 DeviceFocusOutEvents(dev, from->parent, common, mode, 1357 NotifyNonlinearVirtual); 1358 if (to->parent != NullWindow) 1359 DeviceFocusInEvents(dev, common, to, mode, NotifyNonlinearVirtual); 1360 DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to); 1361 if (IsParent(to, sprite->win)) 1362 DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer); 1363 } 1364 } 1365 } 1366} 1367 1368/** 1369 * Figure out if focus events are necessary and send them to the 1370 * appropriate windows. 1371 * 1372 * @param from Window the focus moved out of. 1373 * @param to Window the focus moved into. 1374 */ 1375void 1376DoFocusEvents(DeviceIntPtr pDev, 1377 WindowPtr from, 1378 WindowPtr to, 1379 int mode) 1380{ 1381 if (!IsKeyboardDevice(pDev)) 1382 return; 1383 1384 if (from == to) 1385 return; 1386 1387 CoreFocusEvents(pDev, from, to, mode); 1388 DeviceFocusEvents(pDev, from, to, mode); 1389} 1390