Event.c revision 35525df4
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
1322static Boolean
1323_XtDefaultDispatcher(XEvent *event)
1324{
1325    register Widget widget;
1326    GrabType grabType;
1327    XtPerDisplayInput pdi;
1328    XtGrabList grabList;
1329    Boolean was_dispatched = False;
1330    DPY_TO_APPCON(event->xany.display);
1331
1332    /* the default dispatcher discards all extension events */
1333    if (event->type >= LASTEvent)
1334        return False;
1335
1336    LOCK_APP(app);
1337
1338    switch (event->type) {
1339    case KeyPress:
1340    case KeyRelease:
1341    case ButtonPress:
1342    case ButtonRelease:
1343        grabType = remap;
1344        break;
1345    case MotionNotify:
1346    case EnterNotify:
1347        grabType = ignore;
1348        break;
1349    default:
1350        grabType = pass;
1351        break;
1352    }
1353
1354    widget = XtWindowToWidget(event->xany.display, event->xany.window);
1355    pdi = _XtGetPerDisplayInput(event->xany.display);
1356
1357    grabList = *_XtGetGrabList(pdi);
1358
1359    if (widget == NULL) {
1360        if (grabType == remap
1361            && (widget = LookupSpringLoaded(grabList)) != NULL) {
1362            /* event occurred in a non-widget window, but we've promised also
1363               to dispatch it to the nearest accessible spring_loaded widget */
1364            was_dispatched = (XFilterEvent(event, XtWindow(widget))
1365                              || XtDispatchEventToWidget(widget, event));
1366        }
1367        else
1368            was_dispatched = (Boolean) XFilterEvent(event, None);
1369    }
1370    else if (grabType == pass) {
1371        if (event->type == LeaveNotify ||
1372            event->type == FocusIn || event->type == FocusOut) {
1373            if (XtIsSensitive(widget))
1374                was_dispatched = (XFilterEvent(event, XtWindow(widget)) ||
1375                                  XtDispatchEventToWidget(widget, event));
1376        }
1377        else
1378            was_dispatched = (XFilterEvent(event, XtWindow(widget)) ||
1379                              XtDispatchEventToWidget(widget, event));
1380    }
1381    else if (grabType == ignore) {
1382        if ((grabList == NULL || _XtOnGrabList(widget, grabList))
1383            && XtIsSensitive(widget)) {
1384            was_dispatched = (XFilterEvent(event, XtWindow(widget))
1385                              || DispatchEvent(event, widget));
1386        }
1387    }
1388    else if (grabType == remap) {
1389        EventMask mask = _XtConvertTypeToMask(event->type);
1390        Widget dspWidget;
1391        Boolean was_filtered = False;
1392
1393        dspWidget = _XtFindRemapWidget(event, widget, mask, pdi);
1394
1395        if ((grabList == NULL || _XtOnGrabList(dspWidget, grabList))
1396            && XtIsSensitive(dspWidget)) {
1397            if ((was_filtered =
1398                 (Boolean) XFilterEvent(event, XtWindow(dspWidget)))) {
1399                /* If this event activated a device grab, release it. */
1400                _XtUngrabBadGrabs(event, widget, mask, pdi);
1401                was_dispatched = True;
1402            }
1403            else
1404                was_dispatched = XtDispatchEventToWidget(dspWidget, event);
1405        }
1406        else
1407            _XtUngrabBadGrabs(event, widget, mask, pdi);
1408
1409        if (!was_filtered) {
1410            /* Also dispatch to nearest accessible spring_loaded. */
1411            /* Fetch this afterward to reflect modal list changes */
1412            grabList = *_XtGetGrabList(pdi);
1413            widget = LookupSpringLoaded(grabList);
1414            if (widget != NULL && widget != dspWidget) {
1415                was_dispatched = (XFilterEvent(event, XtWindow(widget))
1416                                  || XtDispatchEventToWidget(widget, event)
1417                                  || was_dispatched);
1418            }
1419        }
1420    }
1421    UNLOCK_APP(app);
1422    return was_dispatched;
1423}
1424
1425Boolean
1426XtDispatchEvent(XEvent *event)
1427{
1428    Boolean was_dispatched, safe;
1429    int dispatch_level;
1430    int starting_count;
1431    XtPerDisplay pd;
1432    Time time = 0;
1433    XtEventDispatchProc dispatch = _XtDefaultDispatcher;
1434    XtAppContext app = XtDisplayToApplicationContext(event->xany.display);
1435
1436    LOCK_APP(app);
1437    dispatch_level = ++app->dispatch_level;
1438    starting_count = app->destroy_count;
1439
1440    switch (event->type) {
1441    case KeyPress:
1442    case KeyRelease:
1443        time = event->xkey.time;
1444        break;
1445    case ButtonPress:
1446    case ButtonRelease:
1447        time = event->xbutton.time;
1448        break;
1449    case MotionNotify:
1450        time = event->xmotion.time;
1451        break;
1452    case EnterNotify:
1453    case LeaveNotify:
1454        time = event->xcrossing.time;
1455        break;
1456    case PropertyNotify:
1457        time = event->xproperty.time;
1458        break;
1459    case SelectionClear:
1460        time = event->xselectionclear.time;
1461        break;
1462
1463    case MappingNotify:
1464        _XtRefreshMapping(event, True);
1465        break;
1466    }
1467    pd = _XtGetPerDisplay(event->xany.display);
1468
1469    if (time)
1470        pd->last_timestamp = time;
1471    pd->last_event = *event;
1472
1473    if (pd->dispatcher_list) {
1474        dispatch = pd->dispatcher_list[event->type];
1475        if (dispatch == NULL)
1476            dispatch = _XtDefaultDispatcher;
1477    }
1478    was_dispatched = (*dispatch) (event);
1479
1480    /*
1481     * To make recursive XtDispatchEvent work, we need to do phase 2 destroys
1482     * only on those widgets destroyed by this particular dispatch.
1483     *
1484     */
1485
1486    if (app->destroy_count > starting_count)
1487        _XtDoPhase2Destroy(app, dispatch_level);
1488
1489    app->dispatch_level = dispatch_level - 1;
1490
1491    if ((safe = _XtSafeToDestroy(app))) {
1492        if (app->dpy_destroy_count != 0)
1493            _XtCloseDisplays(app);
1494        if (app->free_bindings)
1495            _XtDoFreeBindings(app);
1496    }
1497    UNLOCK_APP(app);
1498    LOCK_PROCESS;
1499    if (_XtAppDestroyCount != 0 && safe)
1500        _XtDestroyAppContexts();
1501    UNLOCK_PROCESS;
1502    return was_dispatched;
1503}
1504
1505static void
1506GrabDestroyCallback(Widget widget,
1507                    XtPointer closure _X_UNUSED,
1508                    XtPointer call_data _X_UNUSED)
1509{
1510    /* Remove widget from grab list if it destroyed */
1511    XtRemoveGrab(widget);
1512}
1513
1514static XtGrabRec *
1515NewGrabRec(Widget widget, Boolean exclusive, Boolean spring_loaded)
1516{
1517    register XtGrabList gl;
1518
1519    gl = XtNew(XtGrabRec);
1520    gl->next = NULL;
1521    gl->widget = widget;
1522    XtSetBit(gl->exclusive, exclusive);
1523    XtSetBit(gl->spring_loaded, spring_loaded);
1524
1525    return gl;
1526}
1527
1528void
1529XtAddGrab(Widget widget, _XtBoolean exclusive, _XtBoolean spring_loaded)
1530{
1531    register XtGrabList gl;
1532    XtGrabList *grabListPtr;
1533    XtAppContext app = XtWidgetToApplicationContext(widget);
1534
1535    LOCK_APP(app);
1536    LOCK_PROCESS;
1537    grabListPtr = _XtGetGrabList(_XtGetPerDisplayInput(XtDisplay(widget)));
1538
1539    if (spring_loaded && !exclusive) {
1540        XtAppWarningMsg(app,
1541                        "grabError", "xtAddGrab", XtCXtToolkitError,
1542                        "XtAddGrab requires exclusive grab if spring_loaded is TRUE",
1543                        NULL, NULL);
1544        exclusive = TRUE;
1545    }
1546
1547    gl = NewGrabRec(widget, (Boolean) exclusive, (Boolean) spring_loaded);
1548    gl->next = *grabListPtr;
1549    *grabListPtr = gl;
1550
1551    XtAddCallback(widget, XtNdestroyCallback,
1552                  GrabDestroyCallback, (XtPointer) NULL);
1553    UNLOCK_PROCESS;
1554    UNLOCK_APP(app);
1555}
1556
1557void
1558XtRemoveGrab(Widget widget)
1559{
1560    register XtGrabList gl;
1561    register Boolean done;
1562    XtGrabList *grabListPtr;
1563    XtAppContext app = XtWidgetToApplicationContext(widget);
1564
1565    LOCK_APP(app);
1566    LOCK_PROCESS;
1567
1568    grabListPtr = _XtGetGrabList(_XtGetPerDisplayInput(XtDisplay(widget)));
1569
1570    for (gl = *grabListPtr; gl != NULL; gl = gl->next) {
1571        if (gl->widget == widget)
1572            break;
1573    }
1574
1575    if (gl == NULL) {
1576        XtAppWarningMsg(app,
1577                        "grabError", "xtRemoveGrab", XtCXtToolkitError,
1578                        "XtRemoveGrab asked to remove a widget not on the list",
1579                        NULL, NULL);
1580        UNLOCK_PROCESS;
1581        UNLOCK_APP(app);
1582        return;
1583    }
1584
1585    do {
1586        gl = *grabListPtr;
1587        done = (gl->widget == widget);
1588        *grabListPtr = gl->next;
1589        XtRemoveCallback(gl->widget, XtNdestroyCallback,
1590                         GrabDestroyCallback, (XtPointer) NULL);
1591        XtFree((char *) gl);
1592    } while (!done);
1593    UNLOCK_PROCESS;
1594    UNLOCK_APP(app);
1595    return;
1596}
1597
1598void
1599XtMainLoop(void)
1600{
1601    XtAppMainLoop(_XtDefaultAppContext());
1602}
1603
1604void
1605XtAppMainLoop(XtAppContext app)
1606{
1607    XtInputMask m = XtIMAll;
1608    XtInputMask t;
1609
1610    LOCK_APP(app);
1611    do {
1612        if (m == 0) {
1613            m = XtIMAll;
1614            /* wait for any event, blocking */
1615            XtAppProcessEvent(app, m);
1616        }
1617        else if (((t = XtAppPending(app)) & m)) {
1618            /* wait for certain events, stepping through choices */
1619            XtAppProcessEvent(app, t & m);
1620        }
1621        m >>= 1;
1622    } while (app->exit_flag == FALSE);
1623    UNLOCK_APP(app);
1624}
1625
1626void
1627_XtFreeEventTable(XtEventTable *event_table)
1628{
1629    register XtEventTable event;
1630
1631    event = *event_table;
1632    while (event != NULL) {
1633        register XtEventTable next = event->next;
1634
1635        XtFree((char *) event);
1636        event = next;
1637    }
1638}
1639
1640Time
1641XtLastTimestampProcessed(Display *dpy)
1642{
1643    Time time;
1644
1645    DPY_TO_APPCON(dpy);
1646
1647    LOCK_APP(app);
1648    LOCK_PROCESS;
1649    time = _XtGetPerDisplay(dpy)->last_timestamp;
1650    UNLOCK_PROCESS;
1651    UNLOCK_APP(app);
1652    return (time);
1653}
1654
1655XEvent *
1656XtLastEventProcessed(Display *dpy)
1657{
1658    XEvent *le = NULL;
1659
1660    DPY_TO_APPCON(dpy);
1661
1662    LOCK_APP(app);
1663    le = &_XtGetPerDisplay(dpy)->last_event;
1664    if (!le->xany.serial)
1665        le = NULL;
1666    UNLOCK_APP(app);
1667    return le;
1668}
1669
1670void
1671_XtSendFocusEvent(Widget child, int type)
1672{
1673    child = XtIsWidget(child) ? child : _XtWindowedAncestor(child);
1674    if (XtIsSensitive(child) && !child->core.being_destroyed
1675        && XtIsRealized(child)
1676        && (XtBuildEventMask(child) & FocusChangeMask)) {
1677        XFocusChangeEvent event;
1678        Display *dpy = XtDisplay(child);
1679
1680        event.type = type;
1681        event.serial = LastKnownRequestProcessed(dpy);
1682        event.send_event = True;
1683        event.display = dpy;
1684
1685        event.window = XtWindow(child);
1686        event.mode = NotifyNormal;
1687        event.detail = NotifyAncestor;
1688        if (XFilterEvent((XEvent *) &event, XtWindow(child)))
1689            return;
1690        XtDispatchEventToWidget(child, (XEvent *) &event);
1691    }
1692}
1693
1694static XtEventDispatchProc *
1695NewDispatcherList(void)
1696{
1697    XtEventDispatchProc *l = (XtEventDispatchProc *)
1698        __XtCalloc((Cardinal) 128,
1699                   (Cardinal)
1700                   sizeof(XtEventDispatchProc));
1701
1702    return l;
1703}
1704
1705XtEventDispatchProc
1706XtSetEventDispatcher(Display *dpy,
1707                     int event_type,
1708                     XtEventDispatchProc proc)
1709{
1710    XtEventDispatchProc *list;
1711    XtEventDispatchProc old_proc;
1712    register XtPerDisplay pd;
1713
1714    DPY_TO_APPCON(dpy);
1715
1716    LOCK_APP(app);
1717    LOCK_PROCESS;
1718    pd = _XtGetPerDisplay(dpy);
1719
1720    list = pd->dispatcher_list;
1721    if (!list) {
1722        if (proc)
1723            list = pd->dispatcher_list = NewDispatcherList();
1724        else
1725            return _XtDefaultDispatcher;
1726    }
1727    old_proc = list[event_type];
1728    list[event_type] = proc;
1729    if (old_proc == NULL)
1730        old_proc = _XtDefaultDispatcher;
1731    UNLOCK_PROCESS;
1732    UNLOCK_APP(app);
1733    return old_proc;
1734}
1735
1736void
1737XtRegisterExtensionSelector(Display *dpy,
1738                            int min_event_type,
1739                            int max_event_type,
1740                            XtExtensionSelectProc proc,
1741                            XtPointer client_data)
1742{
1743    XtPerDisplay pd;
1744    int i;
1745
1746    DPY_TO_APPCON(dpy);
1747
1748    if (dpy == NULL)
1749        XtErrorMsg("nullDisplay",
1750                   "xtRegisterExtensionSelector", XtCXtToolkitError,
1751                   "XtRegisterExtensionSelector requires a non-NULL display",
1752                   NULL, NULL);
1753
1754    LOCK_APP(app);
1755    LOCK_PROCESS;
1756    pd = _XtGetPerDisplay(dpy);
1757
1758    for (i = 0; i < pd->ext_select_count; i++) {
1759        ExtSelectRec *e = &pd->ext_select_list[i];
1760
1761        if (e->min == min_event_type && e->max == max_event_type) {
1762            e->proc = proc;
1763            e->client_data = client_data;
1764            return;
1765        }
1766        if ((min_event_type >= e->min && min_event_type <= e->max) ||
1767            (max_event_type >= e->min && max_event_type <= e->max)) {
1768            XtErrorMsg("rangeError", "xtRegisterExtensionSelector",
1769                       XtCXtToolkitError,
1770                       "Attempt to register multiple selectors for one extension event type",
1771                       NULL, NULL);
1772            UNLOCK_PROCESS;
1773            UNLOCK_APP(app);
1774            return;
1775        }
1776    }
1777    pd->ext_select_count++;
1778    pd->ext_select_list = XtReallocArray(pd->ext_select_list,
1779                                         (Cardinal) pd->ext_select_count,
1780                                         (Cardinal) sizeof(ExtSelectRec));
1781    for (i = pd->ext_select_count - 1; i > 0; i--) {
1782        if (pd->ext_select_list[i - 1].min > min_event_type) {
1783            pd->ext_select_list[i] = pd->ext_select_list[i - 1];
1784        }
1785        else
1786            break;
1787    }
1788    pd->ext_select_list[i].min = min_event_type;
1789    pd->ext_select_list[i].max = max_event_type;
1790    pd->ext_select_list[i].proc = proc;
1791    pd->ext_select_list[i].client_data = client_data;
1792    UNLOCK_PROCESS;
1793    UNLOCK_APP(app);
1794}
1795
1796void
1797_XtExtensionSelect(Widget widget)
1798{
1799    int i;
1800    XtPerDisplay pd;
1801
1802    WIDGET_TO_APPCON(widget);
1803
1804    LOCK_APP(app);
1805    LOCK_PROCESS;
1806
1807    pd = _XtGetPerDisplay(XtDisplay(widget));
1808
1809    for (i = 0; i < pd->ext_select_count; i++) {
1810        CallExtensionSelector(widget, pd->ext_select_list + i, FALSE);
1811    }
1812    UNLOCK_PROCESS;
1813    UNLOCK_APP(app);
1814}
1815