Event.c revision fdf6a26f
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, 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#ifdef HAVE_CONFIG_H
72#include <config.h>
73#endif
74#include "IntrinsicI.h"
75#include "Shell.h"
76#include "StringDefs.h"
77
78typedef struct _XtEventRecExt {
79    int type;
80    XtPointer select_data[1];   /* actual dimension is [mask] */
81} XtEventRecExt;
82
83#define EXT_TYPE(p) (((XtEventRecExt*) ((p)+1))->type)
84#define EXT_SELECT_DATA(p,n) (((XtEventRecExt*) ((p)+1))->select_data[n])
85
86#define NonMaskableMask ((EventMask)0x80000000L)
87
88/*
89 * These are definitions to make the code that handles exposure compression
90 * easier to read.
91 *
92 * COMP_EXPOSE      - The compression exposure field of "widget"
93 * COMP_EXPOSE_TYPE - The type of compression (lower 4 bits of COMP_EXPOSE.
94 * GRAPHICS_EXPOSE  - TRUE if the widget wants graphics expose events
95 *                    dispatched.
96 * NO_EXPOSE        - TRUE if the widget wants No expose events dispatched.
97 */
98
99#define COMP_EXPOSE   (widget->core.widget_class->core_class.compress_exposure)
100#define COMP_EXPOSE_TYPE (COMP_EXPOSE & 0x0f)
101#define GRAPHICS_EXPOSE  ((XtExposeGraphicsExpose & COMP_EXPOSE) || \
102                          (XtExposeGraphicsExposeMerged & COMP_EXPOSE))
103#define NO_EXPOSE        (XtExposeNoExpose & COMP_EXPOSE)
104
105EventMask
106XtBuildEventMask(Widget widget)
107{
108    EventMask mask = 0L;
109
110    if (widget != NULL) {
111        XtEventTable ev;
112
113        WIDGET_TO_APPCON(widget);
114
115        LOCK_APP(app);
116        for (ev = widget->core.event_table; ev != NULL; ev = ev->next) {
117            if (!ev->select)
118                continue;
119
120            if (!ev->has_type_specifier)
121                mask |= ev->mask;
122            else {
123                if (EXT_TYPE(ev) < LASTEvent) {
124                    Cardinal i;
125
126                    for (i = 0; i < ev->mask; i++)
127                        if (EXT_SELECT_DATA(ev, i))
128                            mask |= *(EventMask *) EXT_SELECT_DATA(ev, i);
129                }
130            }
131        }
132        LOCK_PROCESS;
133        if (widget->core.widget_class->core_class.expose != NULL)
134            mask |= ExposureMask;
135        if (widget->core.widget_class->core_class.visible_interest)
136            mask |= VisibilityChangeMask;
137        UNLOCK_PROCESS;
138        if (widget->core.tm.translations)
139            mask |= widget->core.tm.translations->eventMask;
140
141        mask = mask & ~NonMaskableMask;
142        UNLOCK_APP(app);
143    }
144    return mask;
145}
146
147static void
148CallExtensionSelector(Widget widget, ExtSelectRec *rec, Boolean forceCall)
149{
150    XtEventRec *p;
151    XtPointer *data;
152    int *types;
153    Cardinal i, count = 0;
154
155    for (p = widget->core.event_table; p != NULL; p = p->next)
156        if (p->has_type_specifier &&
157            EXT_TYPE(p) >= rec->min && EXT_TYPE(p) <= rec->max)
158            count = (Cardinal) (count + p->mask);
159
160    if (count == 0 && !forceCall)
161        return;
162
163    data = (XtPointer *) ALLOCATE_LOCAL(count * sizeof(XtPointer));
164    types = (int *) ALLOCATE_LOCAL(count * sizeof(int));
165    count = 0;
166
167    for (p = widget->core.event_table; p != NULL; p = p->next)
168        if (p->has_type_specifier &&
169            EXT_TYPE(p) >= rec->min && EXT_TYPE(p) <= rec->max)
170            for (i = 0; i < p->mask; i++) {
171                types[count] = EXT_TYPE(p);
172                data[count++] = EXT_SELECT_DATA(p, i);
173            }
174
175    (*rec->proc) (widget, types, data, (int) count, rec->client_data);
176    DEALLOCATE_LOCAL((char *) types);
177    DEALLOCATE_LOCAL((char *) data);
178}
179
180static void
181RemoveEventHandler(Widget widget,
182                   XtPointer select_data,
183                   int type,
184                   Boolean has_type_specifier,
185                   Boolean other,
186                   const XtEventHandler proc,
187                   const XtPointer closure,
188                   Boolean raw)
189{
190    XtEventRec *p, **pp;
191    EventMask oldMask = XtBuildEventMask(widget);
192
193    if (raw)
194        raw = 1;
195    pp = &widget->core.event_table;
196    while ((p = *pp) &&
197           (p->proc != proc || p->closure != closure || p->select == raw ||
198            has_type_specifier != p->has_type_specifier ||
199            (has_type_specifier && EXT_TYPE(p) != type)))
200        pp = &p->next;
201    if (!p)
202        return;
203
204    /* un-register it */
205    if (!has_type_specifier) {
206        EventMask eventMask = *(EventMask *) select_data;
207
208        eventMask &= ~NonMaskableMask;
209        if (other)
210            eventMask |= NonMaskableMask;
211        p->mask &= ~eventMask;
212    }
213    else {
214        Cardinal i;
215
216        /* p->mask specifies count of EXT_SELECT_DATA(p,i)
217         * search through the list of selection data, if not found
218         * don't remove this handler
219         */
220        for (i = 0; i < p->mask && select_data != EXT_SELECT_DATA(p, i);)
221            i++;
222        if (i == p->mask)
223            return;
224        if (p->mask == 1)
225            p->mask = 0;
226        else {
227            p->mask--;
228            while (i < p->mask) {
229                EXT_SELECT_DATA(p, i) = EXT_SELECT_DATA(p, i + 1);
230                i++;
231            }
232        }
233    }
234
235    if (!p->mask) {             /* delete it entirely */
236        *pp = p->next;
237        XtFree((char *) p);
238    }
239
240    /* Reset select mask if realized and not raw. */
241    if (!raw && XtIsRealized(widget) && !widget->core.being_destroyed) {
242        EventMask mask = XtBuildEventMask(widget);
243        Display *dpy = XtDisplay(widget);
244
245        if (oldMask != mask)
246            XSelectInput(dpy, XtWindow(widget), (long) mask);
247
248        if (has_type_specifier) {
249            XtPerDisplay pd = _XtGetPerDisplay(dpy);
250            int i;
251
252            for (i = 0; i < pd->ext_select_count; i++) {
253                if (type >= pd->ext_select_list[i].min &&
254                    type <= pd->ext_select_list[i].max) {
255                    CallExtensionSelector(widget, pd->ext_select_list + i,
256                                          TRUE);
257                    break;
258                }
259                if (type < pd->ext_select_list[i].min)
260                    break;
261            }
262        }
263    }
264}
265
266/*      Function Name: AddEventHandler
267 *      Description: An Internal routine that does the actual work of
268 *                   adding the event handlers.
269 *      Arguments: widget - widget to register an event handler for.
270 *                 eventMask - events to mask for.
271 *                 other - pass non maskable events to this procedure.
272 *                 proc - procedure to register.
273 *                 closure - data to pass to the event handler.
274 *                 position - where to add this event handler.
275 *                 force_new_position - If the element is already in the
276 *                                      list, this will force it to the
277 *                                      beginning or end depending on position.
278 *                 raw - If FALSE call XSelectInput for events in mask.
279 *      Returns: none
280 */
281
282static void
283AddEventHandler(Widget widget,
284                XtPointer select_data,
285                int type,
286                Boolean has_type_specifier,
287                Boolean other,
288                XtEventHandler proc,
289                XtPointer closure,
290                XtListPosition position,
291                Boolean force_new_position,
292                Boolean raw)
293{
294    register XtEventRec *p, **pp;
295    EventMask oldMask = 0, eventMask = 0;
296
297    if (!has_type_specifier) {
298        eventMask = *(EventMask *) select_data & ~NonMaskableMask;
299        if (other)
300            eventMask |= NonMaskableMask;
301        if (!eventMask)
302            return;
303    }
304    else if (!type)
305        return;
306
307    if (XtIsRealized(widget) && !raw)
308        oldMask = XtBuildEventMask(widget);
309
310    if (raw)
311        raw = 1;
312    pp = &widget->core.event_table;
313    while ((p = *pp) &&
314           (p->proc != proc || p->closure != closure || p->select == raw ||
315            has_type_specifier != p->has_type_specifier ||
316            (has_type_specifier && EXT_TYPE(p) != type)))
317        pp = &p->next;
318
319    if (!p) {                   /* New proc to add to list */
320        if (has_type_specifier) {
321            p = (XtEventRec *) __XtMalloc(sizeof(XtEventRec) +
322                                          sizeof(XtEventRecExt));
323            EXT_TYPE(p) = type;
324            EXT_SELECT_DATA(p, 0) = select_data;
325            p->mask = 1;
326            p->has_type_specifier = True;
327        }
328        else {
329            p = (XtEventRec *) __XtMalloc(sizeof(XtEventRec));
330            p->mask = eventMask;
331            p->has_type_specifier = False;
332        }
333        p->proc = proc;
334        p->closure = closure;
335        p->select = !raw;
336
337        if (position == XtListHead) {
338            p->next = widget->core.event_table;
339            widget->core.event_table = p;
340        }
341        else {
342            *pp = p;
343            p->next = NULL;
344        }
345    }
346    else {
347        if (force_new_position) {
348            *pp = p->next;
349
350            if (position == XtListHead) {
351                p->next = widget->core.event_table;
352                widget->core.event_table = p;
353            }
354            else {
355                /*
356                 * Find the last element in the list.
357                 */
358                while (*pp)
359                    pp = &(*pp)->next;
360                *pp = p;
361                p->next = NULL;
362            }
363        }
364
365        if (!has_type_specifier)
366            p->mask |= eventMask;
367        else {
368            Cardinal i;
369
370            /* p->mask specifies count of EXT_SELECT_DATA(p,i) */
371            for (i = 0; i < p->mask && select_data != EXT_SELECT_DATA(p, i);)
372                i++;
373            if (i == p->mask) {
374                p = (XtEventRec *) XtRealloc((char *) p,
375                                             (Cardinal) (sizeof(XtEventRec) +
376                                                         sizeof(XtEventRecExt) +
377                                                         p->mask *
378                                                         sizeof(XtPointer)));
379                EXT_SELECT_DATA(p, i) = select_data;
380                p->mask++;
381                *pp = p;
382            }
383        }
384    }
385
386    if (XtIsRealized(widget) && !raw) {
387        EventMask mask = XtBuildEventMask(widget);
388        Display *dpy = XtDisplay(widget);
389
390        if (oldMask != mask)
391            XSelectInput(dpy, XtWindow(widget), (long) mask);
392
393        if (has_type_specifier) {
394            XtPerDisplay pd = _XtGetPerDisplay(dpy);
395            int i;
396
397            for (i = 0; i < pd->ext_select_count; i++) {
398                if (type >= pd->ext_select_list[i].min &&
399                    type <= pd->ext_select_list[i].max) {
400                    CallExtensionSelector(widget, pd->ext_select_list + i,
401                                          FALSE);
402                    break;
403                }
404                if (type < pd->ext_select_list[i].min)
405                    break;
406            }
407        }
408    }
409}
410
411void
412XtRemoveEventHandler(Widget widget,
413                     EventMask eventMask,
414                     _XtBoolean other,
415                     XtEventHandler proc,
416                     XtPointer closure)
417{
418    WIDGET_TO_APPCON(widget);
419    LOCK_APP(app);
420    RemoveEventHandler(widget, (XtPointer) &eventMask, 0, FALSE,
421                       (Boolean) other, proc, closure, FALSE);
422    UNLOCK_APP(app);
423}
424
425void
426XtAddEventHandler(Widget widget,
427                  EventMask eventMask,
428                  _XtBoolean other,
429                  XtEventHandler proc,
430                  XtPointer closure)
431{
432    WIDGET_TO_APPCON(widget);
433    LOCK_APP(app);
434    AddEventHandler(widget, (XtPointer) &eventMask, 0, FALSE, (Boolean) other,
435                    proc, closure, XtListTail, FALSE, FALSE);
436    UNLOCK_APP(app);
437}
438
439void
440XtInsertEventHandler(Widget widget,
441                     EventMask eventMask,
442                     _XtBoolean other,
443                     XtEventHandler proc,
444                     XtPointer closure,
445                     XtListPosition position)
446{
447    WIDGET_TO_APPCON(widget);
448    LOCK_APP(app);
449    AddEventHandler(widget, (XtPointer) &eventMask, 0, FALSE, (Boolean) other,
450                    proc, closure, position, TRUE, FALSE);
451    UNLOCK_APP(app);
452}
453
454void
455XtRemoveRawEventHandler(Widget widget,
456                        EventMask eventMask,
457                        _XtBoolean other,
458                        XtEventHandler proc,
459                        XtPointer closure)
460{
461    WIDGET_TO_APPCON(widget);
462    LOCK_APP(app);
463    RemoveEventHandler(widget, (XtPointer) &eventMask, 0, FALSE,
464                       (Boolean) other, proc, closure, TRUE);
465    UNLOCK_APP(app);
466}
467
468void
469XtInsertRawEventHandler(Widget widget,
470                        EventMask eventMask,
471                        _XtBoolean other,
472                        XtEventHandler proc,
473                        XtPointer closure,
474                        XtListPosition position)
475{
476    WIDGET_TO_APPCON(widget);
477    LOCK_APP(app);
478    AddEventHandler(widget, (XtPointer) &eventMask, 0, FALSE, (Boolean) other,
479                    proc, closure, position, TRUE, TRUE);
480    UNLOCK_APP(app);
481}
482
483void
484XtAddRawEventHandler(Widget widget,
485                     EventMask eventMask,
486                     _XtBoolean other,
487                     XtEventHandler proc,
488                     XtPointer closure)
489{
490    WIDGET_TO_APPCON(widget);
491    LOCK_APP(app);
492    AddEventHandler(widget, (XtPointer) &eventMask, 0, FALSE, (Boolean) other,
493                    proc, closure, XtListTail, FALSE, TRUE);
494    UNLOCK_APP(app);
495}
496
497void
498XtRemoveEventTypeHandler(Widget widget,
499                         int type,
500                         XtPointer select_data,
501                         XtEventHandler proc,
502                         XtPointer closure)
503{
504    WIDGET_TO_APPCON(widget);
505    LOCK_APP(app);
506    RemoveEventHandler(widget, select_data, type, TRUE,
507                       FALSE, proc, closure, FALSE);
508    UNLOCK_APP(app);
509}
510
511void
512XtInsertEventTypeHandler(Widget widget,
513                         int type,
514                         XtPointer select_data,
515                         XtEventHandler proc,
516                         XtPointer closure,
517                         XtListPosition position)
518{
519    WIDGET_TO_APPCON(widget);
520    LOCK_APP(app);
521    AddEventHandler(widget, select_data, type, TRUE, FALSE,
522                    proc, closure, position, TRUE, FALSE);
523    UNLOCK_APP(app);
524}
525
526typedef struct _WWPair {
527    struct _WWPair *next;
528    Window window;
529    Widget widget;
530} *WWPair;
531
532typedef struct _WWTable {
533    unsigned int mask;          /* size of hash table - 1 */
534    unsigned int rehash;        /* mask - 2 */
535    unsigned int occupied;      /* number of occupied entries */
536    unsigned int fakes;         /* number occupied by WWfake */
537    Widget *entries;            /* the entries */
538    WWPair pairs;               /* bogus entries */
539} *WWTable;
540
541static const WidgetRec WWfake;  /* placeholder for deletions */
542
543#define WWHASH(tab,win) ((win) & tab->mask)
544#define WWREHASHVAL(tab,win) ((((win) % tab->rehash) + 2) | 1)
545#define WWREHASH(tab,idx,rehash) ((unsigned)(idx + rehash) & (tab->mask))
546#define WWTABLE(display) (_XtGetPerDisplay(display)->WWtable)
547
548static void ExpandWWTable(WWTable);
549
550void
551XtRegisterDrawable(Display *display, Drawable drawable, Widget widget)
552{
553    WWTable tab;
554    int idx;
555    Widget entry;
556    Window window = (Window) drawable;
557
558    WIDGET_TO_APPCON(widget);
559
560    LOCK_APP(app);
561    LOCK_PROCESS;
562    tab = WWTABLE(display);
563
564    if (window != XtWindow(widget)) {
565        WWPair pair;
566        pair = XtNew(struct _WWPair);
567
568        pair->next = tab->pairs;
569        pair->window = window;
570        pair->widget = widget;
571        tab->pairs = pair;
572        UNLOCK_PROCESS;
573        UNLOCK_APP(app);
574        return;
575    }
576    if ((tab->occupied + (tab->occupied >> 2)) > tab->mask)
577        ExpandWWTable(tab);
578
579    idx = (int) WWHASH(tab, window);
580    if ((entry = tab->entries[idx]) && entry != &WWfake) {
581        int rehash = (int) WWREHASHVAL(tab, window);
582
583        do {
584            idx = (int) WWREHASH(tab, idx, rehash);
585        } while ((entry = tab->entries[idx]) && entry != &WWfake);
586    }
587    if (!entry)
588        tab->occupied++;
589    else if (entry == &WWfake)
590        tab->fakes--;
591    tab->entries[idx] = widget;
592    UNLOCK_PROCESS;
593    UNLOCK_APP(app);
594}
595
596void
597XtUnregisterDrawable(Display *display, Drawable drawable)
598{
599    WWTable tab;
600    int idx;
601    Widget entry;
602    Window window = (Window) drawable;
603    Widget widget = XtWindowToWidget(display, window);
604    DPY_TO_APPCON(display);
605
606    if (widget == NULL)
607        return;
608
609    LOCK_APP(app);
610    LOCK_PROCESS;
611    tab = WWTABLE(display);
612    if (window != XtWindow(widget)) {
613        WWPair *prev, pair;
614
615        prev = &tab->pairs;
616        while ((pair = *prev) && pair->window != window)
617            prev = &pair->next;
618        if (pair) {
619            *prev = pair->next;
620            XtFree((char *) pair);
621        }
622        UNLOCK_PROCESS;
623        UNLOCK_APP(app);
624        return;
625    }
626    idx = (int) WWHASH(tab, window);
627    if ((entry = tab->entries[idx])) {
628        if (entry != widget) {
629            int rehash = (int) WWREHASHVAL(tab, window);
630
631            do {
632                idx = (int) WWREHASH(tab, idx, rehash);
633                if (!(entry = tab->entries[idx])) {
634                    UNLOCK_PROCESS;
635                    UNLOCK_APP(app);
636                    return;
637                }
638            } while (entry != widget);
639        }
640        tab->entries[idx] = (Widget) &WWfake;
641        tab->fakes++;
642    }
643    UNLOCK_PROCESS;
644    UNLOCK_APP(app);
645}
646
647static void
648ExpandWWTable(register WWTable tab)
649{
650    unsigned int oldmask;
651    register Widget *oldentries, *entries;
652    register Cardinal oldidx, newidx, rehash;
653    register Widget entry;
654
655    LOCK_PROCESS;
656    oldmask = tab->mask;
657    oldentries = tab->entries;
658    tab->occupied -= tab->fakes;
659    tab->fakes = 0;
660    if ((tab->occupied + (tab->occupied >> 2)) > tab->mask) {
661        tab->mask = (tab->mask << 1) + 1;
662        tab->rehash = tab->mask - 2;
663    }
664    entries = tab->entries =
665        (Widget *) __XtCalloc(tab->mask + 1, sizeof(Widget));
666    for (oldidx = 0; oldidx <= oldmask; oldidx++) {
667        if ((entry = oldentries[oldidx]) && entry != &WWfake) {
668            newidx = (Cardinal) WWHASH(tab, XtWindow(entry));
669            if (entries[newidx]) {
670                rehash = (Cardinal) WWREHASHVAL(tab, XtWindow(entry));
671                do {
672                    newidx = (Cardinal) WWREHASH(tab, newidx, rehash);
673                } while (entries[newidx]);
674            }
675            entries[newidx] = entry;
676        }
677    }
678    XtFree((char *) oldentries);
679    UNLOCK_PROCESS;
680}
681
682Widget
683XtWindowToWidget(register Display *display, register Window window)
684{
685    WWTable tab;
686    int idx;
687    Widget entry;
688    WWPair pair;
689    DPY_TO_APPCON(display);
690
691    if (!window)
692        return NULL;
693
694    LOCK_APP(app);
695    LOCK_PROCESS;
696    tab = WWTABLE(display);
697    idx = (int) WWHASH(tab, window);
698    if ((entry = tab->entries[idx]) && XtWindow(entry) != window) {
699        int rehash = (int) WWREHASHVAL(tab, window);
700
701        do {
702            idx = (int) WWREHASH(tab, idx, rehash);
703        } while ((entry = tab->entries[idx]) && XtWindow(entry) != window);
704    }
705    if (entry) {
706        UNLOCK_PROCESS;
707        UNLOCK_APP(app);
708        return entry;
709    }
710    for (pair = tab->pairs; pair; pair = pair->next) {
711        if (pair->window == window) {
712            entry = pair->widget;
713            UNLOCK_PROCESS;
714            UNLOCK_APP(app);
715            return entry;
716        }
717    }
718    UNLOCK_PROCESS;
719    UNLOCK_APP(app);
720    return NULL;
721}
722
723void
724_XtAllocWWTable(XtPerDisplay pd)
725{
726    register WWTable tab;
727
728    tab = (WWTable) __XtMalloc(sizeof(struct _WWTable));
729    tab->mask = 0x7f;
730    tab->rehash = tab->mask - 2;
731    tab->entries = (Widget *) __XtCalloc(tab->mask + 1, sizeof(Widget));
732    tab->occupied = 0;
733    tab->fakes = 0;
734    tab->pairs = NULL;
735    pd->WWtable = tab;
736}
737
738void
739_XtFreeWWTable(register XtPerDisplay pd)
740{
741    register WWPair pair, next;
742
743    for (pair = pd->WWtable->pairs; pair; pair = next) {
744        next = pair->next;
745        XtFree((char *) pair);
746    }
747    XtFree((char *) pd->WWtable->entries);
748    XtFree((char *) pd->WWtable);
749}
750
751#define EHMAXSIZE 25            /* do not make whopping big */
752
753static Boolean
754CallEventHandlers(Widget widget, XEvent *event, EventMask mask)
755{
756    register XtEventRec *p;
757    XtEventHandler *proc;
758    XtPointer *closure;
759    Boolean cont_to_disp = True;
760    int i, numprocs;
761
762    /* Have to copy the procs into an array, because one of them might
763     * call XtRemoveEventHandler, which would break our linked list. */
764
765    numprocs = 0;
766    for (p = widget->core.event_table; p; p = p->next) {
767        if ((!p->has_type_specifier && (mask & p->mask)) ||
768            (p->has_type_specifier && event->type == EXT_TYPE(p)))
769            numprocs++;
770    }
771    proc = XtMallocArray((Cardinal) numprocs, (Cardinal)
772                         (sizeof(XtEventHandler) + sizeof(XtPointer)));
773    closure = (XtPointer *) (proc + numprocs);
774
775    numprocs = 0;
776    for (p = widget->core.event_table; p; p = p->next) {
777        if ((!p->has_type_specifier && (mask & p->mask)) ||
778            (p->has_type_specifier && event->type == EXT_TYPE(p))) {
779            proc[numprocs] = p->proc;
780            closure[numprocs] = p->closure;
781            numprocs++;
782        }
783    }
784    /* FUNCTIONS CALLED THROUGH POINTER proc:
785       Selection.c:ReqCleanup,
786       "Shell.c":EventHandler,
787       PassivGrab.c:ActiveHandler,
788       PassivGrab.c:RealizeHandler,
789       Keyboard.c:QueryEventMask,
790       _XtHandleFocus,
791       Selection.c:HandleSelectionReplies,
792       Selection.c:HandleGetIncrement,
793       Selection.c:HandleIncremental,
794       Selection.c:HandlePropertyGone,
795       Selection.c:HandleSelectionEvents
796     */
797    for (i = 0; i < numprocs && cont_to_disp; i++)
798        (*(proc[i])) (widget, closure[i], event, &cont_to_disp);
799    XtFree((char *) proc);
800    return cont_to_disp;
801}
802
803static void CompressExposures(XEvent *, Widget);
804
805#define KnownButtons (Button1MotionMask|Button2MotionMask|Button3MotionMask|\
806                      Button4MotionMask|Button5MotionMask)
807
808/* keep this SMALL to avoid blowing stack cache! */
809/* because some compilers allocate all local locals on procedure entry */
810#define EHSIZE 4
811
812Boolean
813XtDispatchEventToWidget(Widget widget, XEvent *event)
814{
815    register XtEventRec *p;
816    Boolean was_dispatched = False;
817    Boolean call_tm = False;
818    Boolean cont_to_disp;
819    EventMask mask;
820
821    WIDGET_TO_APPCON(widget);
822
823    LOCK_APP(app);
824
825    mask = _XtConvertTypeToMask(event->type);
826    if (event->type == MotionNotify)
827        mask |= (event->xmotion.state & KnownButtons);
828
829    LOCK_PROCESS;
830    if ((mask == ExposureMask) ||
831        ((event->type == NoExpose) && NO_EXPOSE) ||
832        ((event->type == GraphicsExpose) && GRAPHICS_EXPOSE)) {
833
834        if (widget->core.widget_class->core_class.expose != NULL) {
835
836            /* We need to mask off the bits that could contain the information
837             * about whether or not we desire Graphics and NoExpose events.  */
838
839            if ((COMP_EXPOSE_TYPE == XtExposeNoCompress) ||
840                (event->type == NoExpose))
841
842                (*widget->core.widget_class->core_class.expose)
843                    (widget, event, (Region) NULL);
844            else {
845                CompressExposures(event, widget);
846            }
847            was_dispatched = True;
848        }
849    }
850
851    if ((mask == VisibilityChangeMask) &&
852        XtClass(widget)->core_class.visible_interest) {
853        was_dispatched = True;
854        /* our visibility just changed... */
855        switch (((XVisibilityEvent *) event)->state) {
856        case VisibilityUnobscured:
857            widget->core.visible = TRUE;
858            break;
859
860        case VisibilityPartiallyObscured:
861            /* what do we want to say here? */
862            /* well... some of us is visible */
863            widget->core.visible = TRUE;
864            break;
865
866        case VisibilityFullyObscured:
867            widget->core.visible = FALSE;
868            /* do we want to mark our children obscured? */
869            break;
870        }
871    }
872    UNLOCK_PROCESS;
873
874    /* to maintain "copy" semantics we check TM now but call later */
875    if (widget->core.tm.translations &&
876        (mask & widget->core.tm.translations->eventMask))
877        call_tm = True;
878
879    cont_to_disp = True;
880    p = widget->core.event_table;
881    if (p) {
882        if (p->next) {
883            XtEventHandler proc[EHSIZE];
884            XtPointer closure[EHSIZE];
885            int numprocs = 0;
886
887            /* Have to copy the procs into an array, because one of them might
888             * call XtRemoveEventHandler, which would break our linked list. */
889
890            for (; p; p = p->next) {
891                if ((!p->has_type_specifier && (mask & p->mask)) ||
892                    (p->has_type_specifier && event->type == EXT_TYPE(p))) {
893                    if (numprocs >= EHSIZE)
894                        break;
895                    proc[numprocs] = p->proc;
896                    closure[numprocs] = p->closure;
897                    numprocs++;
898                }
899            }
900            if (numprocs) {
901                if (p) {
902                    cont_to_disp = CallEventHandlers(widget, event, mask);
903                }
904                else {
905                    int i;
906
907                    for (i = 0; i < numprocs && cont_to_disp; i++)
908                        (*(proc[i])) (widget, closure[i], event, &cont_to_disp);
909                    /* FUNCTIONS CALLED THROUGH POINTER proc:
910                       Selection.c:ReqCleanup,
911                       "Shell.c":EventHandler,
912                       PassivGrab.c:ActiveHandler,
913                       PassivGrab.c:RealizeHandler,
914                       Keyboard.c:QueryEventMask,
915                       _XtHandleFocus,
916                       Selection.c:HandleSelectionReplies,
917                       Selection.c:HandleGetIncrement,
918                       Selection.c:HandleIncremental,
919                       Selection.c:HandlePropertyGone,
920                       Selection.c:HandleSelectionEvents
921                     */
922                }
923                was_dispatched = True;
924            }
925        }
926        else if ((!p->has_type_specifier && (mask & p->mask)) ||
927                 (p->has_type_specifier && event->type == EXT_TYPE(p))) {
928            (*p->proc) (widget, p->closure, event, &cont_to_disp);
929            was_dispatched = True;
930        }
931    }
932    if (call_tm && cont_to_disp)
933        _XtTranslateEvent(widget, event);
934    UNLOCK_APP(app);
935    return (was_dispatched | call_tm);
936}
937
938/*
939 * This structure is passed into the check exposure proc.
940 */
941
942typedef struct _CheckExposeInfo {
943    int type1, type2;           /* Types of events to check for. */
944    Boolean maximal;            /* Ignore non-exposure events? */
945    Boolean non_matching;       /* Was there an event that did not
946                                   match either type? */
947    Window window;              /* Window to match. */
948} CheckExposeInfo;
949
950#define GetCount(ev) (((XExposeEvent *)(ev))->count)
951
952static void SendExposureEvent(XEvent *, Widget, XtPerDisplay);
953static Bool CheckExposureEvent(Display *, XEvent *, char *);
954static void AddExposureToRectangularRegion(XEvent *, Region);
955
956/*      Function Name: CompressExposures
957 *      Description: Handles all exposure compression
958 *      Arguments: event - the xevent that is to be dispatched
959 *                 widget - the widget that this event occurred in.
960 *      Returns: none.
961 *
962 *      NOTE: Event must be of type Expose or GraphicsExpose.
963 */
964
965static void
966CompressExposures(XEvent *event, Widget widget)
967{
968    CheckExposeInfo info;
969    int count;
970    Display *dpy = XtDisplay(widget);
971    XtPerDisplay pd = _XtGetPerDisplay(dpy);
972    XtEnum comp_expose;
973    XtEnum comp_expose_type;
974    Boolean no_region;
975
976    LOCK_PROCESS;
977    comp_expose = COMP_EXPOSE;
978    UNLOCK_PROCESS;
979    comp_expose_type = comp_expose & 0x0f;
980    no_region = ((comp_expose & XtExposeNoRegion) ? True : False);
981
982    if (no_region)
983        AddExposureToRectangularRegion(event, pd->region);
984    else
985        XtAddExposureToRegion(event, pd->region);
986
987    if (GetCount(event) != 0)
988        return;
989
990    if ((comp_expose_type == XtExposeCompressSeries) ||
991        (XEventsQueued(dpy, QueuedAfterReading) == 0)) {
992        SendExposureEvent(event, widget, pd);
993        return;
994    }
995
996    if (comp_expose & XtExposeGraphicsExposeMerged) {
997        info.type1 = Expose;
998        info.type2 = GraphicsExpose;
999    }
1000    else {
1001        info.type1 = event->type;
1002        info.type2 = 0;
1003    }
1004    info.maximal = (comp_expose_type == XtExposeCompressMaximal);
1005    info.non_matching = FALSE;
1006    info.window = XtWindow(widget);
1007
1008    /*
1009     * We have to be very careful here not to hose down the processor
1010     * when blocking until count gets to zero.
1011     *
1012     * First, check to see if there are any events in the queue for this
1013     * widget, and of the correct type.
1014     *
1015     * Once we cannot find any more events, check to see that count is zero.
1016     * If it is not then block until we get another exposure event.
1017     *
1018     * If we find no more events, and count on the last one we saw was zero we
1019     * we can be sure that all events have been processed.
1020     *
1021     * Unfortunately, we wind up having to look at the entire queue
1022     * event if we're not doing Maximal compression, due to the
1023     * semantics of XCheckIfEvent (we can't abort without re-ordering
1024     * the event queue as a side-effect).
1025     */
1026
1027    count = 0;
1028    while (TRUE) {
1029        XEvent event_return;
1030
1031        if (XCheckIfEvent(dpy, &event_return,
1032                          CheckExposureEvent, (char *) &info)) {
1033
1034            count = GetCount(&event_return);
1035            if (no_region)
1036                AddExposureToRectangularRegion(&event_return, pd->region);
1037            else
1038                XtAddExposureToRegion(&event_return, pd->region);
1039        }
1040        else if (count != 0) {
1041            XIfEvent(dpy, &event_return, CheckExposureEvent, (char *) &info);
1042            count = GetCount(&event_return);
1043            if (no_region)
1044                AddExposureToRectangularRegion(&event_return, pd->region);
1045            else
1046                XtAddExposureToRegion(&event_return, pd->region);
1047        }
1048        else                    /* count == 0 && XCheckIfEvent Failed. */
1049            break;
1050    }
1051
1052    SendExposureEvent(event, widget, pd);
1053}
1054
1055void
1056XtAddExposureToRegion(XEvent *event, Region region)
1057{
1058    XRectangle rect;
1059    XExposeEvent *ev = (XExposeEvent *) event;
1060
1061    /* These Expose and GraphicsExpose fields are at identical offsets */
1062
1063    if (event->type == Expose || event->type == GraphicsExpose) {
1064        rect.x = (Position) ev->x;
1065        rect.y = (Position) ev->y;
1066        rect.width = (Dimension) ev->width;
1067        rect.height = (Dimension) ev->height;
1068        XUnionRectWithRegion(&rect, region, region);
1069    }
1070}
1071
1072#ifndef MAX
1073#define MAX(a,b) (((a) > (b)) ? (a) : (b))
1074#endif
1075
1076#ifndef MIN
1077#define MIN(a,b) (((a) < (b)) ? (a) : (b))
1078#endif
1079
1080static void
1081AddExposureToRectangularRegion(XEvent *event,   /* when called internally, type is always appropriate */
1082                               Region region)
1083{
1084    XRectangle rect;
1085    XExposeEvent *ev = (XExposeEvent *) event;
1086
1087    /* These Expose and GraphicsExpose fields are at identical offsets */
1088
1089    rect.x = (Position) ev->x;
1090    rect.y = (Position) ev->y;
1091    rect.width = (Dimension) ev->width;
1092    rect.height = (Dimension) ev->height;
1093
1094    if (XEmptyRegion(region)) {
1095        XUnionRectWithRegion(&rect, region, region);
1096    }
1097    else {
1098        XRectangle merged, bbox;
1099
1100        XClipBox(region, &bbox);
1101        merged.x = MIN(rect.x, bbox.x);
1102        merged.y = MIN(rect.y, bbox.y);
1103        merged.width = (unsigned short) (MAX(rect.x + rect.width,
1104                                             bbox.x + bbox.width) - merged.x);
1105        merged.height = (unsigned short) (MAX(rect.y + rect.height,
1106                                              bbox.y + bbox.height) - merged.y);
1107        XUnionRectWithRegion(&merged, region, region);
1108    }
1109}
1110
1111static Region nullRegion;
1112
1113/* READ-ONLY VARIABLES: nullRegion */
1114
1115void
1116_XtEventInitialize(void)
1117{
1118#ifndef __lock_lint
1119    nullRegion = XCreateRegion();
1120#endif
1121}
1122
1123/*      Function Name: SendExposureEvent
1124 *      Description: Sets the x, y, width, and height of the event
1125 *                   to be the clip box of Expose Region.
1126 *      Arguments: event  - the X Event to mangle; Expose or GraphicsExpose.
1127 *                 widget - the widget that this event occurred in.
1128 *                 pd     - the per display information for this widget.
1129 *      Returns: none.
1130 */
1131
1132static void
1133SendExposureEvent(XEvent *event, Widget widget, XtPerDisplay pd)
1134{
1135    XtExposeProc expose;
1136    XRectangle rect;
1137    XtEnum comp_expose;
1138    XExposeEvent *ev = (XExposeEvent *) event;
1139
1140    XClipBox(pd->region, &rect);
1141    ev->x = rect.x;
1142    ev->y = rect.y;
1143    ev->width = rect.width;
1144    ev->height = rect.height;
1145
1146    LOCK_PROCESS;
1147    comp_expose = COMP_EXPOSE;
1148    expose = widget->core.widget_class->core_class.expose;
1149    UNLOCK_PROCESS;
1150    if (comp_expose & XtExposeNoRegion)
1151        (*expose) (widget, event, NULL);
1152    else
1153        (*expose) (widget, event, pd->region);
1154    (void) XIntersectRegion(nullRegion, pd->region, pd->region);
1155}
1156
1157/*      Function Name: CheckExposureEvent
1158 *      Description: Checks the event queue for an expose event
1159 *      Arguments: display - the display connection.
1160 *                 event - the event to check.
1161 *                 arg - a pointer to the exposure info structure.
1162 *      Returns: TRUE if we found an event of the correct type
1163 *               with the right window.
1164 *
1165 * NOTE: The only valid types (info.type1 and info.type2) are Expose
1166 *       and GraphicsExpose.
1167 */
1168
1169static Bool
1170CheckExposureEvent(Display *disp _X_UNUSED, XEvent *event, char *arg)
1171{
1172    CheckExposeInfo *info = ((CheckExposeInfo *) arg);
1173
1174    if ((info->type1 == event->type) || (info->type2 == event->type)) {
1175        if (!info->maximal && info->non_matching)
1176            return FALSE;
1177        if (event->type == GraphicsExpose)
1178            return (event->xgraphicsexpose.drawable == info->window);
1179        return (event->xexpose.window == info->window);
1180    }
1181    info->non_matching = TRUE;
1182    return (FALSE);
1183}
1184
1185static EventMask const masks[] = {
1186    0,                          /* Error, should never see  */
1187    0,                          /* Reply, should never see  */
1188    KeyPressMask,               /* KeyPress                 */
1189    KeyReleaseMask,             /* KeyRelease               */
1190    ButtonPressMask,            /* ButtonPress              */
1191    ButtonReleaseMask,          /* ButtonRelease            */
1192    PointerMotionMask           /* MotionNotify             */
1193        | ButtonMotionMask,
1194    EnterWindowMask,            /* EnterNotify              */
1195    LeaveWindowMask,            /* LeaveNotify              */
1196    FocusChangeMask,            /* FocusIn                  */
1197    FocusChangeMask,            /* FocusOut                 */
1198    KeymapStateMask,            /* KeymapNotify             */
1199    ExposureMask,               /* Expose                   */
1200    NonMaskableMask,            /* GraphicsExpose, in GC    */
1201    NonMaskableMask,            /* NoExpose, in GC          */
1202    VisibilityChangeMask,       /* VisibilityNotify         */
1203    SubstructureNotifyMask,     /* CreateNotify             */
1204    StructureNotifyMask         /* DestroyNotify            */
1205        | SubstructureNotifyMask,
1206    StructureNotifyMask         /* UnmapNotify              */
1207        | SubstructureNotifyMask,
1208    StructureNotifyMask         /* MapNotify                */
1209        | SubstructureNotifyMask,
1210    SubstructureRedirectMask,   /* MapRequest               */
1211    StructureNotifyMask         /* ReparentNotify           */
1212        | SubstructureNotifyMask,
1213    StructureNotifyMask         /* ConfigureNotify          */
1214        | SubstructureNotifyMask,
1215    SubstructureRedirectMask,   /* ConfigureRequest         */
1216    StructureNotifyMask         /* GravityNotify            */
1217        | SubstructureNotifyMask,
1218    ResizeRedirectMask,         /* ResizeRequest            */
1219    StructureNotifyMask         /* CirculateNotify          */
1220        | SubstructureNotifyMask,
1221    SubstructureRedirectMask,   /* CirculateRequest         */
1222    PropertyChangeMask,         /* PropertyNotify           */
1223    NonMaskableMask,            /* SelectionClear           */
1224    NonMaskableMask,            /* SelectionRequest         */
1225    NonMaskableMask,            /* SelectionNotify          */
1226    ColormapChangeMask,         /* ColormapNotify           */
1227    NonMaskableMask,            /* ClientMessage            */
1228    NonMaskableMask             /* MappingNotify            */
1229};
1230
1231EventMask
1232_XtConvertTypeToMask(int eventType)
1233{
1234    if ((Cardinal) eventType < XtNumber(masks))
1235        return masks[eventType];
1236    else
1237        return NoEventMask;
1238}
1239
1240Boolean
1241_XtOnGrabList(register Widget widget, XtGrabRec *grabList)
1242{
1243    register XtGrabRec *gl;
1244
1245    for (; widget != NULL; widget = (Widget) widget->core.parent) {
1246        for (gl = grabList; gl != NULL; gl = gl->next) {
1247            if (gl->widget == widget)
1248                return TRUE;
1249            if (gl->exclusive)
1250                break;
1251        }
1252    }
1253    return FALSE;
1254}
1255
1256static Widget
1257LookupSpringLoaded(XtGrabList grabList)
1258{
1259    XtGrabList gl;
1260
1261    for (gl = grabList; gl != NULL; gl = gl->next) {
1262        if (gl->spring_loaded) {
1263            if (XtIsSensitive(gl->widget))
1264                return gl->widget;
1265            else
1266                return NULL;
1267        }
1268        if (gl->exclusive)
1269            break;
1270    }
1271    return NULL;
1272}
1273
1274static Boolean
1275DispatchEvent(XEvent *event, Widget widget)
1276{
1277
1278    if (event->type == EnterNotify &&
1279        event->xcrossing.mode == NotifyNormal &&
1280        widget->core.widget_class->core_class.compress_enterleave) {
1281        if (XPending(event->xcrossing.display)) {
1282            XEvent nextEvent;
1283            XPeekEvent(event->xcrossing.display, &nextEvent);
1284
1285            if (nextEvent.type == LeaveNotify &&
1286                event->xcrossing.window == nextEvent.xcrossing.window &&
1287                nextEvent.xcrossing.mode == NotifyNormal &&
1288                ((event->xcrossing.detail != NotifyInferior &&
1289                  nextEvent.xcrossing.detail != NotifyInferior) ||
1290                 (event->xcrossing.detail == NotifyInferior &&
1291                  nextEvent.xcrossing.detail == NotifyInferior))) {
1292                /* skip the enter/leave pair */
1293                XNextEvent(event->xcrossing.display, &nextEvent);
1294
1295                return False;
1296            }
1297        }
1298    }
1299
1300    if (event->type == MotionNotify &&
1301        widget->core.widget_class->core_class.compress_motion) {
1302        while (XPending(event->xmotion.display)) {
1303            XEvent nextEvent;
1304            XPeekEvent(event->xmotion.display, &nextEvent);
1305
1306            if (nextEvent.type == MotionNotify &&
1307                event->xmotion.window == nextEvent.xmotion.window &&
1308                event->xmotion.subwindow == nextEvent.xmotion.subwindow) {
1309                /* replace the current event with the next one */
1310                XNextEvent(event->xmotion.display, event);
1311            }
1312            else
1313                break;
1314        }
1315    }
1316
1317    return XtDispatchEventToWidget(widget, event);
1318}
1319
1320typedef enum _GrabType { pass, ignore, remap } GrabType;
1321
1322#if !defined(AIXV3) || !defined(AIXSHLIB)
1323static                          /* AIX shared libraries are broken */
1324#endif
1325Boolean
1326_XtDefaultDispatcher(XEvent *event)
1327{
1328    register Widget widget;
1329    GrabType grabType;
1330    XtPerDisplayInput pdi;
1331    XtGrabList grabList;
1332    Boolean was_dispatched = False;
1333    DPY_TO_APPCON(event->xany.display);
1334
1335    /* the default dispatcher discards all extension events */
1336    if (event->type >= LASTEvent)
1337        return False;
1338
1339    LOCK_APP(app);
1340
1341    switch (event->type) {
1342    case KeyPress:
1343    case KeyRelease:
1344    case ButtonPress:
1345    case ButtonRelease:
1346        grabType = remap;
1347        break;
1348    case MotionNotify:
1349    case EnterNotify:
1350        grabType = ignore;
1351        break;
1352    default:
1353        grabType = pass;
1354        break;
1355    }
1356
1357    widget = XtWindowToWidget(event->xany.display, event->xany.window);
1358    pdi = _XtGetPerDisplayInput(event->xany.display);
1359
1360    grabList = *_XtGetGrabList(pdi);
1361
1362    if (widget == NULL) {
1363        if (grabType == remap
1364            && (widget = LookupSpringLoaded(grabList)) != NULL) {
1365            /* event occurred in a non-widget window, but we've promised also
1366               to dispatch it to the nearest accessible spring_loaded widget */
1367            was_dispatched = (XFilterEvent(event, XtWindow(widget))
1368                              || XtDispatchEventToWidget(widget, event));
1369        }
1370        else
1371            was_dispatched = (Boolean) XFilterEvent(event, None);
1372    }
1373    else if (grabType == pass) {
1374        if (event->type == LeaveNotify ||
1375            event->type == FocusIn || event->type == FocusOut) {
1376            if (XtIsSensitive(widget))
1377                was_dispatched = (XFilterEvent(event, XtWindow(widget)) ||
1378                                  XtDispatchEventToWidget(widget, event));
1379        }
1380        else
1381            was_dispatched = (XFilterEvent(event, XtWindow(widget)) ||
1382                              XtDispatchEventToWidget(widget, event));
1383    }
1384    else if (grabType == ignore) {
1385        if ((grabList == NULL || _XtOnGrabList(widget, grabList))
1386            && XtIsSensitive(widget)) {
1387            was_dispatched = (XFilterEvent(event, XtWindow(widget))
1388                              || DispatchEvent(event, widget));
1389        }
1390    }
1391    else if (grabType == remap) {
1392        EventMask mask = _XtConvertTypeToMask(event->type);
1393        Widget dspWidget;
1394        Boolean was_filtered = False;
1395
1396        dspWidget = _XtFindRemapWidget(event, widget, mask, pdi);
1397
1398        if ((grabList == NULL || _XtOnGrabList(dspWidget, grabList))
1399            && XtIsSensitive(dspWidget)) {
1400            if ((was_filtered =
1401                 (Boolean) XFilterEvent(event, XtWindow(dspWidget)))) {
1402                /* If this event activated a device grab, release it. */
1403                _XtUngrabBadGrabs(event, widget, mask, pdi);
1404                was_dispatched = True;
1405            }
1406            else
1407                was_dispatched = XtDispatchEventToWidget(dspWidget, event);
1408        }
1409        else
1410            _XtUngrabBadGrabs(event, widget, mask, pdi);
1411
1412        if (!was_filtered) {
1413            /* Also dispatch to nearest accessible spring_loaded. */
1414            /* Fetch this afterward to reflect modal list changes */
1415            grabList = *_XtGetGrabList(pdi);
1416            widget = LookupSpringLoaded(grabList);
1417            if (widget != NULL && widget != dspWidget) {
1418                was_dispatched = (XFilterEvent(event, XtWindow(widget))
1419                                  || XtDispatchEventToWidget(widget, event)
1420                                  || was_dispatched);
1421            }
1422        }
1423    }
1424    UNLOCK_APP(app);
1425    return was_dispatched;
1426}
1427
1428Boolean
1429XtDispatchEvent(XEvent *event)
1430{
1431    Boolean was_dispatched, safe;
1432    int dispatch_level;
1433    int starting_count;
1434    XtPerDisplay pd;
1435    Time time = 0;
1436    XtEventDispatchProc dispatch = _XtDefaultDispatcher;
1437    XtAppContext app = XtDisplayToApplicationContext(event->xany.display);
1438
1439    LOCK_APP(app);
1440    dispatch_level = ++app->dispatch_level;
1441    starting_count = app->destroy_count;
1442
1443    switch (event->type) {
1444    case KeyPress:
1445    case KeyRelease:
1446        time = event->xkey.time;
1447        break;
1448    case ButtonPress:
1449    case ButtonRelease:
1450        time = event->xbutton.time;
1451        break;
1452    case MotionNotify:
1453        time = event->xmotion.time;
1454        break;
1455    case EnterNotify:
1456    case LeaveNotify:
1457        time = event->xcrossing.time;
1458        break;
1459    case PropertyNotify:
1460        time = event->xproperty.time;
1461        break;
1462    case SelectionClear:
1463        time = event->xselectionclear.time;
1464        break;
1465
1466    case MappingNotify:
1467        _XtRefreshMapping(event, True);
1468        break;
1469    }
1470    pd = _XtGetPerDisplay(event->xany.display);
1471
1472    if (time)
1473        pd->last_timestamp = time;
1474    pd->last_event = *event;
1475
1476    if (pd->dispatcher_list) {
1477        dispatch = pd->dispatcher_list[event->type];
1478        if (dispatch == NULL)
1479            dispatch = _XtDefaultDispatcher;
1480    }
1481    was_dispatched = (*dispatch) (event);
1482
1483    /*
1484     * To make recursive XtDispatchEvent work, we need to do phase 2 destroys
1485     * only on those widgets destroyed by this particular dispatch.
1486     *
1487     */
1488
1489    if (app->destroy_count > starting_count)
1490        _XtDoPhase2Destroy(app, dispatch_level);
1491
1492    app->dispatch_level = dispatch_level - 1;
1493
1494    if ((safe = _XtSafeToDestroy(app))) {
1495        if (app->dpy_destroy_count != 0)
1496            _XtCloseDisplays(app);
1497        if (app->free_bindings)
1498            _XtDoFreeBindings(app);
1499    }
1500    UNLOCK_APP(app);
1501    LOCK_PROCESS;
1502    if (_XtAppDestroyCount != 0 && safe)
1503        _XtDestroyAppContexts();
1504    UNLOCK_PROCESS;
1505    return was_dispatched;
1506}
1507
1508static void
1509GrabDestroyCallback(Widget widget,
1510                    XtPointer closure _X_UNUSED,
1511                    XtPointer call_data _X_UNUSED)
1512{
1513    /* Remove widget from grab list if it destroyed */
1514    XtRemoveGrab(widget);
1515}
1516
1517static XtGrabRec *
1518NewGrabRec(Widget widget, Boolean exclusive, Boolean spring_loaded)
1519{
1520    register XtGrabList gl;
1521
1522    gl = XtNew(XtGrabRec);
1523    gl->next = NULL;
1524    gl->widget = widget;
1525    XtSetBit(gl->exclusive, exclusive);
1526    XtSetBit(gl->spring_loaded, spring_loaded);
1527
1528    return gl;
1529}
1530
1531void
1532XtAddGrab(Widget widget, _XtBoolean exclusive, _XtBoolean spring_loaded)
1533{
1534    register XtGrabList gl;
1535    XtGrabList *grabListPtr;
1536    XtAppContext app = XtWidgetToApplicationContext(widget);
1537
1538    LOCK_APP(app);
1539    LOCK_PROCESS;
1540    grabListPtr = _XtGetGrabList(_XtGetPerDisplayInput(XtDisplay(widget)));
1541
1542    if (spring_loaded && !exclusive) {
1543        XtAppWarningMsg(app,
1544                        "grabError", "xtAddGrab", XtCXtToolkitError,
1545                        "XtAddGrab requires exclusive grab if spring_loaded is TRUE",
1546                        NULL, NULL);
1547        exclusive = TRUE;
1548    }
1549
1550    gl = NewGrabRec(widget, (Boolean) exclusive, (Boolean) spring_loaded);
1551    gl->next = *grabListPtr;
1552    *grabListPtr = gl;
1553
1554    XtAddCallback(widget, XtNdestroyCallback,
1555                  GrabDestroyCallback, (XtPointer) NULL);
1556    UNLOCK_PROCESS;
1557    UNLOCK_APP(app);
1558}
1559
1560void
1561XtRemoveGrab(Widget widget)
1562{
1563    register XtGrabList gl;
1564    register Boolean done;
1565    XtGrabList *grabListPtr;
1566    XtAppContext app = XtWidgetToApplicationContext(widget);
1567
1568    LOCK_APP(app);
1569    LOCK_PROCESS;
1570
1571    grabListPtr = _XtGetGrabList(_XtGetPerDisplayInput(XtDisplay(widget)));
1572
1573    for (gl = *grabListPtr; gl != NULL; gl = gl->next) {
1574        if (gl->widget == widget)
1575            break;
1576    }
1577
1578    if (gl == NULL) {
1579        XtAppWarningMsg(app,
1580                        "grabError", "xtRemoveGrab", XtCXtToolkitError,
1581                        "XtRemoveGrab asked to remove a widget not on the list",
1582                        NULL, NULL);
1583        UNLOCK_PROCESS;
1584        UNLOCK_APP(app);
1585        return;
1586    }
1587
1588    do {
1589        gl = *grabListPtr;
1590        done = (gl->widget == widget);
1591        *grabListPtr = gl->next;
1592        XtRemoveCallback(gl->widget, XtNdestroyCallback,
1593                         GrabDestroyCallback, (XtPointer) NULL);
1594        XtFree((char *) gl);
1595    } while (!done);
1596    UNLOCK_PROCESS;
1597    UNLOCK_APP(app);
1598    return;
1599}
1600
1601void
1602XtMainLoop(void)
1603{
1604    XtAppMainLoop(_XtDefaultAppContext());
1605}
1606
1607void
1608XtAppMainLoop(XtAppContext app)
1609{
1610    XtInputMask m = XtIMAll;
1611    XtInputMask t;
1612
1613    LOCK_APP(app);
1614    do {
1615        if (m == 0) {
1616            m = XtIMAll;
1617            /* wait for any event, blocking */
1618            XtAppProcessEvent(app, m);
1619        }
1620        else if (((t = XtAppPending(app)) & m)) {
1621            /* wait for certain events, stepping through choices */
1622            XtAppProcessEvent(app, t & m);
1623        }
1624        m >>= 1;
1625    } while (app->exit_flag == FALSE);
1626    UNLOCK_APP(app);
1627}
1628
1629void
1630_XtFreeEventTable(XtEventTable *event_table)
1631{
1632    register XtEventTable event;
1633
1634    event = *event_table;
1635    while (event != NULL) {
1636        register XtEventTable next = event->next;
1637
1638        XtFree((char *) event);
1639        event = next;
1640    }
1641}
1642
1643Time
1644XtLastTimestampProcessed(Display *dpy)
1645{
1646    Time time;
1647
1648    DPY_TO_APPCON(dpy);
1649
1650    LOCK_APP(app);
1651    LOCK_PROCESS;
1652    time = _XtGetPerDisplay(dpy)->last_timestamp;
1653    UNLOCK_PROCESS;
1654    UNLOCK_APP(app);
1655    return (time);
1656}
1657
1658XEvent *
1659XtLastEventProcessed(Display *dpy)
1660{
1661    XEvent *le = NULL;
1662
1663    DPY_TO_APPCON(dpy);
1664
1665    LOCK_APP(app);
1666    le = &_XtGetPerDisplay(dpy)->last_event;
1667    if (!le->xany.serial)
1668        le = NULL;
1669    UNLOCK_APP(app);
1670    return le;
1671}
1672
1673void
1674_XtSendFocusEvent(Widget child, int type)
1675{
1676    child = XtIsWidget(child) ? child : _XtWindowedAncestor(child);
1677    if (XtIsSensitive(child) && !child->core.being_destroyed
1678        && XtIsRealized(child)
1679        && (XtBuildEventMask(child) & FocusChangeMask)) {
1680        XFocusChangeEvent event;
1681        Display *dpy = XtDisplay(child);
1682
1683        event.type = type;
1684        event.serial = LastKnownRequestProcessed(dpy);
1685        event.send_event = True;
1686        event.display = dpy;
1687
1688        event.window = XtWindow(child);
1689        event.mode = NotifyNormal;
1690        event.detail = NotifyAncestor;
1691        if (XFilterEvent((XEvent *) &event, XtWindow(child)))
1692            return;
1693        XtDispatchEventToWidget(child, (XEvent *) &event);
1694    }
1695}
1696
1697static XtEventDispatchProc *
1698NewDispatcherList(void)
1699{
1700    XtEventDispatchProc *l = (XtEventDispatchProc *)
1701        __XtCalloc((Cardinal) 128,
1702                   (Cardinal)
1703                   sizeof(XtEventDispatchProc));
1704
1705    return l;
1706}
1707
1708XtEventDispatchProc
1709XtSetEventDispatcher(Display *dpy,
1710                     int event_type,
1711                     XtEventDispatchProc proc)
1712{
1713    XtEventDispatchProc *list;
1714    XtEventDispatchProc old_proc;
1715    register XtPerDisplay pd;
1716
1717    DPY_TO_APPCON(dpy);
1718
1719    LOCK_APP(app);
1720    LOCK_PROCESS;
1721    pd = _XtGetPerDisplay(dpy);
1722
1723    list = pd->dispatcher_list;
1724    if (!list) {
1725        if (proc)
1726            list = pd->dispatcher_list = NewDispatcherList();
1727        else
1728            return _XtDefaultDispatcher;
1729    }
1730    old_proc = list[event_type];
1731    list[event_type] = proc;
1732    if (old_proc == NULL)
1733        old_proc = _XtDefaultDispatcher;
1734    UNLOCK_PROCESS;
1735    UNLOCK_APP(app);
1736    return old_proc;
1737}
1738
1739void
1740XtRegisterExtensionSelector(Display *dpy,
1741                            int min_event_type,
1742                            int max_event_type,
1743                            XtExtensionSelectProc proc,
1744                            XtPointer client_data)
1745{
1746    XtPerDisplay pd;
1747    int i;
1748
1749    DPY_TO_APPCON(dpy);
1750
1751    if (dpy == NULL)
1752        XtErrorMsg("nullDisplay",
1753                   "xtRegisterExtensionSelector", XtCXtToolkitError,
1754                   "XtRegisterExtensionSelector requires a non-NULL display",
1755                   NULL, NULL);
1756
1757    LOCK_APP(app);
1758    LOCK_PROCESS;
1759    pd = _XtGetPerDisplay(dpy);
1760
1761    for (i = 0; i < pd->ext_select_count; i++) {
1762        ExtSelectRec *e = &pd->ext_select_list[i];
1763
1764        if (e->min == min_event_type && e->max == max_event_type) {
1765            e->proc = proc;
1766            e->client_data = client_data;
1767            return;
1768        }
1769        if ((min_event_type >= e->min && min_event_type <= e->max) ||
1770            (max_event_type >= e->min && max_event_type <= e->max)) {
1771            XtErrorMsg("rangeError", "xtRegisterExtensionSelector",
1772                       XtCXtToolkitError,
1773                       "Attempt to register multiple selectors for one extension event type",
1774                       NULL, NULL);
1775            UNLOCK_PROCESS;
1776            UNLOCK_APP(app);
1777            return;
1778        }
1779    }
1780    pd->ext_select_count++;
1781    pd->ext_select_list = XtReallocArray(pd->ext_select_list,
1782                                         (Cardinal) pd->ext_select_count,
1783                                         (Cardinal) sizeof(ExtSelectRec));
1784    for (i = pd->ext_select_count - 1; i > 0; i--) {
1785        if (pd->ext_select_list[i - 1].min > min_event_type) {
1786            pd->ext_select_list[i] = pd->ext_select_list[i - 1];
1787        }
1788        else
1789            break;
1790    }
1791    pd->ext_select_list[i].min = min_event_type;
1792    pd->ext_select_list[i].max = max_event_type;
1793    pd->ext_select_list[i].proc = proc;
1794    pd->ext_select_list[i].client_data = client_data;
1795    UNLOCK_PROCESS;
1796    UNLOCK_APP(app);
1797}
1798
1799void
1800_XtExtensionSelect(Widget widget)
1801{
1802    int i;
1803    XtPerDisplay pd;
1804
1805    WIDGET_TO_APPCON(widget);
1806
1807    LOCK_APP(app);
1808    LOCK_PROCESS;
1809
1810    pd = _XtGetPerDisplay(XtDisplay(widget));
1811
1812    for (i = 0; i < pd->ext_select_count; i++) {
1813        CallExtensionSelector(widget, pd->ext_select_list + i, FALSE);
1814    }
1815    UNLOCK_PROCESS;
1816    UNLOCK_APP(app);
1817}
1818