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