1/*
2
3Copyright (c) 1993, Oracle and/or its affiliates.
4
5Permission is hereby granted, free of charge, to any person obtaining a
6copy of this software and associated documentation files (the "Software"),
7to deal in the Software without restriction, including without limitation
8the rights to use, copy, modify, merge, publish, distribute, sublicense,
9and/or sell copies of the Software, and to permit persons to whom the
10Software is furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice (including the next
13paragraph) shall be included in all copies or substantial portions of the
14Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22DEALINGS IN THE SOFTWARE.
23
24*/
25/********************************************************
26
27Copyright 1988 by Hewlett-Packard Company
28Copyright 1987, 1988, 1989 by Digital Equipment Corporation, Maynard, Massachusetts
29
30Permission to use, copy, modify, and distribute this software
31and its documentation for any purpose and without fee is hereby
32granted, provided that the above copyright notice appear in all
33copies and that both that copyright notice and this permission
34notice appear in supporting documentation, and that the names of
35Hewlett-Packard or Digital not be used in advertising or
36publicity pertaining to distribution of the software without specific,
37written prior permission.
38
39DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45SOFTWARE.
46
47********************************************************/
48
49/*
50
51Copyright 1987, 1988, 1994, 1998  The Open Group
52
53Permission to use, copy, modify, distribute, and sell this software and its
54documentation for any purpose is hereby granted without fee, provided that
55the above copyright notice appear in all copies and that both that
56copyright notice and this permission notice appear in supporting
57documentation.
58
59The above copyright notice and this permission notice shall be included in
60all copies or substantial portions of the Software.
61
62THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
63IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
64FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
65OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
66AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
67CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
68
69Except as contained in this notice, the name of The Open Group shall not be
70used in advertising or otherwise to promote the sale, use or other dealings
71in this Software without prior written authorization from The Open Group.
72
73*/
74
75#ifdef HAVE_CONFIG_H
76#include <config.h>
77#endif
78#include "IntrinsicI.h"
79
80#include "PassivGraI.h"
81#include "EventI.h"
82
83#define _GetWindowedAncestor(w) (XtIsWidget(w) ? w : _XtWindowedAncestor(w))
84
85/* InActiveSubtree cache of the current focus source and its ancestors */
86static Widget *pathTrace = NULL;
87static int pathTraceDepth = 0;
88static int pathTraceMax = 0;
89
90/* FindKeyDestination cache of focus destination and ancestors up to source */
91static Widget *pseudoTrace = NULL;
92static int pseudoTraceDepth = 0;
93static int pseudoTraceMax = 0;
94
95void
96_XtClearAncestorCache(Widget widget)
97{
98    /* the caller must lock the process lock */
99    if (pathTraceDepth && pathTrace[0] == widget)
100        pathTraceDepth = 0;
101}
102
103static XtServerGrabPtr
104CheckServerGrabs(XEvent *event, Widget *trace, Cardinal traceDepth)
105{
106    Cardinal i;
107
108    for (i = traceDepth; i > 0; i--) {
109        XtServerGrabPtr grab;
110
111        if ((grab = _XtCheckServerGrabsOnWidget(event, trace[i - 1], KEYBOARD)))
112            return (grab);
113    }
114    return (XtServerGrabPtr) 0;
115}
116
117static Boolean
118IsParent(Widget a, Widget b)
119{
120    for (b = XtParent(b); b; b = XtParent(b)) {
121        if (b == a)
122            return TRUE;
123        if (XtIsShell(b))
124            return FALSE;
125    }
126    return FALSE;
127}
128
129#define RelRtn(lca, type) {*relTypeRtn = type; return lca;}
130
131static Widget
132CommonAncestor(register Widget a, register Widget b, XtGeneology *relTypeRtn)
133{
134    if (a == b) {
135        RelRtn(a, XtMySelf)
136    }
137    else if (IsParent(a, b)) {
138        RelRtn(a, XtMyAncestor)
139    }
140    else if (IsParent(b, a)) {
141        RelRtn(b, XtMyDescendant)
142    }
143    else
144        for (b = XtParent(b); b && !XtIsShell(b); b = XtParent(b))
145            if (IsParent(b, a)) {
146                RelRtn(b, XtMyCousin)
147            }
148    RelRtn(NULL, XtUnrelated)
149}
150
151#undef RelRtn
152
153static Widget
154_FindFocusWidget(Widget widget,
155                 Widget *trace,
156                 int traceDepth,
157                 Boolean activeCheck,
158                 Boolean *isTarget)
159{
160    int src;
161    Widget dst;
162    XtPerWidgetInput pwi = NULL;
163
164    /* For each ancestor, starting at the top, see if it's forwarded */
165
166    /* first check the trace list till done or we go to branch */
167    for (src = traceDepth - 1, dst = widget; src > 0;) {
168        if ((pwi = _XtGetPerWidgetInput(trace[src], FALSE))) {
169            if (pwi->focusKid) {
170                dst = pwi->focusKid;
171                for (src--; src > 0 && trace[src] != dst; src--) {
172                }
173            }
174            else
175                dst = trace[--src];
176        }
177        else
178            dst = trace[--src];
179    }
180
181    if (isTarget) {
182        if (pwi && pwi->focusKid == widget)
183            *isTarget = TRUE;
184        else
185            *isTarget = FALSE;
186    }
187
188    if (!activeCheck)
189        while (XtIsWidget(dst)
190               && (pwi = _XtGetPerWidgetInput(dst, FALSE))
191               && pwi->focusKid)
192            dst = pwi->focusKid;
193
194    return dst;
195}
196
197static Widget
198FindFocusWidget(Widget widget, XtPerDisplayInput pdi)
199{
200    if (pdi->focusWidget)
201        return pdi->focusWidget;
202    else
203        return _FindFocusWidget(widget, pdi->trace, pdi->traceDepth, FALSE,
204                                NULL);
205}
206
207Widget
208XtGetKeyboardFocusWidget(Widget widget)
209{
210    XtPerDisplayInput pdi;
211    Widget retval;
212
213    WIDGET_TO_APPCON(widget);
214
215    LOCK_APP(app);
216    pdi = _XtGetPerDisplayInput(XtDisplay(widget));
217    retval = FindFocusWidget(widget, pdi);
218    UNLOCK_APP(app);
219    return retval;
220}
221
222static Boolean
223IsOutside(XKeyEvent *e, Widget w)
224{
225    Position left, right, top, bottom;
226
227    /*
228     * if the pointer is outside the shell or inside
229     * the window try to see if it would receive the
230     * focus
231     */
232    XtTranslateCoords(w, 0, 0, &left, &top);
233    /* We need to take borders into consideration */
234    left = (Position) (left - w->core.border_width);
235    top = (Position) (top - w->core.border_width);
236    right = (Position) (left + w->core.width + w->core.border_width);
237    bottom = (Position) (top + w->core.height + w->core.border_width);
238
239    if ((e->x_root < left) || (e->y_root < top) ||
240        (e->x_root > right) || (e->y_root > bottom))
241        return TRUE;
242    else
243        return FALSE;
244}
245
246static Widget
247FindKeyDestination(Widget widget,
248                   XKeyEvent *event,
249                   XtServerGrabPtr prevGrab,
250                   XtServerGrabType prevGrabType,
251                   XtServerGrabPtr devGrab,
252                   XtServerGrabType devGrabType,
253                   XtPerDisplayInput pdi)
254{
255
256    Widget dspWidget;
257    Widget focusWidget;
258
259    LOCK_PROCESS;
260    dspWidget =
261        focusWidget =
262        pdi->focusWidget = _GetWindowedAncestor(FindFocusWidget(widget, pdi));
263
264    /*
265     * If a grab is active from a previous activation then dispatch
266     * based on owner_events ala protocol but with focus being
267     * determined by XtSetKeyboardFocus.
268     */
269    if (IsAnyGrab(prevGrabType)) {
270        if (prevGrab->ownerEvents)
271            dspWidget = focusWidget;
272        else
273            dspWidget = prevGrab->widget;
274    }
275    else {
276        /*
277         * If the focus widget is the event widget or a descendant
278         * then we can avoid the rest of this. Else ugh...
279         */
280        if (focusWidget != widget) {
281            XtGeneology ewRelFw;        /* relationship of event widget to
282                                           focusWidget */
283            Widget lca;
284
285            lca = CommonAncestor(widget, focusWidget, &ewRelFw);
286
287            /*
288             * if the event widget is an ancestor of focus due to the pointer
289             * and/or the grab being in an ancestor and it's a passive grab
290             * send to grab widget.
291             * we are also dispatching to widget if ownerEvents and the event
292             * is outside the client
293             */
294            if ((ewRelFw == XtMyAncestor) &&
295                (devGrabType == XtPassiveServerGrab)) {
296                if (IsOutside(event, widget) || event->type == KeyPress)
297                    dspWidget = devGrab->widget;
298            }
299            else {
300                /*
301                 * if the grab widget is not an ancestor of the focus
302                 * release the grab in order to avoid locking. There
303                 * is a possible case  in that ownerEvents true will fall
304                 * through and if synch is set and the event widget
305                 * could turn it off we'll lock. check for it ? why not
306                 */
307                if ((ewRelFw != XtMyAncestor)
308                    && (devGrabType == XtPassiveServerGrab)
309                    && (!IsAnyGrab(prevGrabType))
310                    ) {
311                    XtUngrabKeyboard(devGrab->widget, event->time);
312                }
313                /*
314                 * if there isn't a grab with then check
315                 * for a logical grab that would have been activated
316                 * if the server was using Xt focus instead of server
317                 * focus
318                 */
319                if ((event->type != KeyPress) || (event->keycode == 0)  /* Xlib XIM composed input */
320                    )
321                    dspWidget = focusWidget;
322                else {
323                    XtServerGrabPtr grab;
324
325                    if (!pseudoTraceDepth ||
326                        !(focusWidget == pseudoTrace[0]) ||
327                        !(lca == pseudoTrace[pseudoTraceDepth])) {
328                        /*
329                         * fill ancestor list from lca
330                         * (non-inclusive)to focusWidget by
331                         * passing in lca as breakWidget
332                         */
333                        _XtFillAncestorList(&pseudoTrace,
334                                            &pseudoTraceMax,
335                                            &pseudoTraceDepth,
336                                            focusWidget, lca);
337                        /* ignore lca */
338                        pseudoTraceDepth--;
339                    }
340                    if ((grab = CheckServerGrabs((XEvent *) event,
341                                                 pseudoTrace,
342                                                 (Cardinal) pseudoTraceDepth)))
343                    {
344                        XtDevice device = &pdi->keyboard;
345
346                        device->grabType = XtPseudoPassiveServerGrab;
347                        pdi->activatingKey = (KeyCode) event->keycode;
348                        device->grab = *grab;
349                        dspWidget = grab->widget;
350                    }
351                }
352            }
353        }
354    }
355    UNLOCK_PROCESS;
356    return dspWidget;
357}
358
359Widget
360_XtProcessKeyboardEvent(XKeyEvent *event, Widget widget, XtPerDisplayInput pdi)
361{
362    XtDevice device = &pdi->keyboard;
363    XtServerGrabPtr devGrab = &device->grab;
364    XtServerGrabRec prevGrabRec;
365    XtServerGrabType prevGrabType = device->grabType;
366    Widget dspWidget = NULL;
367    Boolean deactivateGrab = FALSE;
368
369    prevGrabRec = *devGrab;
370
371    switch (event->type) {
372    case KeyPress:
373    {
374        XtServerGrabPtr newGrab;
375
376        if (event->keycode != 0 &&      /* Xlib XIM composed input */
377            !IsServerGrab(device->grabType) &&
378            (newGrab = CheckServerGrabs((XEvent *) event,
379                                        pdi->trace,
380                                        (Cardinal) pdi->traceDepth))) {
381            /*
382             * honor pseudo-grab from prior event by X
383             * unlocking keyboard. Not Xt Unlock !
384             */
385            if (IsPseudoGrab(prevGrabType))
386                XUngrabKeyboard(XtDisplay(newGrab->widget), event->time);
387            else {
388                /* Activate the grab */
389                device->grab = *newGrab;
390                pdi->activatingKey = (KeyCode) event->keycode;
391                device->grabType = XtPassiveServerGrab;
392            }
393        }
394    }
395        break;
396
397    case KeyRelease:
398    {
399        if (IsEitherPassiveGrab(device->grabType) &&
400            (event->keycode == pdi->activatingKey))
401            deactivateGrab = TRUE;
402    }
403        break;
404    }
405    dspWidget = FindKeyDestination(widget, event,
406                                   &prevGrabRec, prevGrabType,
407                                   devGrab, device->grabType, pdi);
408    if (deactivateGrab) {
409        /* Deactivate the grab */
410        device->grabType = XtNoServerGrab;
411        pdi->activatingKey = 0;
412    }
413    return dspWidget;
414}
415
416static Widget
417GetShell(Widget widget)
418{
419    Widget shell;
420
421    for (shell = widget; shell && !XtIsShell(shell); shell = XtParent(shell)) {
422    }
423    return shell;
424}
425
426/*
427 * Check that widget really has Xt focus due to it having received an
428 * event
429 */
430typedef enum { NotActive = 0, IsActive, IsTarget } ActiveType;
431
432static ActiveType
433InActiveSubtree(Widget widget)
434{
435    Boolean isTarget;
436    ActiveType retval;
437
438    LOCK_PROCESS;
439    if (!pathTraceDepth || widget != pathTrace[0]) {
440        _XtFillAncestorList(&pathTrace,
441                            &pathTraceMax, &pathTraceDepth, widget, NULL);
442    }
443    if (widget == _FindFocusWidget(widget,
444                                   pathTrace, pathTraceDepth, TRUE, &isTarget))
445        retval = (isTarget ? IsTarget : IsActive);
446    else
447        retval = NotActive;
448    UNLOCK_PROCESS;
449    return retval;
450}
451
452void
453_XtHandleFocus(Widget widget,
454               XtPointer client_data, /* child who wants focus */
455               XEvent *event,
456               Boolean *cont _X_UNUSED)
457{
458    XtPerDisplayInput pdi = _XtGetPerDisplayInput(XtDisplay(widget));
459    XtPerWidgetInput pwi = (XtPerWidgetInput) client_data;
460    XtGeneology oldFocalPoint = pwi->focalPoint;
461    XtGeneology newFocalPoint = pwi->focalPoint;
462
463    switch (event->type) {
464
465    case KeyPress:
466    case KeyRelease:
467        /*
468         * We're getting the keyevents used to guarantee propagating
469         * child interest ala ForwardEvent in R3
470         */
471        return;
472
473    case EnterNotify:
474    case LeaveNotify:
475
476        /*
477         * If operating in a focus driven model, then enter and
478         * leave events do not affect the keyboard focus.
479         */
480        if ((event->xcrossing.detail != NotifyInferior)
481            && (event->xcrossing.focus)) {
482            switch (oldFocalPoint) {
483            case XtMyAncestor:
484                if (event->type == LeaveNotify)
485                    newFocalPoint = XtUnrelated;
486                break;
487            case XtUnrelated:
488                if (event->type == EnterNotify)
489                    newFocalPoint = XtMyAncestor;
490                break;
491            case XtMySelf:
492                break;
493            case XtMyDescendant:
494                break;
495
496            }
497        }
498        break;
499    case FocusIn:
500        switch (event->xfocus.detail) {
501        case NotifyNonlinear:
502        case NotifyAncestor:
503        case NotifyInferior:
504            newFocalPoint = XtMySelf;
505            break;
506        case NotifyNonlinearVirtual:
507        case NotifyVirtual:
508            newFocalPoint = XtMyDescendant;
509            break;
510        case NotifyPointer:
511            newFocalPoint = XtMyAncestor;
512            break;
513        }
514        break;
515    case FocusOut:
516        switch (event->xfocus.detail) {
517        case NotifyPointer:
518        case NotifyNonlinear:
519        case NotifyAncestor:
520        case NotifyNonlinearVirtual:
521        case NotifyVirtual:
522            newFocalPoint = XtUnrelated;
523            break;
524        case NotifyInferior:
525            return;
526        }
527        break;
528    }
529
530    if (newFocalPoint != oldFocalPoint) {
531        Boolean add;
532        Widget descendant = pwi->focusKid;
533
534        pwi->focalPoint = newFocalPoint;
535
536        if ((oldFocalPoint == XtUnrelated) &&
537            InActiveSubtree(widget) != NotActive) {
538            pdi->focusWidget = NULL;    /* invalidate the cache */
539            pwi->haveFocus = TRUE;
540            add = TRUE;
541        }
542        else if (newFocalPoint == XtUnrelated) {
543            pdi->focusWidget = NULL;    /* invalidate the cache */
544            pwi->haveFocus = FALSE;
545            add = FALSE;
546        }
547        else
548            return;
549
550        if (descendant) {
551            if (add) {
552                _XtSendFocusEvent(descendant, FocusIn);
553            }
554            else {
555                _XtSendFocusEvent(descendant, FocusOut);
556            }
557        }
558    }
559}
560
561static void
562AddFocusHandler(Widget widget,
563                Widget descendant,
564                XtPerWidgetInput pwi,
565                XtPerWidgetInput psi,
566                XtPerDisplayInput pdi,
567                EventMask oldEventMask)
568{
569    EventMask eventMask, targetEventMask;
570    Widget target;
571
572    /*
573     * widget must now select for key events if the descendant is
574     * interested in them.
575     *
576     * shell borders are not occluded by the child, they're occluded
577     * by reparenting window managers. !!!
578     */
579    target = descendant ? _GetWindowedAncestor(descendant) : NULL;
580    targetEventMask = XtBuildEventMask(target);
581    eventMask = targetEventMask & (KeyPressMask | KeyReleaseMask);
582    eventMask |= FocusChangeMask | EnterWindowMask | LeaveWindowMask;
583
584    if (oldEventMask) {
585        oldEventMask &= KeyPressMask | KeyReleaseMask;
586        oldEventMask |= FocusChangeMask | EnterWindowMask | LeaveWindowMask;
587
588        if (oldEventMask != eventMask)
589            XtRemoveEventHandler(widget, (oldEventMask & ~eventMask),
590                                 False, _XtHandleFocus, (XtPointer) pwi);
591    }
592
593    if (oldEventMask != eventMask)
594        XtAddEventHandler(widget, eventMask, False,
595                          _XtHandleFocus, (XtPointer) pwi);
596
597    /* What follows is too much grief to go through if the
598     * target doesn't actually care about focus change events,
599     * so just invalidate the focus cache & refill it when
600     * the next input event actually arrives.
601     */
602
603    if (!(targetEventMask & FocusChangeMask)) {
604        pdi->focusWidget = NULL;
605        return;
606    }
607
608    if (XtIsRealized(widget) && !pwi->haveFocus) {
609        if (psi->haveFocus) {
610            Window root, child;
611            int root_x, root_y, win_x, win_y;
612            int left, right, top, bottom;
613            unsigned int modMask;
614            ActiveType act;
615
616            /*
617             * If the shell has the focus but the source widget
618             * doesn't, it may only be because the source widget
619             * wasn't previously tracking focus or crossing events.
620             * If the target wants focus events, we have to
621             * now determine whether the source has the focus.
622             */
623
624            if ((act = InActiveSubtree(widget)) == IsTarget)
625                pwi->haveFocus = TRUE;
626            else if (act == IsActive) {
627                /*
628                 * An ancestor contains the focus, so if source
629                 * contains the pointer, then source has the focus.
630                 */
631
632                if (XQueryPointer(XtDisplay(widget), XtWindow(widget),
633                                  &root, &child,
634                                  &root_x, &root_y, &win_x, &win_y, &modMask)) {
635                    /* We need to take borders into consideration */
636                    left = top = -((int) widget->core.border_width);
637                    right =
638                        (int) (widget->core.width +
639                               (widget->core.border_width << 1));
640                    bottom =
641                        (int) (widget->core.height +
642                               (widget->core.border_width << 1));
643
644                    if (win_x >= left && win_x < right &&
645                        win_y >= top && win_y < bottom)
646                        pwi->haveFocus = TRUE;
647                }
648            }
649        }
650    }
651    if (pwi->haveFocus) {
652        pdi->focusWidget = NULL;        /* invalidate the cache */
653        _XtSendFocusEvent(target, FocusIn);
654    }
655}
656
657static void
658QueryEventMask(Widget widget,           /* child who gets focus */
659               XtPointer client_data,   /* ancestor giving it */
660               XEvent *event _X_UNUSED,
661               Boolean *cont _X_UNUSED)
662{
663    /* widget was once the target of an XtSetKeyboardFocus but
664     * was unrealized at the time.   Make sure ancestor still wants
665     * focus set here then install the handler now that we know the
666     * complete event mask.
667     */
668    Widget ancestor = (Widget) client_data;
669    XtPerWidgetInput pwi = _XtGetPerWidgetInput(ancestor, FALSE);
670
671    if (pwi) {
672        Widget target = pwi->queryEventDescendant;
673
674        /* use of 'target' is non-standard hackery;
675           allows focus to non-widget */
676        if (pwi->focusKid == target) {
677            AddFocusHandler(ancestor, target, pwi,
678                            _XtGetPerWidgetInput(GetShell(ancestor), TRUE),
679                            _XtGetPerDisplayInput(XtDisplay(ancestor)),
680                            (EventMask) 0);
681        }
682        XtRemoveEventHandler(widget, XtAllEvents, True,
683                             QueryEventMask, client_data);
684        pwi->map_handler_added = FALSE;
685    }
686}
687
688static void
689FocusDestroyCallback(Widget widget _X_UNUSED,
690                     XtPointer closure, /* Widget */
691                     XtPointer call_data _X_UNUSED)
692{
693    XtSetKeyboardFocus((Widget) closure, NULL);
694}
695
696void
697XtSetKeyboardFocus(Widget widget, Widget descendant)
698{
699    XtPerDisplayInput pdi;
700    XtPerWidgetInput pwi;
701    Widget oldDesc, oldTarget, target, hookobj;
702
703    WIDGET_TO_APPCON(widget);
704
705    LOCK_APP(app);
706    LOCK_PROCESS;
707    pdi = _XtGetPerDisplayInput(XtDisplay(widget));
708    pwi = _XtGetPerWidgetInput(widget, TRUE);
709    oldDesc = pwi->focusKid;
710
711    if (descendant == widget)
712        descendant = (Widget) None;
713
714    target = descendant ? _GetWindowedAncestor(descendant) : NULL;
715    oldTarget = oldDesc ? _GetWindowedAncestor(oldDesc) : NULL;
716
717    if (descendant != oldDesc) {
718
719        /* update the forward path */
720        pwi->focusKid = descendant;
721
722        /* all the rest handles focus ins and focus outs and misc gunk */
723
724        if (oldDesc) {
725            /* invalidate FindKeyDestination's ancestor list */
726            if (pseudoTraceDepth && oldTarget == pseudoTrace[0])
727                pseudoTraceDepth = 0;
728
729            XtRemoveCallback(oldDesc, XtNdestroyCallback,
730                             FocusDestroyCallback, (XtPointer) widget);
731
732            if (!oldTarget->core.being_destroyed) {
733                if (pwi->map_handler_added) {
734                    XtRemoveEventHandler(oldTarget, XtAllEvents, True,
735                                         QueryEventMask, (XtPointer) widget);
736                    pwi->map_handler_added = FALSE;
737                }
738                if (pwi->haveFocus) {
739                    _XtSendFocusEvent(oldTarget, FocusOut);
740                }
741            }
742            else if (pwi->map_handler_added) {
743                pwi->map_handler_added = FALSE;
744            }
745
746            if (pwi->haveFocus)
747                pdi->focusWidget = NULL;        /* invalidate cache */
748
749            /*
750             * If there was a forward path then remove the handler if
751             * the path is being set to null and it isn't a shell.
752             * shells always have a handler for tracking focus for the
753             * hierarchy.
754             *
755             * Keep the pwi record on the assumption that the client
756             * will continue to dynamically assign focus for this widget.
757             */
758            if (!XtIsShell(widget) && !descendant) {
759                XtRemoveEventHandler(widget, XtAllEvents, True,
760                                     _XtHandleFocus, (XtPointer) pwi);
761                pwi->haveFocus = FALSE;
762            }
763        }
764
765        if (descendant) {
766            Widget shell = GetShell(widget);
767            XtPerWidgetInput psi = _XtGetPerWidgetInput(shell, TRUE);
768
769            XtAddCallback(descendant, XtNdestroyCallback,
770                          FocusDestroyCallback, (XtPointer) widget);
771
772            AddFocusHandler(widget, descendant, pwi, psi, pdi,
773                            oldTarget ? XtBuildEventMask(oldTarget) : 0);
774
775            if (widget != shell)
776                XtAddEventHandler(shell,
777                                  FocusChangeMask | EnterWindowMask |
778                                  LeaveWindowMask, False, _XtHandleFocus,
779                                  (XtPointer) psi);
780
781            if (!XtIsRealized(target)) {
782                XtAddEventHandler(target, (EventMask) StructureNotifyMask,
783                                  False, QueryEventMask, (XtPointer) widget);
784                pwi->map_handler_added = TRUE;
785                pwi->queryEventDescendant = descendant;
786            }
787        }
788    }
789    hookobj = XtHooksOfDisplay(XtDisplay(widget));
790    if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
791        XtChangeHookDataRec call_data;
792
793        call_data.type = XtHsetKeyboardFocus;
794        call_data.widget = widget;
795        call_data.event_data = (XtPointer) descendant;
796        XtCallCallbackList(hookobj,
797                           ((HookObject) hookobj)->hooks.changehook_callbacks,
798                           (XtPointer) &call_data);
799    }
800    UNLOCK_PROCESS;
801    UNLOCK_APP(app);
802}
803