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