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