1/***********************************************************
2Copyright (c) 1993, Oracle and/or its affiliates.
3
4Permission is hereby granted, free of charge, to any person obtaining a
5copy of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation
7the rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice (including the next
12paragraph) shall be included in all copies or substantial portions of the
13Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
22
23Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24
25                        All Rights Reserved
26
27Permission to use, copy, modify, and distribute this software and its
28documentation for any purpose and without fee is hereby granted,
29provided that the above copyright notice appear in all copies and that
30both that copyright notice and this permission notice appear in
31supporting documentation, and that the name of Digital not be
32used in advertising or publicity pertaining to distribution of the
33software without specific, written prior permission.
34
35DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41SOFTWARE.
42
43******************************************************************/
44
45/*
46
47Copyright 1987, 1988, 1994, 1998  The Open Group
48
49Permission to use, copy, modify, distribute, and sell this software and its
50documentation for any purpose is hereby granted without fee, provided that
51the above copyright notice appear in all copies and that both that
52copyright notice and this permission notice appear in supporting
53documentation.
54
55The above copyright notice and this permission notice shall be included in
56all copies or substantial portions of the Software.
57
58THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64
65Except as contained in this notice, the name of The Open Group shall not be
66used in advertising or otherwise to promote the sale, use or other dealings
67in this Software without prior written authorization from The Open Group.
68
69*/
70
71#define INTRINSIC_C
72
73#ifdef HAVE_CONFIG_H
74#include <config.h>
75#endif
76#include "IntrinsicI.h"
77#include "VarargsI.h"           /* for geoTattler */
78#ifndef NO_IDENTIFY_WINDOWS
79#include <X11/Xatom.h>
80#endif
81#include <sys/stat.h>
82#ifdef WIN32
83#include <direct.h>             /* for _getdrives() */
84#endif
85
86#include <stdio.h>
87#include <stdlib.h>
88
89String XtCXtToolkitError = "XtToolkitError";
90
91Boolean
92XtIsSubclass(Widget widget, WidgetClass myWidgetClass)
93{
94    register WidgetClass w;
95    Boolean retval = FALSE;
96
97    WIDGET_TO_APPCON(widget);
98
99    LOCK_APP(app);
100    LOCK_PROCESS;
101    for (w = widget->core.widget_class; w != NULL; w = w->core_class.superclass)
102        if (w == myWidgetClass) {
103            retval = TRUE;
104            break;
105        }
106    UNLOCK_PROCESS;
107    UNLOCK_APP(app);
108    return retval;
109}                               /* XtIsSubclass */
110
111Boolean
112_XtCheckSubclassFlag(Widget object, _XtXtEnum flag)
113{
114    Boolean retval;
115
116    LOCK_PROCESS;
117    if (object->core.widget_class->core_class.class_inited & flag)
118        retval = TRUE;
119    else
120        retval = FALSE;
121    UNLOCK_PROCESS;
122    return retval;
123} /*_XtVerifySubclass */
124
125Boolean
126_XtIsSubclassOf(Widget object,
127                WidgetClass myWidgetClass,
128                WidgetClass superClass,
129                _XtXtEnum flag)
130{
131    LOCK_PROCESS;
132    if (!(object->core.widget_class->core_class.class_inited & flag)) {
133        UNLOCK_PROCESS;
134        return False;
135    }
136    else {
137        register WidgetClass c = object->core.widget_class;
138
139        while (c != superClass) {
140            if (c == myWidgetClass) {
141                UNLOCK_PROCESS;
142                return True;
143            }
144            c = c->core_class.superclass;
145        }
146        UNLOCK_PROCESS;
147        return False;
148    }
149} /*_XtIsSubclassOf */
150
151XtPointer
152XtGetClassExtension(WidgetClass object_class,
153                    Cardinal byte_offset,
154                    XrmQuark type, long version, Cardinal record_size)
155{
156    ObjectClassExtension ext;
157
158    LOCK_PROCESS;
159
160    ext = *(ObjectClassExtension *) ((char *) object_class + byte_offset);
161    while (ext && (ext->record_type != type || ext->version < version
162                   || ext->record_size < record_size)) {
163        ext = (ObjectClassExtension) ext->next_extension;
164    }
165
166    UNLOCK_PROCESS;
167    return (XtPointer) ext;
168}
169
170static void
171ComputeWindowAttributes(Widget widget,
172                        XtValueMask *value_mask,
173                        XSetWindowAttributes *values)
174{
175    XtExposeProc expose;
176
177    *value_mask = CWEventMask | CWColormap;
178    (*values).event_mask = (long) XtBuildEventMask(widget);
179    (*values).colormap = widget->core.colormap;
180    if (widget->core.background_pixmap != XtUnspecifiedPixmap) {
181        *value_mask |= CWBackPixmap;
182        (*values).background_pixmap = widget->core.background_pixmap;
183    }
184    else {
185        *value_mask |= CWBackPixel;
186        (*values).background_pixel = widget->core.background_pixel;
187    }
188    if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
189        *value_mask |= CWBorderPixmap;
190        (*values).border_pixmap = widget->core.border_pixmap;
191    }
192    else {
193        *value_mask |= CWBorderPixel;
194        (*values).border_pixel = widget->core.border_pixel;
195    }
196    LOCK_PROCESS;
197    expose = widget->core.widget_class->core_class.expose;
198    UNLOCK_PROCESS;
199    if (expose == (XtExposeProc) NULL) {
200        /* Try to avoid redisplay upon resize by making bit_gravity the same
201           as the default win_gravity */
202        *value_mask |= CWBitGravity;
203        (*values).bit_gravity = NorthWestGravity;
204    }
205}                               /* ComputeWindowAttributes */
206
207static void
208CallChangeManaged(register Widget widget)
209{
210    register Cardinal i;
211    XtWidgetProc change_managed;
212    register WidgetList children;
213    int managed_children = 0;
214
215    register CompositePtr cpPtr;
216    register CompositePartPtr clPtr;
217
218    if (XtIsComposite(widget)) {
219        cpPtr = (CompositePtr) &((CompositeWidget) widget)->composite;
220        clPtr = (CompositePartPtr) &((CompositeWidgetClass)
221                                      widget->core.
222                                      widget_class)->composite_class;
223    }
224    else
225        return;
226
227    children = cpPtr->children;
228    LOCK_PROCESS;
229    change_managed = clPtr->change_managed;
230    UNLOCK_PROCESS;
231
232    /* CallChangeManaged for all children */
233    for (i = cpPtr->num_children; i != 0; --i) {
234        CallChangeManaged(children[i - 1]);
235        if (XtIsManaged(children[i - 1]))
236            managed_children++;
237    }
238
239    if (change_managed != NULL && managed_children != 0) {
240        CALLGEOTAT(_XtGeoTrace(widget, "Call \"%s\"[%d,%d]'s changemanaged\n",
241                               XtName(widget),
242                               widget->core.width, widget->core.height));
243        (*change_managed) (widget);
244    }
245}                               /* CallChangeManaged */
246
247static void
248MapChildren(CompositePart *cwp)
249{
250    Cardinal i;
251    WidgetList children;
252
253    children = cwp->children;
254    for (i = 0; i < cwp->num_children; i++) {
255        Widget child = children[i];
256
257        if (XtIsWidget(child)) {
258            if (child->core.managed && child->core.mapped_when_managed) {
259                XtMapWidget(children[i]);
260            }
261        }
262    }
263}                               /* MapChildren */
264
265static Boolean
266ShouldMapAllChildren(CompositePart *cwp)
267{
268    Cardinal i;
269    WidgetList children;
270
271    children = cwp->children;
272    for (i = 0; i < cwp->num_children; i++) {
273        Widget child = children[i];
274
275        if (XtIsWidget(child)) {
276            if (XtIsRealized(child) && (!(child->core.managed
277                                          && child->core.
278                                          mapped_when_managed))) {
279                return False;
280            }
281        }
282    }
283
284    return True;
285}                               /* ShouldMapAllChildren */
286
287static void
288RealizeWidget(Widget widget)
289{
290    XtValueMask value_mask;
291    XSetWindowAttributes values;
292    XtRealizeProc realize;
293    Window window;
294    Display *display;
295    String class_name;
296    Widget hookobj;
297
298    if (!XtIsWidget(widget) || XtIsRealized(widget))
299        return;
300    display = XtDisplay(widget);
301
302    _XtInstallTranslations(widget);
303
304    ComputeWindowAttributes(widget, &value_mask, &values);
305    LOCK_PROCESS;
306    realize = widget->core.widget_class->core_class.realize;
307    class_name = widget->core.widget_class->core_class.class_name;
308    UNLOCK_PROCESS;
309    if (realize == NULL)
310        XtAppErrorMsg(XtWidgetToApplicationContext(widget),
311                      "invalidProcedure", "realizeProc", XtCXtToolkitError,
312                      "No realize class procedure defined", NULL, NULL);
313    else {
314        CALLGEOTAT(_XtGeoTrace(widget, "Call \"%s\"[%d,%d]'s realize proc\n",
315                               XtName(widget),
316                               widget->core.width, widget->core.height));
317        (*realize) (widget, &value_mask, &values);
318    }
319    window = XtWindow(widget);
320    hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
321    if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
322        XtChangeHookDataRec call_data;
323
324        call_data.type = XtHrealizeWidget;
325        call_data.widget = widget;
326        XtCallCallbackList(hookobj,
327                           ((HookObject) hookobj)->hooks.changehook_callbacks,
328                           (XtPointer) &call_data);
329    }
330#ifndef NO_IDENTIFY_WINDOWS
331    if (_XtGetPerDisplay(display)->appContext->identify_windows) {
332        int len_nm, len_cl;
333        char *s;
334
335        len_nm = widget->core.name ? (int) strlen(widget->core.name) : 0;
336        len_cl = (int) strlen(class_name);
337        s = __XtMalloc((unsigned) (len_nm + len_cl + 2));
338        s[0] = '\0';
339        if (len_nm)
340            strcpy(s, widget->core.name);
341        strcpy(s + len_nm + 1, class_name);
342        XChangeProperty(display, window,
343                        XInternAtom(display, "_MIT_OBJ_CLASS",
344                                    False),
345                        XA_STRING, 8, PropModeReplace, (unsigned char *) s,
346                        len_nm + len_cl + 2);
347        XtFree(s);
348    }
349#endif
350#ifdef notdef
351    _XtRegisterAsyncHandlers(widget);
352#endif
353    /* (re)register any grabs extant in the translations */
354    _XtRegisterGrabs(widget);
355    /* reregister any grabs added with XtGrab{Button,Key} */
356    _XtRegisterPassiveGrabs(widget);
357    XtRegisterDrawable(display, window, widget);
358
359    _XtExtensionSelect(widget);
360
361    if (XtIsComposite(widget)) {
362        Cardinal i;
363        CompositePart *cwp = &(((CompositeWidget) widget)->composite);
364        WidgetList children = cwp->children;
365
366        /* Realize all children */
367        for (i = cwp->num_children; i != 0; --i) {
368            RealizeWidget(children[i - 1]);
369        }
370        /* Map children that are managed and mapped_when_managed */
371
372        if (cwp->num_children != 0) {
373            if (ShouldMapAllChildren(cwp)) {
374                XMapSubwindows(display, window);
375            }
376            else {
377                MapChildren(cwp);
378            }
379        }
380    }
381
382    /* If this is the application's popup shell, map it */
383    if (widget->core.parent == NULL && widget->core.mapped_when_managed) {
384        XtMapWidget(widget);
385    }
386}                               /* RealizeWidget */
387
388void
389XtRealizeWidget(Widget widget)
390{
391    WIDGET_TO_APPCON(widget);
392
393    LOCK_APP(app);
394    if (XtIsRealized(widget)) {
395        UNLOCK_APP(app);
396        return;
397    }
398    CallChangeManaged(widget);
399    RealizeWidget(widget);
400    UNLOCK_APP(app);
401}                               /* XtRealizeWidget */
402
403static void
404UnrealizeWidget(Widget widget)
405{
406    CompositeWidget cw;
407
408    if (!XtIsWidget(widget) || !XtIsRealized(widget))
409        return;
410
411    /* If this is the application's popup shell, unmap it? */
412    /* no, the window is being destroyed */
413
414    /* Recurse on children */
415    if (XtIsComposite(widget)) {
416        Cardinal i;
417        WidgetList children;
418
419        cw = (CompositeWidget) widget;
420        children = cw->composite.children;
421        /* Unrealize all children */
422        for (i = cw->composite.num_children; i != 0; --i) {
423            UnrealizeWidget(children[i - 1]);
424        }
425        /* Unmap children that are managed and mapped_when_managed? */
426        /* No, it's ok to be managed and unrealized as long as your parent */
427        /* is unrealized. XtUnrealize widget makes sure the "top" widget */
428        /* is unmanaged, we can ignore all descendents */
429    }
430
431    if (XtHasCallbacks(widget, XtNunrealizeCallback) == XtCallbackHasSome)
432        XtCallCallbacks(widget, XtNunrealizeCallback, NULL);
433
434    /* Unregister window */
435    XtUnregisterDrawable(XtDisplay(widget), XtWindow(widget));
436
437    /* Remove Event Handlers */
438    /* remove grabs. Happens automatically when window is destroyed. */
439
440    /* Destroy X Window, done at outer level with one request */
441    widget->core.window = None;
442
443    /* Removing the event handler here saves having to keep track if
444     * the translation table is changed while the widget is unrealized.
445     */
446    _XtRemoveTranslations(widget);
447}                               /* UnrealizeWidget */
448
449void
450XtUnrealizeWidget(Widget widget)
451{
452    Window window;
453    Widget hookobj;
454
455    WIDGET_TO_APPCON(widget);
456
457    LOCK_APP(app);
458    window = XtWindow(widget);
459    if (!XtIsRealized(widget)) {
460        UNLOCK_APP(app);
461        return;
462    }
463    if (widget->core.managed && widget->core.parent != NULL)
464        XtUnmanageChild(widget);
465    UnrealizeWidget(widget);
466    if (window != None)
467        XDestroyWindow(XtDisplay(widget), window);
468    hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
469    if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
470        XtChangeHookDataRec call_data;
471
472        call_data.type = XtHunrealizeWidget;
473        call_data.widget = widget;
474        XtCallCallbackList(hookobj,
475                           ((HookObject) hookobj)->hooks.changehook_callbacks,
476                           (XtPointer) &call_data);
477    }
478    UNLOCK_APP(app);
479}                               /* XtUnrealizeWidget */
480
481void
482XtCreateWindow(Widget widget,
483               unsigned int window_class,
484               Visual *visual,
485               XtValueMask value_mask,
486               XSetWindowAttributes *attributes)
487{
488    XtAppContext app = XtWidgetToApplicationContext(widget);
489
490    LOCK_APP(app);
491    if (widget->core.window == None) {
492        if (widget->core.width == 0 || widget->core.height == 0) {
493            Cardinal count = 1;
494
495            XtAppErrorMsg(app,
496                          "invalidDimension", "xtCreateWindow",
497                          XtCXtToolkitError,
498                          "Widget %s has zero width and/or height",
499                          &widget->core.name, &count);
500        }
501        widget->core.window =
502            XCreateWindow(XtDisplay(widget),
503                          (widget->core.parent ?
504                           widget->core.parent->core.window :
505                           widget->core.screen->root),
506                          (int) widget->core.x, (int) widget->core.y,
507                          (unsigned) widget->core.width,
508                          (unsigned) widget->core.height,
509                          (unsigned) widget->core.border_width,
510                          (int) widget->core.depth, window_class, visual,
511                          value_mask, attributes);
512    }
513    UNLOCK_APP(app);
514}                               /* XtCreateWindow */
515
516/* ---------------- XtNameToWidget ----------------- */
517
518static Widget NameListToWidget(Widget root,
519                               XrmNameList names,
520                               XrmBindingList bindings,
521                               int in_depth, int *out_depth, int *found_depth);
522
523typedef Widget(*NameMatchProc) (XrmNameList,
524                                XrmBindingList,
525                                WidgetList, Cardinal, int, int *, int *);
526
527static Widget
528MatchExactChildren(XrmNameList names,
529                   XrmBindingList bindings,
530                   register WidgetList children,
531                   register Cardinal num,
532                   int in_depth,
533                   int *out_depth,
534                   int *found_depth)
535{
536    register Cardinal i;
537    register XrmName name = *names;
538    Widget w, result = NULL;
539    int d, min = 10000;
540
541    for (i = 0; i < num; i++) {
542        if (name == children[i]->core.xrm_name) {
543            w = NameListToWidget(children[i], &names[1], &bindings[1],
544                                 in_depth + 1, &d, found_depth);
545            if (w != NULL && d < min) {
546                result = w;
547                min = d;
548            }
549        }
550    }
551    *out_depth = min;
552    return result;
553}
554
555static Widget
556MatchWildChildren(XrmNameList names,
557                  XrmBindingList bindings,
558                  register WidgetList children,
559                  register Cardinal num,
560                  int in_depth,
561                  int *out_depth,
562                  int *found_depth)
563{
564    register Cardinal i;
565    Widget w, result = NULL;
566    int d, min = 10000;
567
568    for (i = 0; i < num; i++) {
569        w = NameListToWidget(children[i], names, bindings,
570                             in_depth + 1, &d, found_depth);
571        if (w != NULL && d < min) {
572            result = w;
573            min = d;
574        }
575    }
576    *out_depth = min;
577    return result;
578}
579
580static Widget
581SearchChildren(Widget root,
582               XrmNameList names,
583               XrmBindingList bindings,
584               NameMatchProc matchproc,
585               int in_depth,
586               int *out_depth,
587               int *found_depth)
588{
589    Widget w1 = NULL, w2;
590    int d1, d2;
591
592    if (XtIsComposite(root)) {
593        w1 = (*matchproc) (names, bindings,
594                           ((CompositeWidget) root)->composite.children,
595                           ((CompositeWidget) root)->composite.num_children,
596                           in_depth, &d1, found_depth);
597    }
598    else
599        d1 = 10000;
600    w2 = (*matchproc) (names, bindings, root->core.popup_list,
601                       root->core.num_popups, in_depth, &d2, found_depth);
602    *out_depth = (d1 < d2 ? d1 : d2);
603    return (d1 < d2 ? w1 : w2);
604}
605
606static Widget
607NameListToWidget(register Widget root,
608                 XrmNameList names,
609                 XrmBindingList bindings,
610                 int in_depth,
611                 int *out_depth,
612                 int *found_depth)
613{
614    int d1, d2;
615
616    if (in_depth >= *found_depth) {
617        *out_depth = 10000;
618        return NULL;
619    }
620
621    if (names[0] == NULLQUARK) {
622        *out_depth = *found_depth = in_depth;
623        return root;
624    }
625
626    if (!XtIsWidget(root)) {
627        *out_depth = 10000;
628        return NULL;
629    }
630
631    if (*bindings == XrmBindTightly) {
632        return SearchChildren(root, names, bindings, MatchExactChildren,
633                              in_depth, out_depth, found_depth);
634
635    }
636    else {                      /* XrmBindLoosely */
637        Widget w1, w2;
638
639        w1 = SearchChildren(root, names, bindings, MatchExactChildren,
640                            in_depth, &d1, found_depth);
641        w2 = SearchChildren(root, names, bindings, MatchWildChildren,
642                            in_depth, &d2, found_depth);
643        *out_depth = (d1 < d2 ? d1 : d2);
644        return (d1 < d2 ? w1 : w2);
645    }
646}                               /* NameListToWidget */
647
648Widget
649XtNameToWidget(Widget root, _Xconst char *name)
650{
651    XrmName *names;
652    XrmBinding *bindings;
653    int len, depth, found = 10000;
654    Widget result;
655
656    WIDGET_TO_APPCON(root);
657
658    len = (int) strlen(name);
659    if (len == 0)
660        return NULL;
661
662    LOCK_APP(app);
663    names = (XrmName *) ALLOCATE_LOCAL((unsigned) (len + 1) * sizeof(XrmName));
664    bindings = (XrmBinding *)
665        ALLOCATE_LOCAL((unsigned) (len + 1) * sizeof(XrmBinding));
666    if (names == NULL || bindings == NULL)
667        _XtAllocError(NULL);
668
669    XrmStringToBindingQuarkList(name, bindings, names);
670    if (names[0] == NULLQUARK) {
671        DEALLOCATE_LOCAL((char *) bindings);
672        DEALLOCATE_LOCAL((char *) names);
673        UNLOCK_APP(app);
674        return NULL;
675    }
676
677    result = NameListToWidget(root, names, bindings, 0, &depth, &found);
678
679    DEALLOCATE_LOCAL((char *) bindings);
680    DEALLOCATE_LOCAL((char *) names);
681    UNLOCK_APP(app);
682    return result;
683}                               /* XtNameToWidget */
684
685/* Define user versions of intrinsics macros */
686
687#undef XtDisplayOfObject
688Display *
689XtDisplayOfObject(Widget object)
690{
691    /* Attempts to LockApp() here will generate endless recursive loops */
692    if (XtIsSubclass(object, hookObjectClass))
693        return DisplayOfScreen(((HookObject) object)->hooks.screen);
694    return XtDisplay(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
695}
696
697#undef XtDisplay
698Display *
699XtDisplay(Widget widget)
700{
701    /* Attempts to LockApp() here will generate endless recursive loops */
702    return DisplayOfScreen(widget->core.screen);
703}
704
705#undef XtScreenOfObject
706Screen *
707XtScreenOfObject(Widget object)
708{
709    /* Attempts to LockApp() here will generate endless recursive loops */
710    if (XtIsSubclass(object, hookObjectClass))
711        return ((HookObject) object)->hooks.screen;
712    return XtScreen(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
713}
714
715#undef XtScreen
716Screen *
717XtScreen(Widget widget)
718{
719    /* Attempts to LockApp() here will generate endless recursive loops */
720    return widget->core.screen;
721}
722
723#undef XtWindowOfObject
724Window
725XtWindowOfObject(Widget object)
726{
727    return XtWindow(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
728}
729
730#undef XtWindow
731Window
732XtWindow(Widget widget)
733{
734    return widget->core.window;
735}
736
737#undef XtSuperclass
738WidgetClass
739XtSuperclass(Widget widget)
740{
741    WidgetClass retval;
742
743    LOCK_PROCESS;
744    retval = XtClass(widget)->core_class.superclass;
745    UNLOCK_PROCESS;
746    return retval;
747}
748
749#undef XtClass
750WidgetClass
751XtClass(Widget widget)
752{
753    WidgetClass retval;
754
755    LOCK_PROCESS;
756    retval = widget->core.widget_class;
757    UNLOCK_PROCESS;
758    return retval;
759}
760
761#undef XtIsManaged
762Boolean
763XtIsManaged(Widget object)
764{
765    Boolean retval;
766
767    WIDGET_TO_APPCON(object);
768
769    LOCK_APP(app);
770    if (XtIsRectObj(object))
771        retval = object->core.managed;
772    else
773        retval = False;
774    UNLOCK_APP(app);
775    return retval;
776}
777
778#undef XtIsRealized
779Boolean
780XtIsRealized(Widget object)
781{
782    Boolean retval;
783
784    WIDGET_TO_APPCON(object);
785
786    LOCK_APP(app);
787    retval = XtWindowOfObject(object) != None;
788    UNLOCK_APP(app);
789    return retval;
790}                               /* XtIsRealized */
791
792#undef XtIsSensitive
793Boolean
794XtIsSensitive(Widget object)
795{
796    Boolean retval;
797
798    WIDGET_TO_APPCON(object);
799
800    LOCK_APP(app);
801    if (XtIsRectObj(object))
802        retval = object->core.sensitive && object->core.ancestor_sensitive;
803    else
804        retval = False;
805    UNLOCK_APP(app);
806    return retval;
807}
808
809/*
810 * Internal routine; must be called only after XtIsWidget returns false
811 */
812Widget
813_XtWindowedAncestor(register Widget object)
814{
815    Widget obj = object;
816
817    for (object = XtParent(object); object && !XtIsWidget(object);)
818        object = XtParent(object);
819
820    if (object == NULL) {
821        String params = XtName(obj);
822        Cardinal num_params = 1;
823
824        XtErrorMsg("noWidgetAncestor", "windowedAncestor", XtCXtToolkitError,
825                   "Object \"%s\" does not have windowed ancestor",
826                   &params, &num_params);
827    }
828
829    return object;
830}
831
832#undef XtParent
833Widget
834XtParent(Widget widget)
835{
836    /* Attempts to LockApp() here will generate endless recursive loops */
837    return widget->core.parent;
838}
839
840#undef XtName
841String
842XtName(Widget object)
843{
844    /* Attempts to LockApp() here will generate endless recursive loops */
845    return XrmQuarkToString(object->core.xrm_name);
846}
847
848Boolean
849XtIsObject(Widget object)
850{
851    WidgetClass wc;
852    String class_name;
853
854    /* perform basic sanity checks */
855    if (object->core.self != object || object->core.xrm_name == NULLQUARK)
856        return False;
857
858    LOCK_PROCESS;
859    wc = object->core.widget_class;
860    if (wc->core_class.class_name == NULL ||
861        wc->core_class.xrm_class == NULLQUARK ||
862        (class_name = XrmClassToString(wc->core_class.xrm_class)) == NULL ||
863        strcmp(wc->core_class.class_name, class_name) != 0) {
864        UNLOCK_PROCESS;
865        return False;
866    }
867    UNLOCK_PROCESS;
868
869    if (XtIsWidget(object)) {
870        if (object->core.name == NULL ||
871            (class_name = XrmNameToString(object->core.xrm_name)) == NULL ||
872            strcmp(object->core.name, class_name) != 0)
873            return False;
874    }
875    return True;
876}
877
878#if defined(WIN32)
879static int
880access_file(char *path, char *pathbuf, int len_pathbuf, char **pathret)
881{
882    if (access(path, F_OK) == 0) {
883        if (strlen(path) < len_pathbuf)
884            *pathret = pathbuf;
885        else
886            *pathret = XtMalloc(strlen(path));
887        if (*pathret) {
888            strcpy(*pathret, path);
889            return 1;
890        }
891    }
892    return 0;
893}
894
895static int
896AccessFile(char *path, char *pathbuf, int len_pathbuf, char **pathret)
897{
898    unsigned long drives;
899    int i, len;
900    char *drive;
901    char buf[MAX_PATH];
902    char *bufp;
903
904    /* just try the "raw" name first and see if it works */
905    if (access_file(path, pathbuf, len_pathbuf, pathret))
906        return 1;
907
908#if defined(WIN32) && defined(__MINGW32__)
909    /* don't try others */
910    return 0;
911#endif
912
913    /* try the places set in the environment */
914    drive = getenv("_XBASEDRIVE");
915    if (!drive)
916        drive = "C:";
917    len = strlen(drive) + strlen(path);
918    bufp = XtStackAlloc(len + 1, buf);
919    strcpy(bufp, drive);
920    strcat(bufp, path);
921    if (access_file(bufp, pathbuf, len_pathbuf, pathret)) {
922        XtStackFree(bufp, buf);
923        return 1;
924    }
925
926    /* one last place to look */
927    drive = getenv("HOMEDRIVE");
928    if (drive) {
929        len = strlen(drive) + strlen(path);
930        bufp = XtStackAlloc(len + 1, buf);
931        strcpy(bufp, drive);
932        strcat(bufp, path);
933        if (access_file(bufp, pathbuf, len_pathbuf, pathret)) {
934            XtStackFree(bufp, buf);
935            return 1;
936        }
937    }
938
939    /* does OS/2 (with or with gcc-emx) have getdrives()? */
940    /* tried everywhere else, go fishing */
941    drives = _getdrives();
942#define C_DRIVE ('C' - 'A')
943#define Z_DRIVE ('Z' - 'A')
944    for (i = C_DRIVE; i <= Z_DRIVE; i++) {      /* don't check on A: or B: */
945        if ((1 << i) & drives) {
946            len = 2 + strlen(path);
947            bufp = XtStackAlloc(len + 1, buf);
948            *bufp = 'A' + i;
949            *(bufp + 1) = ':';
950            *(bufp + 2) = '\0';
951            strcat(bufp, path);
952            if (access_file(bufp, pathbuf, len_pathbuf, pathret)) {
953                XtStackFree(bufp, buf);
954                return 1;
955            }
956        }
957    }
958    return 0;
959}
960#endif
961
962static Boolean
963TestFile(String path)
964{
965    int ret = 0;
966    struct stat status;
967
968#if defined(WIN32)
969    char buf[MAX_PATH];
970    char *bufp;
971    int len;
972    UINT olderror = SetErrorMode(SEM_FAILCRITICALERRORS);
973
974    if (AccessFile(path, buf, MAX_PATH, &bufp))
975        path = bufp;
976
977    (void) SetErrorMode(olderror);
978#endif
979    ret = (access(path, R_OK) == 0 &&   /* exists and is readable */
980           stat(path, &status) == 0 &&  /* get the status */
981#ifndef X_NOT_POSIX
982           S_ISDIR(status.st_mode) == 0);       /* not a directory */
983#else
984           (status.st_mode & S_IFMT) != S_IFDIR);       /* not a directory */
985#endif                          /* X_NOT_POSIX else */
986    return (Boolean) ret;
987}
988
989/* return of TRUE = resolved string fit, FALSE = didn't fit.  Not
990   null-terminated and not collapsed if it didn't fit */
991
992static Boolean Resolve(register _Xconst char *source,   /* The source string */
993                       register int len,        /* The length in bytes of *source */
994                       Substitution sub,        /* Array of string values to substitute */
995                       Cardinal num,    /* Number of substitution entries */
996                       char *buf,       /* Where to put the resolved string; */
997                       char collapse) { /* Character to collapse */
998    register int bytesLeft = PATH_MAX;
999    register char *bp = buf;
1000
1001#ifndef DONT_COLLAPSE
1002    Boolean atBeginning = TRUE;
1003    Boolean prevIsCollapse = FALSE;
1004
1005#define PUT(ch) \
1006    { \
1007        if (--bytesLeft == 0) return FALSE; \
1008        if (prevIsCollapse) \
1009            if ((*bp = ch) != collapse) { \
1010                prevIsCollapse = FALSE; \
1011                bp++; \
1012            } \
1013            else bytesLeft++; \
1014        else if ((*bp++ = ch) == collapse && !atBeginning) \
1015            prevIsCollapse = TRUE; \
1016    }
1017#else                           /* DONT_COLLAPSE */
1018
1019#define PUT(ch) \
1020    { \
1021        if (--bytesLeft == 0) return FALSE; \
1022        *bp++ = ch; \
1023    }
1024#endif                          /* DONT_COLLAPSE */
1025#define escape '%'
1026
1027    while (len--) {
1028#ifndef DONT_COLLAPSE
1029        if (*source == collapse) {
1030            PUT(*source);
1031            source++;
1032            continue;
1033        }
1034        else
1035#endif                          /* DONT_COLLAPSE */
1036        if (*source != escape) {
1037            PUT(*source);
1038        }
1039        else {
1040            source++;
1041            if (len-- == 0) {
1042                PUT(escape);
1043                break;
1044            }
1045
1046            if (*source == ':' || *source == escape) {
1047                PUT(*source);
1048            }
1049            else {
1050                /* Match the character against the match array */
1051                register Cardinal j;
1052
1053                for (j = 0; j < num && sub[j].match != *source; j++) {
1054                }
1055
1056                /* Substitute the substitution string */
1057
1058                if (j >= num) {
1059                    PUT(*source);
1060                }
1061                else if (sub[j].substitution != NULL) {
1062                    char *sp = sub[j].substitution;
1063
1064                    while (*sp) {
1065                        PUT(*sp);
1066                        sp++;
1067                    }
1068                }
1069            }
1070        }
1071        source++;
1072#ifndef DONT_COLLAPSE
1073        atBeginning = FALSE;
1074#endif                          /* DONT_COLLAPSE */
1075    }
1076    PUT('\0');
1077
1078    return TRUE;
1079#undef PUT
1080#undef escape
1081}
1082
1083_XtString
1084XtFindFile(_Xconst _XtString path,
1085           Substitution substitutions,
1086           Cardinal num_substitutions,
1087           XtFilePredicate predicate)
1088{
1089    char *buf, *buf1, *buf2;
1090    _Xconst _XtString colon;
1091    int len;
1092    Boolean firstTime = TRUE;
1093
1094    buf1 = __XtMalloc((unsigned) PATH_MAX);
1095    buf2 = __XtMalloc((unsigned) PATH_MAX);
1096    buf = buf1;
1097
1098    if (predicate == NULL)
1099        predicate = TestFile;
1100
1101    while (1) {
1102        colon = path;
1103        /* skip leading colons */
1104        while (*colon) {
1105            if (*colon != ':')
1106                break;
1107            colon++;
1108            path++;
1109        }
1110        /* now look for an un-escaped colon */
1111        for (; *colon; colon++) {
1112            if (*colon == '%' && *(path + 1)) {
1113                colon++;        /* bump it an extra time to skip %. */
1114                continue;
1115            }
1116            if (*colon == ':')
1117                break;
1118        }
1119        len = (int) (colon - path);
1120        if (Resolve(path, len, substitutions, num_substitutions, buf, '/')) {
1121            if (firstTime || strcmp(buf1, buf2) != 0) {
1122#ifdef XNL_DEBUG
1123                printf("Testing file %s\n", buf);
1124#endif                          /* XNL_DEBUG */
1125                /* Check out the file */
1126                if ((*predicate) (buf)) {
1127                    /* We've found it, return it */
1128#ifdef XNL_DEBUG
1129                    printf("File found.\n");
1130#endif                          /* XNL_DEBUG */
1131                    if (buf == buf1) {
1132                        XtFree(buf2);
1133                        return buf1;
1134                    }
1135                    XtFree(buf1);
1136                    return buf2;
1137                }
1138                if (buf == buf1)
1139                    buf = buf2;
1140                else
1141                    buf = buf1;
1142                firstTime = FALSE;
1143            }
1144        }
1145
1146        /* Nope...any more paths? */
1147
1148        if (*colon == '\0')
1149            break;
1150        path = colon + 1;
1151    }
1152
1153    /* No file found */
1154
1155    XtFree(buf1);
1156    XtFree(buf2);
1157    return NULL;
1158}
1159
1160/* The implementation of this routine is operating system dependent */
1161/* Should match the code in Xlib _XlcMapOSLocaleName */
1162
1163static String
1164ExtractLocaleName(String lang)
1165{
1166
1167#if defined(CSRG_BASED) || defined(sun) || defined(SVR4) || defined(WIN32) || defined (linux)
1168#ifdef WIN32
1169#define SKIPCOUNT 1
1170#define STARTCHAR '='
1171#define ENDCHAR ';'
1172#define WHITEFILL
1173#else
1174#if defined(linux)
1175#define STARTSTR "LC_CTYPE="
1176#define ENDCHAR ';'
1177#else
1178#if !defined(sun) || defined(SVR4)
1179#define STARTCHAR '/'
1180#define ENDCHAR '/'
1181#endif
1182#endif
1183#endif
1184
1185    String start;
1186    String end;
1187    int len;
1188
1189#ifdef SKIPCOUNT
1190    int n;
1191#endif
1192    static char *buf = NULL;
1193
1194#ifdef WHITEFILL
1195    char *temp;
1196#endif
1197
1198    start = lang;
1199#ifdef SKIPCOUNT
1200    for (n = SKIPCOUNT;
1201         --n >= 0 && start && (start = strchr(start, STARTCHAR)); start++);
1202    if (!start)
1203        start = lang;
1204#endif
1205#ifdef STARTCHAR
1206    if (start && (start = strchr(start, STARTCHAR)))
1207#elif  defined (STARTSTR)
1208    if (start && (start = strstr(start, STARTSTR)))
1209#endif
1210    {
1211#ifdef STARTCHAR
1212        start++;
1213#elif defined (STARTSTR)
1214        start += strlen(STARTSTR);
1215#endif
1216
1217        if ((end = strchr(start, ENDCHAR))) {
1218            len = (int) (end - start);
1219            XtFree(buf);
1220            buf = XtMalloc((Cardinal) (len + 1));
1221            if (buf == NULL)
1222                return NULL;
1223            strncpy(buf, start, (size_t) len);
1224            *(buf + len) = '\0';
1225#ifdef WHITEFILL
1226            for (temp = buf; (temp = strchr(temp, ' ')) != NULL;)
1227                *temp++ = '-';
1228#endif
1229            return buf;
1230        }
1231        else                    /* if no ENDCHAR is found we are at the end of the line */
1232            return start;
1233    }
1234#ifdef WHITEFILL
1235    if (strchr(lang, ' ')) {
1236        XtFree(buf);
1237        buf = strdup(lang);
1238        if (buf == NULL)
1239            return NULL;
1240        for (temp = buf; (temp = strchr(temp, ' ')) != NULL;)
1241            *temp++ = '-';
1242        return buf;
1243    }
1244#endif
1245#undef STARTCHAR
1246#undef ENDCHAR
1247#undef WHITEFILL
1248#endif
1249
1250    return lang;
1251}
1252
1253static void
1254FillInLangSubs(Substitution subs, XtPerDisplay pd)
1255{
1256    int len;
1257    String string;
1258    char *p1, *p2, *p3;
1259    char **rest;
1260    char *ch;
1261
1262    if (pd->language == NULL || pd->language[0] == '\0') {
1263        subs[0].substitution = subs[1].substitution =
1264            subs[2].substitution = subs[3].substitution = NULL;
1265        return;
1266    }
1267
1268    string = ExtractLocaleName(pd->language);
1269
1270    if (string == NULL || string[0] == '\0') {
1271        subs[0].substitution = subs[1].substitution =
1272            subs[2].substitution = subs[3].substitution = NULL;
1273        return;
1274    }
1275
1276    len = (int) strlen(string) + 1;
1277    subs[0].substitution = (_XtString) string;
1278    p1 = subs[1].substitution = XtMallocArray(3, (Cardinal) len);
1279    p2 = subs[2].substitution = subs[1].substitution + len;
1280    p3 = subs[3].substitution = subs[2].substitution + len;
1281
1282    /* Everything up to the first "_" goes into p1.  From "_" to "." in
1283       p2.  The rest in p3.  If no delimiters, all goes into p1.  We
1284       assume p1, p2, and p3 are large enough. */
1285
1286    *p1 = *p2 = *p3 = '\0';
1287
1288    ch = strchr(string, '_');
1289    if (ch != NULL) {
1290        len = (int) (ch - string);
1291        (void) strncpy(p1, string, (size_t) len);
1292        p1[len] = '\0';
1293        string = ch + 1;
1294        rest = &p2;
1295    }
1296    else
1297        rest = &p1;
1298
1299    /* Rest points to where we put the first part */
1300
1301    ch = strchr(string, '.');
1302    if (ch != NULL) {
1303        len = (int) (ch - string);
1304        strncpy(*rest, string, (size_t) len);
1305        (*rest)[len] = '\0';
1306        (void) strcpy(p3, ch + 1);
1307    }
1308    else
1309        (void) strcpy(*rest, string);
1310}
1311
1312/*
1313 * default path used if environment variable XFILESEARCHPATH
1314 * is not defined.  Also substituted for %D.
1315 * The exact value should be documented in the implementation
1316 * notes for any Xt implementation.
1317 */
1318static const char *
1319implementation_default_path(void)
1320{
1321#if defined(WIN32)
1322    static char xfilesearchpath[] = "";
1323
1324    return xfilesearchpath;
1325#else
1326    return XFILESEARCHPATHDEFAULT;
1327#endif
1328}
1329
1330
1331/* *INDENT-OFF* */
1332static SubstitutionRec defaultSubs[] = {
1333    {'N', NULL},
1334    {'T', NULL},
1335    {'S', NULL},
1336    {'C', NULL},
1337    {'L', NULL},
1338    {'l', NULL},
1339    {'t', NULL},
1340    {'c', NULL}
1341};
1342/* *INDENT-ON* */
1343
1344_XtString
1345XtResolvePathname(Display *dpy,
1346                  _Xconst char *type,
1347                  _Xconst char *filename,
1348                  _Xconst char *suffix,
1349                  _Xconst char *path,
1350                  Substitution substitutions,
1351                  Cardinal num_substitutions,
1352                  XtFilePredicate predicate)
1353{
1354    XtPerDisplay pd;
1355    static const char *defaultPath = NULL;
1356    const char *impl_default = implementation_default_path();
1357    int idef_len = (int) strlen(impl_default);
1358    char *massagedPath;
1359    int bytesAllocd, bytesLeft;
1360    char *ch, *result;
1361    Substitution merged_substitutions;
1362    XrmRepresentation db_type;
1363    XrmValue value;
1364    XrmName name_list[3];
1365    XrmClass class_list[3];
1366    Boolean pathMallocd = False;
1367
1368    LOCK_PROCESS;
1369    pd = _XtGetPerDisplay(dpy);
1370    if (path == NULL) {
1371        if (defaultPath == NULL) {
1372            defaultPath = getenv("XFILESEARCHPATH");
1373            if (defaultPath == NULL)
1374                defaultPath = impl_default;
1375        }
1376        path = defaultPath;
1377    }
1378
1379    if (path == NULL)
1380        path = "";              /* NULL would kill us later */
1381
1382    if (filename == NULL) {
1383        filename = XrmClassToString(pd->class);
1384    }
1385
1386    bytesAllocd = bytesLeft = 1000;
1387    massagedPath = ALLOCATE_LOCAL((size_t) bytesAllocd);
1388    if (massagedPath == NULL)
1389        _XtAllocError(NULL);
1390
1391    if (path[0] == ':') {
1392        strcpy(massagedPath, "%N%S");
1393        ch = &massagedPath[4];
1394        bytesLeft -= 4;
1395    }
1396    else
1397        ch = massagedPath;
1398
1399    /* Insert %N%S between adjacent colons
1400     * and default path for %D.
1401     * Default path should not have any adjacent colons of its own.
1402     */
1403
1404    while (*path != '\0') {
1405        if (bytesLeft < idef_len) {
1406            int bytesUsed = bytesAllocd - bytesLeft;
1407            char *new;
1408
1409            bytesAllocd += 1000;
1410            new = __XtMalloc((Cardinal) bytesAllocd);
1411            strncpy(new, massagedPath, (size_t) bytesUsed);
1412            ch = new + bytesUsed;
1413            if (pathMallocd)
1414                XtFree(massagedPath);
1415            else
1416                DEALLOCATE_LOCAL(massagedPath);
1417            pathMallocd = True;
1418            massagedPath = new;
1419            bytesLeft = bytesAllocd - bytesUsed;
1420        }
1421        if (*path == '%' && *(path + 1) == ':') {
1422            *ch++ = '%';
1423            *ch++ = ':';
1424            path += 2;
1425            bytesLeft -= 2;
1426            continue;
1427        }
1428        if (*path == ':' && *(path + 1) == ':') {
1429            strcpy(ch, ":%N%S:");
1430            ch += 6;
1431            bytesLeft -= 6;
1432            while (*path == ':')
1433                path++;
1434            continue;
1435        }
1436        if (*path == '%' && *(path + 1) == 'D') {
1437            strcpy(ch, impl_default);
1438            ch += idef_len;
1439            bytesLeft -= idef_len;
1440            path += 2;
1441            continue;
1442        }
1443        *ch++ = *path++;
1444        bytesLeft--;
1445    }
1446    *ch = '\0';
1447#ifdef XNL_DEBUG
1448    printf("Massaged path: %s\n", massagedPath);
1449#endif                          /* XNL_DEBUG */
1450
1451    if (num_substitutions == 0)
1452        merged_substitutions = defaultSubs;
1453    else {
1454        int i = XtNumber(defaultSubs);
1455        Substitution sub, def;
1456
1457        merged_substitutions = sub = (Substitution)
1458            ALLOCATE_LOCAL((unsigned) (num_substitutions + (Cardinal) i) *
1459                           sizeof(SubstitutionRec));
1460        if (sub == NULL)
1461            _XtAllocError(NULL);
1462        for (def = defaultSubs; i--; sub++, def++)
1463            sub->match = def->match;
1464        for (i = (int) num_substitutions; i--;)
1465            *sub++ = *substitutions++;
1466    }
1467    merged_substitutions[0].substitution = (_XtString) filename;
1468    merged_substitutions[1].substitution = (_XtString) type;
1469    merged_substitutions[2].substitution = (_XtString) suffix;
1470    name_list[0] = pd->name;
1471    name_list[1] = XrmPermStringToQuark("customization");
1472    name_list[2] = NULLQUARK;
1473    class_list[0] = pd->class;
1474    class_list[1] = XrmPermStringToQuark("Customization");
1475    class_list[2] = NULLQUARK;
1476    if (XrmQGetResource(XrmGetDatabase(dpy), name_list, class_list,
1477                        &db_type, &value) && db_type == _XtQString)
1478        merged_substitutions[3].substitution = (char *) value.addr;
1479    else
1480        merged_substitutions[3].substitution = NULL;
1481    FillInLangSubs(&merged_substitutions[4], pd);
1482
1483    result = XtFindFile(massagedPath, merged_substitutions,
1484                        num_substitutions + XtNumber(defaultSubs), predicate);
1485
1486    if (merged_substitutions[5].substitution != NULL)
1487        XtFree((XtPointer) merged_substitutions[5].substitution);
1488
1489    if (merged_substitutions != defaultSubs)
1490        DEALLOCATE_LOCAL(merged_substitutions);
1491
1492    if (pathMallocd)
1493        XtFree(massagedPath);
1494    else
1495        DEALLOCATE_LOCAL(massagedPath);
1496
1497    UNLOCK_PROCESS;
1498    return result;
1499}
1500
1501Boolean
1502XtCallAcceptFocus(Widget widget, Time *time)
1503{
1504    XtAcceptFocusProc ac;
1505    Boolean retval;
1506
1507    WIDGET_TO_APPCON(widget);
1508
1509    LOCK_APP(app);
1510    LOCK_PROCESS;
1511    ac = XtClass(widget)->core_class.accept_focus;
1512    UNLOCK_PROCESS;
1513
1514    if (ac != NULL)
1515        retval = (*ac) (widget, time);
1516    else
1517        retval = FALSE;
1518    UNLOCK_APP(app);
1519    return retval;
1520}
1521
1522#ifdef XT_GEO_TATTLER
1523/**************************************************************************
1524 GeoTattler:  This is used to debug Geometry management in Xt.
1525
1526  It uses a pseudo resource XtNgeotattler.
1527
1528  E.G. if those lines are found in the resource database:
1529
1530    myapp*draw.XmScale.geoTattler: ON
1531    *XmScrollBar.geoTattler:ON
1532    *XmRowColumn.exit_button.geoTattler:ON
1533
1534   then:
1535
1536    all the XmScale children of the widget named draw,
1537    all the XmScrollBars,
1538    the widget named exit_button in any XmRowColumn
1539
1540   will return True to the function IsTattled(), and will generate
1541   outlined trace to stdout.
1542
1543*************************************************************************/
1544
1545#define XtNgeoTattler "geoTattler"
1546#define XtCGeoTattler "GeoTattler"
1547
1548typedef struct {
1549    Boolean geo_tattler;
1550} GeoDataRec;
1551
1552/* *INDENT-OFF* */
1553static XtResource geo_resources[] = {
1554    { XtNgeoTattler, XtCGeoTattler, XtRBoolean, sizeof(Boolean),
1555      XtOffsetOf(GeoDataRec, geo_tattler),
1556      XtRImmediate, (XtPointer) False }
1557};
1558/* *INDENT-ON* */
1559
1560/************************************************************************
1561  This function uses XtGetSubresources to find out if a widget
1562  needs to be geo-spied by the caller. */
1563static Boolean
1564IsTattled(Widget widget)
1565{
1566    GeoDataRec geo_data;
1567
1568    XtGetSubresources(widget, (XtPointer) &geo_data,
1569                      (String) NULL, (String) NULL,
1570                      geo_resources, XtNumber(geo_resources), NULL, 0);
1571
1572    return geo_data.geo_tattler;
1573
1574}                               /* IsTattled */
1575
1576static int n_tab = 0;           /* not MT for now */
1577
1578void
1579_XtGeoTab(int direction)
1580{                               /* +1 or -1 */
1581    n_tab += direction;
1582}
1583
1584void
1585_XtGeoTrace(Widget widget, const char *fmt, ...)
1586{
1587    if (IsTattled(widget)) {
1588        va_list args;
1589        int i;
1590
1591        va_start(args, fmt);
1592        for (i = 0; i < n_tab; i++)
1593            printf("     ");
1594        (void) vprintf(fmt, args);
1595        va_end(args);
1596    }
1597}
1598
1599#endif                          /* XT_GEO_TATTLER */
1600