Home | History | Annotate | Line # | Download | only in src
      1 /*
      2 
      3 Copyright (c) 1993, Oracle and/or its affiliates.
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining a
      6 copy of this software and associated documentation files (the "Software"),
      7 to deal in the Software without restriction, including without limitation
      8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
      9 and/or sell copies of the Software, and to permit persons to whom the
     10 Software is furnished to do so, subject to the following conditions:
     11 
     12 The above copyright notice and this permission notice (including the next
     13 paragraph) shall be included in all copies or substantial portions of the
     14 Software.
     15 
     16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     22 DEALINGS IN THE SOFTWARE.
     23 
     24 */
     25 /********************************************************
     26 
     27 Copyright 1988 by Hewlett-Packard Company
     28 Copyright 1987, 1988, 1989 by Digital Equipment Corporation, Maynard, Massachusetts
     29 
     30 Permission to use, copy, modify, and distribute this software
     31 and its documentation for any purpose and without fee is hereby
     32 granted, provided that the above copyright notice appear in all
     33 copies and that both that copyright notice and this permission
     34 notice appear in supporting documentation, and that the names of
     35 Hewlett-Packard or Digital not be used in advertising or
     36 publicity pertaining to distribution of the software without specific,
     37 written prior permission.
     38 
     39 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     40 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     41 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     42 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     43 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     44 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     45 SOFTWARE.
     46 
     47 ********************************************************/
     48 
     49 /*
     50 
     51 Copyright 1987, 1988, 1994, 1998  The Open Group
     52 
     53 Permission to use, copy, modify, distribute, and sell this software and its
     54 documentation for any purpose is hereby granted without fee, provided that
     55 the above copyright notice appear in all copies and that both that
     56 copyright notice and this permission notice appear in supporting
     57 documentation.
     58 
     59 The above copyright notice and this permission notice shall be included in
     60 all copies or substantial portions of the Software.
     61 
     62 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     63 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     64 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     65 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     66 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     67 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     68 
     69 Except as contained in this notice, the name of The Open Group shall not be
     70 used in advertising or otherwise to promote the sale, use or other dealings
     71 in 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 */
     86 static Widget *pathTrace = NULL;
     87 static int pathTraceDepth = 0;
     88 static int pathTraceMax = 0;
     89 
     90 /* FindKeyDestination cache of focus destination and ancestors up to source */
     91 static Widget *pseudoTrace = NULL;
     92 static int pseudoTraceDepth = 0;
     93 static int pseudoTraceMax = 0;
     94 
     95 void
     96 _XtClearAncestorCache(Widget widget)
     97 {
     98     /* the caller must lock the process lock */
     99     if (pathTraceDepth && pathTrace[0] == widget)
    100         pathTraceDepth = 0;
    101 }
    102 
    103 static XtServerGrabPtr
    104 CheckServerGrabs(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 
    117 static Boolean
    118 IsParent(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 
    131 static Widget
    132 CommonAncestor(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 
    153 static 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 
    197 static Widget
    198 FindFocusWidget(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 
    207 Widget
    208 XtGetKeyboardFocusWidget(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 
    222 static Boolean
    223 IsOutside(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 
    246 static Widget
    247 FindKeyDestination(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 
    359 Widget
    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 
    416 static Widget
    417 GetShell(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  */
    430 typedef enum { NotActive = 0, IsActive, IsTarget } ActiveType;
    431 
    432 static ActiveType
    433 InActiveSubtree(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 
    452 void
    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 
    561 static void
    562 AddFocusHandler(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 
    657 static void
    658 QueryEventMask(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 
    688 static void
    689 FocusDestroyCallback(Widget widget _X_UNUSED,
    690                      XtPointer closure, /* Widget */
    691                      XtPointer call_data _X_UNUSED)
    692 {
    693     XtSetKeyboardFocus((Widget) closure, NULL);
    694 }
    695 
    696 void
    697 XtSetKeyboardFocus(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