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