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