1/*
2
3Copyright (c) 1993, Oracle and/or its affiliates.
4
5Permission is hereby granted, free of charge, to any person obtaining a
6copy of this software and associated documentation files (the "Software"),
7to deal in the Software without restriction, including without limitation
8the rights to use, copy, modify, merge, publish, distribute, sublicense,
9and/or sell copies of the Software, and to permit persons to whom the
10Software is furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice (including the next
13paragraph) shall be included in all copies or substantial portions of the
14Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22DEALINGS IN THE SOFTWARE.
23
24*/
25/********************************************************
26
27Copyright 1988 by Hewlett-Packard Company
28Copyright 1987, 1988, 1989,1990 by Digital Equipment Corporation, Maynard, Massachusetts
29
30Permission to use, copy, modify, and distribute this software
31and its documentation for any purpose and without fee is hereby
32granted, provided that the above copyright notice appear in all
33copies and that both that copyright notice and this permission
34notice appear in supporting documentation, and that the names of
35Hewlett-Packard or Digital not be used in advertising or
36publicity pertaining to distribution of the software without specific,
37written prior permission.
38
39DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
45SOFTWARE.
46
47********************************************************/
48
49/*
50
51Copyright 1987, 1988, 1989, 1990, 1994, 1998  The Open Group
52
53Permission to use, copy, modify, distribute, and sell this software and its
54documentation for any purpose is hereby granted without fee, provided that
55the above copyright notice appear in all copies and that both that
56copyright notice and this permission notice appear in supporting
57documentation.
58
59The above copyright notice and this permission notice shall be included in
60all copies or substantial portions of the Software.
61
62THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
63IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
64FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
65OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
66AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
67CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
68
69Except as contained in this notice, the name of The Open Group shall not be
70used in advertising or otherwise to promote the sale, use or other dealings
71in this Software without prior written authorization from The Open Group.
72
73*/
74
75#ifdef HAVE_CONFIG_H
76#include <config.h>
77#endif
78#include "IntrinsicI.h"
79#include "StringDefs.h"
80#include "PassivGraI.h"
81
82/* typedef unsigned long Mask; */
83#define BITMASK(i) (((Mask)1) << ((i) & 31))
84#define MASKIDX(i) ((i) >> 5)
85#define MASKWORD(buf, i) buf[MASKIDX(i)]
86#define BITSET(buf, i) MASKWORD(buf, i) |= BITMASK(i)
87#define BITCLEAR(buf, i) MASKWORD(buf, i) &= ~BITMASK(i)
88#define GETBIT(buf, i) (MASKWORD(buf, i) & BITMASK(i))
89#define MasksPerDetailMask 8
90
91#define pDisplay(grabPtr) (((grabPtr)->widget)->core.screen->display)
92#define pWindow(grabPtr) (((grabPtr)->widget)->core.window)
93
94/***************************************************************************/
95/*********************** Internal Support Routines *************************/
96/***************************************************************************/
97
98/*
99 * Turn off (clear) the bit in the specified detail mask which is associated
100 * with the detail.
101 */
102
103static void
104DeleteDetailFromMask(Mask **ppDetailMask, unsigned short detail)
105{
106    Mask *pDetailMask = *ppDetailMask;
107
108    if (!pDetailMask) {
109        int i;
110
111        pDetailMask = XtMallocArray(MasksPerDetailMask, (Cardinal) sizeof(Mask));
112        for (i = MasksPerDetailMask; --i >= 0;)
113            pDetailMask[i] = (unsigned long) (~0);
114        *ppDetailMask = pDetailMask;
115    }
116    BITCLEAR((pDetailMask), detail);
117}
118
119/*
120 * Make an exact copy of the specified detail mask.
121 */
122
123static Mask *
124CopyDetailMask(const Mask *pOriginalDetailMask)
125{
126    Mask *pTempMask;
127    int i;
128
129    if (!pOriginalDetailMask)
130        return NULL;
131
132    pTempMask = XtMallocArray(MasksPerDetailMask, (Cardinal) sizeof(Mask));
133
134    for (i = 0; i < MasksPerDetailMask; i++)
135        pTempMask[i] = pOriginalDetailMask[i];
136
137    return pTempMask;
138}
139
140/*
141 * Allocate a new grab entry, and fill in all of the fields using the
142 * specified parameters.
143 */
144
145static XtServerGrabPtr
146CreateGrab(Widget widget,
147           Boolean ownerEvents,
148           Modifiers modifiers,
149           KeyCode keybut,
150           int pointer_mode,
151           int keyboard_mode,
152           Mask event_mask,
153           Window confine_to,
154           Cursor cursor,
155           Boolean need_ext)
156{
157    XtServerGrabPtr grab;
158
159    if (confine_to || cursor)
160        need_ext = True;
161    grab = (XtServerGrabPtr) __XtMalloc(sizeof(XtServerGrabRec) +
162                                        (need_ext ? sizeof(XtServerGrabExtRec)
163                                         : 0));
164    grab->next = NULL;
165    grab->widget = widget;
166    XtSetBit(grab->ownerEvents, ownerEvents);
167    XtSetBit(grab->pointerMode, pointer_mode);
168    XtSetBit(grab->keyboardMode, keyboard_mode);
169    grab->eventMask = (unsigned short) event_mask;
170    XtSetBit(grab->hasExt, need_ext);
171    grab->confineToIsWidgetWin = (XtWindow(widget) == confine_to);
172    grab->modifiers = (unsigned short) modifiers;
173    grab->keybut = keybut;
174    if (need_ext) {
175        XtServerGrabExtPtr ext = GRABEXT(grab);
176
177        ext->pModifiersMask = NULL;
178        ext->pKeyButMask = NULL;
179        ext->confineTo = confine_to;
180        ext->cursor = cursor;
181    }
182    return grab;
183}
184
185/*
186 * Free up the space occupied by a grab entry.
187 */
188
189static void
190FreeGrab(XtServerGrabPtr pGrab)
191{
192    if (pGrab->hasExt) {
193	XtServerGrabExtPtr ext = GRABEXT(pGrab);
194	XtFree((char *)ext->pModifiersMask);
195	XtFree((char *)ext->pKeyButMask);
196    }
197    XtFree((char *) pGrab);
198}
199
200typedef struct _DetailRec {
201    unsigned short exact;
202    Mask *pMask;
203} DetailRec, *DetailPtr;
204
205/*
206 * If the first detail is set to 'exception' and the second detail
207 * is contained in the mask of the first, then TRUE is returned.
208 */
209
210static Bool
211IsInGrabMask(register DetailPtr firstDetail,
212             register DetailPtr secondDetail,
213             unsigned short exception)
214{
215    if (firstDetail->exact == exception) {
216        if (!firstDetail->pMask)
217            return TRUE;
218
219        /* (at present) never called with two non-null pMasks */
220        if (secondDetail->exact == exception)
221            return FALSE;
222
223        if (GETBIT(firstDetail->pMask, secondDetail->exact))
224            return TRUE;
225    }
226
227    return FALSE;
228}
229
230/*
231 * If neither of the details is set to 'exception', and they match
232 * exactly, then TRUE is returned.
233 */
234
235static Bool
236IdenticalExactDetails(unsigned short firstExact,
237                      unsigned short secondExact,
238                      unsigned short exception)
239{
240    if ((firstExact == exception) || (secondExact == exception))
241        return FALSE;
242
243    if (firstExact == secondExact)
244        return TRUE;
245
246    return FALSE;
247}
248
249/*
250 * If the first detail is set to 'exception', and its mask has the bit
251 * enabled which corresponds to the second detail, OR if neither of the
252 * details is set to 'exception' and the details match exactly, then
253 * TRUE is returned.
254 */
255
256static Bool
257DetailSupersedesSecond(register DetailPtr firstDetail,
258                       register DetailPtr secondDetail,
259                       unsigned short exception)
260{
261    if (IsInGrabMask(firstDetail, secondDetail, exception))
262        return TRUE;
263
264    if (IdenticalExactDetails(firstDetail->exact, secondDetail->exact,
265                              exception))
266        return TRUE;
267
268    return FALSE;
269}
270
271/*
272 * If the two grab events match exactly, or if the first grab entry
273 * 'encompasses' the second grab entry, then TRUE is returned.
274 */
275
276static Bool
277GrabSupersedesSecond(register XtServerGrabPtr pFirstGrab,
278                     register XtServerGrabPtr pSecondGrab)
279{
280    DetailRec first, second;
281
282    first.exact = pFirstGrab->modifiers;
283    if (pFirstGrab->hasExt)
284        first.pMask = GRABEXT(pFirstGrab)->pModifiersMask;
285    else
286        first.pMask = NULL;
287    second.exact = pSecondGrab->modifiers;
288    if (pSecondGrab->hasExt)
289        second.pMask = GRABEXT(pSecondGrab)->pModifiersMask;
290    else
291        second.pMask = NULL;
292    if (!DetailSupersedesSecond(&first, &second, (unsigned short) AnyModifier))
293        return FALSE;
294
295    first.exact = pFirstGrab->keybut;
296    if (pFirstGrab->hasExt)
297        first.pMask = GRABEXT(pFirstGrab)->pKeyButMask;
298    else
299        first.pMask = NULL;
300    second.exact = pSecondGrab->keybut;
301    if (pSecondGrab->hasExt)
302        second.pMask = GRABEXT(pSecondGrab)->pKeyButMask;
303    else
304        second.pMask = NULL;
305    if (DetailSupersedesSecond(&first, &second, (unsigned short) AnyKey))
306        return TRUE;
307
308    return FALSE;
309}
310
311/*
312 * Two grabs are considered to be matching if either of the following are true:
313 *
314 * 1) The two grab entries match exactly, or the first grab entry
315 *    encompasses the second grab entry.
316 * 2) The second grab entry encompasses the first grab entry.
317 * 3) The keycodes match exactly, and one entry's modifiers encompasses
318 *    the others.
319 * 4) The keycode for one entry encompasses the other, and the detail
320 *    for the other entry encompasses the first.
321 */
322
323static Bool
324GrabMatchesSecond(register XtServerGrabPtr pFirstGrab,
325                  register XtServerGrabPtr pSecondGrab)
326{
327    DetailRec firstD, firstM, secondD, secondM;
328
329    if (pDisplay(pFirstGrab) != pDisplay(pSecondGrab))
330        return FALSE;
331
332    if (GrabSupersedesSecond(pFirstGrab, pSecondGrab))
333        return TRUE;
334
335    if (GrabSupersedesSecond(pSecondGrab, pFirstGrab))
336        return TRUE;
337
338    firstD.exact = pFirstGrab->keybut;
339    firstM.exact = pFirstGrab->modifiers;
340    if (pFirstGrab->hasExt) {
341        firstD.pMask = GRABEXT(pFirstGrab)->pKeyButMask;
342        firstM.pMask = GRABEXT(pFirstGrab)->pModifiersMask;
343    }
344    else {
345        firstD.pMask = NULL;
346        firstM.pMask = NULL;
347    }
348    secondD.exact = pSecondGrab->keybut;
349    secondM.exact = pSecondGrab->modifiers;
350    if (pSecondGrab->hasExt) {
351        secondD.pMask = GRABEXT(pSecondGrab)->pKeyButMask;
352        secondM.pMask = GRABEXT(pSecondGrab)->pModifiersMask;
353    }
354    else {
355        secondD.pMask = NULL;
356        secondM.pMask = NULL;
357    }
358
359    if (DetailSupersedesSecond(&secondD, &firstD, (unsigned short) AnyKey) &&
360        DetailSupersedesSecond(&firstM, &secondM, (unsigned short) AnyModifier))
361        return TRUE;
362
363    if (DetailSupersedesSecond(&firstD, &secondD, (unsigned short) AnyKey) &&
364        DetailSupersedesSecond(&secondM, &firstM, (unsigned short) AnyModifier))
365        return TRUE;
366
367    return FALSE;
368}
369
370/*
371 * Delete a grab combination from the passive grab list.  Each entry will
372 * be checked to see if it is affected by the grab being deleted.  This
373 * may result in multiple entries being modified/deleted.
374 */
375
376static void
377DeleteServerGrabFromList(XtServerGrabPtr *passiveListPtr,
378                         XtServerGrabPtr pMinuendGrab)
379{
380    register XtServerGrabPtr *next;
381    register XtServerGrabPtr grab;
382    register XtServerGrabExtPtr ext;
383
384    for (next = passiveListPtr; (grab = *next);) {
385        if (GrabMatchesSecond(grab, pMinuendGrab) &&
386            (pDisplay(grab) == pDisplay(pMinuendGrab))) {
387            if (GrabSupersedesSecond(pMinuendGrab, grab)) {
388                /*
389                 * The entry being deleted encompasses the list entry,
390                 * so delete the list entry.
391                 */
392                *next = grab->next;
393                FreeGrab(grab);
394                continue;
395            }
396
397            if (!grab->hasExt) {
398                grab = (XtServerGrabPtr)
399                    XtRealloc((char *) grab, (sizeof(XtServerGrabRec) +
400                                              sizeof(XtServerGrabExtRec)));
401                *next = grab;
402                grab->hasExt = True;
403                ext = GRABEXT(grab);
404                ext->pKeyButMask = NULL;
405                ext->pModifiersMask = NULL;
406                ext->confineTo = None;
407                ext->cursor = None;
408            }
409            else
410                ext = GRABEXT(grab);
411            if ((grab->keybut == AnyKey) && (grab->modifiers != AnyModifier)) {
412                /*
413                 * If the list entry has the key detail of AnyKey, and
414                 * a modifier detail not set to AnyModifier, then we
415                 * simply need to turn off the key detail bit in the
416                 * list entry's key detail mask.
417                 */
418                DeleteDetailFromMask(&ext->pKeyButMask, pMinuendGrab->keybut);
419            }
420            else if ((grab->modifiers == AnyModifier) &&
421                     (grab->keybut != AnyKey)) {
422                /*
423                 * The list entry has a specific key detail, but its
424                 * modifier detail is set to AnyModifier; so, we only
425                 * need to turn off the specified modifier combination
426                 * in the list entry's modifier mask.
427                 */
428                DeleteDetailFromMask(&ext->pModifiersMask,
429                                     pMinuendGrab->modifiers);
430            }
431            else if ((pMinuendGrab->keybut != AnyKey) &&
432                     (pMinuendGrab->modifiers != AnyModifier)) {
433                /*
434                 * The list entry has a key detail of AnyKey and a
435                 * modifier detail of AnyModifier; the entry being
436                 * deleted has a specific key and a specific modifier
437                 * combination.  Therefore, we need to mask off the
438                 * keycode from the list entry, and also create a
439                 * new entry for this keycode, which has a modifier
440                 * mask set to AnyModifier & ~(deleted modifiers).
441                 */
442                XtServerGrabPtr pNewGrab;
443
444                DeleteDetailFromMask(&ext->pKeyButMask, pMinuendGrab->keybut);
445                pNewGrab = CreateGrab(grab->widget,
446                                      (Boolean) grab->ownerEvents,
447                                      (Modifiers) AnyModifier,
448                                      pMinuendGrab->keybut,
449                                      (int) grab->pointerMode,
450                                      (int) grab->keyboardMode,
451                                      (Mask) 0, (Window) 0, (Cursor) 0, True);
452                GRABEXT(pNewGrab)->pModifiersMask =
453                    CopyDetailMask(ext->pModifiersMask);
454
455                DeleteDetailFromMask(&GRABEXT(pNewGrab)->pModifiersMask,
456                                     pMinuendGrab->modifiers);
457
458                pNewGrab->next = *passiveListPtr;
459                *passiveListPtr = pNewGrab;
460            }
461            else if (pMinuendGrab->keybut == AnyKey) {
462                /*
463                 * The list entry has keycode AnyKey and modifier
464                 * AnyModifier; the entry being deleted has
465                 * keycode AnyKey and specific modifiers.  So we
466                 * simply need to mask off the specified modifier
467                 * combination.
468                 */
469                DeleteDetailFromMask(&ext->pModifiersMask,
470                                     pMinuendGrab->modifiers);
471            }
472            else {
473                /*
474                 * The list entry has keycode AnyKey and modifier
475                 * AnyModifier; the entry being deleted has a
476                 * specific keycode and modifier AnyModifier.  So
477                 * we simply need to mask off the specified
478                 * keycode.
479                 */
480                DeleteDetailFromMask(&ext->pKeyButMask, pMinuendGrab->keybut);
481            }
482        }
483        next = &(*next)->next;
484    }
485}
486
487static void
488DestroyPassiveList(XtServerGrabPtr *passiveListPtr)
489{
490    XtServerGrabPtr next, grab;
491
492    for (next = *passiveListPtr; next;) {
493        grab = next;
494        next = grab->next;
495
496        /* not necessary to explicitly ungrab key or button;
497         * window is being destroyed so server will take care of it.
498         */
499
500        FreeGrab(grab);
501    }
502}
503
504/*
505 * This function is called at widget destroy time to clean up
506 */
507void
508_XtDestroyServerGrabs(Widget w,
509                      XtPointer closure,
510                      XtPointer call_data _X_UNUSED)
511{
512    XtPerWidgetInput pwi = (XtPerWidgetInput) closure;
513    XtPerDisplayInput pdi;
514
515    LOCK_PROCESS;
516    pdi = _XtGetPerDisplayInput(XtDisplay(w));
517    _XtClearAncestorCache(w);
518    UNLOCK_PROCESS;
519
520    /* Remove the active grab, if necessary */
521    if ((pdi->keyboard.grabType != XtNoServerGrab) &&
522        (pdi->keyboard.grab.widget == w)) {
523        pdi->keyboard.grabType = XtNoServerGrab;
524        pdi->activatingKey = (KeyCode) 0;
525    }
526    if ((pdi->pointer.grabType != XtNoServerGrab) &&
527        (pdi->pointer.grab.widget == w))
528        pdi->pointer.grabType = XtNoServerGrab;
529
530    DestroyPassiveList(&pwi->keyList);
531    DestroyPassiveList(&pwi->ptrList);
532
533    _XtFreePerWidgetInput(w, pwi);
534}
535
536/*
537 * If the incoming event is on the passive grab list, then activate
538 * the grab.  The grab will remain in effect until the key is released.
539 */
540
541XtServerGrabPtr
542_XtCheckServerGrabsOnWidget(XEvent *event, Widget widget, _XtBoolean isKeyboard)
543{
544    register XtServerGrabPtr grab;
545    XtServerGrabRec tempGrab;
546    XtServerGrabPtr *passiveListPtr;
547    XtPerWidgetInput pwi;
548
549    LOCK_PROCESS;
550    pwi = _XtGetPerWidgetInput(widget, FALSE);
551    UNLOCK_PROCESS;
552    if (!pwi)
553        return (XtServerGrabPtr) NULL;
554    if (isKeyboard)
555        passiveListPtr = &pwi->keyList;
556    else
557        passiveListPtr = &pwi->ptrList;
558
559    /*
560     * if either there is no entry in the context manager or the entry
561     * is empty, or the keyboard is grabbed, then no work to be done
562     */
563    if (!*passiveListPtr)
564        return (XtServerGrabPtr) NULL;
565
566    /* Take only the lower thirteen bits as modifier state.  The X Keyboard
567     * Extension may be representing keyboard group state in two upper bits.
568     */
569    tempGrab.widget = widget;
570    tempGrab.keybut = (KeyCode) event->xkey.keycode;    /* also xbutton.button */
571    tempGrab.modifiers = event->xkey.state & 0x1FFF;    /*also xbutton.state */
572    tempGrab.hasExt = False;
573
574    for (grab = *passiveListPtr; grab; grab = grab->next) {
575        if (GrabMatchesSecond(&tempGrab, grab))
576            return (grab);
577    }
578    return (XtServerGrabPtr) NULL;
579}
580
581/*
582 * This handler is needed to guarantee that we see releases on passive
583 * button grabs for widgets that haven't selected for button release.
584 */
585
586static void
587ActiveHandler(Widget widget _X_UNUSED,
588              XtPointer pdi _X_UNUSED,
589              XEvent *event _X_UNUSED,
590              Boolean *cont _X_UNUSED)
591{
592    /* nothing */
593}
594
595/*
596 *      MakeGrab
597 */
598static void
599MakeGrab(XtServerGrabPtr grab,
600         XtServerGrabPtr *passiveListPtr,
601         Boolean isKeyboard,
602         XtPerDisplayInput pdi,
603         XtPerWidgetInput pwi)
604{
605    if (!isKeyboard && !pwi->active_handler_added) {
606        XtAddEventHandler(grab->widget, ButtonReleaseMask, FALSE,
607                          ActiveHandler, (XtPointer) pdi);
608        pwi->active_handler_added = TRUE;
609    }
610
611    if (isKeyboard) {
612        XGrabKey(pDisplay(grab),
613                 grab->keybut, grab->modifiers,
614                 pWindow(grab), grab->ownerEvents,
615                 grab->pointerMode, grab->keyboardMode);
616    }
617    else {
618        Window confineTo = None;
619        Cursor cursor = None;
620
621        if (grab->hasExt) {
622            if (grab->confineToIsWidgetWin)
623                confineTo = XtWindow(grab->widget);
624            else
625                confineTo = GRABEXT(grab)->confineTo;
626            cursor = GRABEXT(grab)->cursor;
627        }
628        XGrabButton(pDisplay(grab),
629                    grab->keybut, grab->modifiers,
630                    pWindow(grab), grab->ownerEvents, grab->eventMask,
631                    grab->pointerMode, grab->keyboardMode, confineTo, cursor);
632    }
633
634    /* Add the new grab entry to the passive key grab list */
635    grab->next = *passiveListPtr;
636    *passiveListPtr = grab;
637}
638
639static void
640MakeGrabs(XtServerGrabPtr *passiveListPtr,
641          Boolean isKeyboard,
642          XtPerDisplayInput pdi)
643{
644    XtServerGrabPtr next = *passiveListPtr;
645
646    /*
647     * make MakeGrab build a new list that has had the merge
648     * processing done on it. Start with an empty list
649     * (passiveListPtr).
650     */
651    LOCK_PROCESS;
652    *passiveListPtr = NULL;
653    while (next) {
654        XtServerGrabPtr grab;
655        XtPerWidgetInput pwi;
656
657        grab = next;
658        next = grab->next;
659        pwi = _XtGetPerWidgetInput(grab->widget, FALSE);
660        MakeGrab(grab, passiveListPtr, isKeyboard, pdi, pwi);
661    }
662    UNLOCK_PROCESS;
663}
664
665/*
666 * This function is the event handler attached to the associated widget
667 * when grabs need to be added, but the widget is not yet realized.  When
668 * it is first mapped, this handler will be invoked, and it will add all
669 * needed grabs.
670 */
671
672static void
673RealizeHandler(Widget widget,
674               XtPointer closure,
675               XEvent *event _X_UNUSED,
676               Boolean *cont _X_UNUSED)
677{
678    XtPerWidgetInput pwi = (XtPerWidgetInput) closure;
679    XtPerDisplayInput pdi;
680
681    LOCK_PROCESS;
682    pdi = _XtGetPerDisplayInput(XtDisplay(widget));
683    UNLOCK_PROCESS;
684    MakeGrabs(&pwi->keyList, KEYBOARD, pdi);
685    MakeGrabs(&pwi->ptrList, POINTER, pdi);
686
687    XtRemoveEventHandler(widget, XtAllEvents, True,
688                         RealizeHandler, (XtPointer) pwi);
689    pwi->realize_handler_added = FALSE;
690}
691
692/***************************************************************************/
693/**************************** Global Routines ******************************/
694/***************************************************************************/
695
696/*
697 * Routine used by an application to set up a passive grab for a key/modifier
698 * combination.
699 */
700
701static void
702GrabKeyOrButton(Widget widget,
703                KeyCode keyOrButton,
704                Modifiers modifiers,
705                Boolean owner_events,
706                int pointer_mode,
707                int keyboard_mode,
708                Mask event_mask,
709                Window confine_to,
710                Cursor cursor,
711                Boolean isKeyboard)
712{
713    XtServerGrabPtr *passiveListPtr;
714    XtServerGrabPtr newGrab;
715    XtPerWidgetInput pwi;
716    XtPerDisplayInput pdi;
717
718    XtCheckSubclass(widget, coreWidgetClass, "in XtGrabKey or XtGrabButton");
719    LOCK_PROCESS;
720    pwi = _XtGetPerWidgetInput(widget, TRUE);
721    if (isKeyboard)
722        passiveListPtr = &pwi->keyList;
723    else
724        passiveListPtr = &pwi->ptrList;
725    pdi = _XtGetPerDisplayInput(XtDisplay(widget));
726    UNLOCK_PROCESS;
727    newGrab = CreateGrab(widget, owner_events, modifiers,
728                         keyOrButton, pointer_mode, keyboard_mode,
729                         event_mask, confine_to, cursor, False);
730    /*
731     *  if the widget is realized then process the entry into the grab
732     * list. else if the list is empty (i.e. first time) then add the
733     * event handler. then add the raw entry to the list for processing
734     * in the handler at realize time.
735     */
736    if (XtIsRealized(widget))
737        MakeGrab(newGrab, passiveListPtr, isKeyboard, pdi, pwi);
738    else {
739        if (!pwi->realize_handler_added) {
740            XtAddEventHandler(widget, StructureNotifyMask, FALSE,
741                              RealizeHandler, (XtPointer) pwi);
742            pwi->realize_handler_added = TRUE;
743        }
744
745        while (*passiveListPtr)
746            passiveListPtr = &(*passiveListPtr)->next;
747        *passiveListPtr = newGrab;
748    }
749}
750
751static void
752UngrabKeyOrButton(Widget widget,
753                  int keyOrButton,
754                  Modifiers modifiers,
755                  Boolean isKeyboard)
756{
757    XtServerGrabRec tempGrab;
758    XtPerWidgetInput pwi;
759
760    XtCheckSubclass(widget, coreWidgetClass,
761                    "in XtUngrabKey or XtUngrabButton");
762
763    /* Build a temporary grab list entry */
764    tempGrab.widget = widget;
765    tempGrab.modifiers = (unsigned short) modifiers;
766    tempGrab.keybut = (KeyCode) keyOrButton;
767    tempGrab.hasExt = False;
768
769    LOCK_PROCESS;
770    pwi = _XtGetPerWidgetInput(widget, FALSE);
771    UNLOCK_PROCESS;
772    /*
773     * if there is no entry in the context manager then somethings wrong
774     */
775    if (!pwi) {
776        XtAppWarningMsg(XtWidgetToApplicationContext(widget),
777                        "invalidGrab", "ungrabKeyOrButton", XtCXtToolkitError,
778                        "Attempt to remove nonexistent passive grab",
779                        NULL, NULL);
780        return;
781    }
782
783    if (XtIsRealized(widget)) {
784        if (isKeyboard)
785            XUngrabKey(widget->core.screen->display,
786                       keyOrButton, (unsigned int) modifiers,
787                       widget->core.window);
788        else
789            XUngrabButton(widget->core.screen->display,
790                          (unsigned) keyOrButton, (unsigned int) modifiers,
791                          widget->core.window);
792    }
793
794    /* Delete all entries which are encompassed by the specified grab. */
795    DeleteServerGrabFromList(isKeyboard ? &pwi->keyList : &pwi->ptrList,
796                             &tempGrab);
797}
798
799void
800XtGrabKey(Widget widget,
801          _XtKeyCode keycode,
802          Modifiers modifiers,
803          _XtBoolean owner_events,
804          int pointer_mode,
805          int keyboard_mode)
806{
807    WIDGET_TO_APPCON(widget);
808
809    LOCK_APP(app);
810    GrabKeyOrButton(widget, (KeyCode) keycode, modifiers,
811                    (Boolean) owner_events, pointer_mode, keyboard_mode,
812                    (Mask) 0, (Window) None, (Cursor) None, KEYBOARD);
813    UNLOCK_APP(app);
814}
815
816void
817XtGrabButton(Widget widget,
818             int button,
819             Modifiers modifiers,
820             _XtBoolean owner_events,
821             unsigned int event_mask,
822             int pointer_mode,
823             int keyboard_mode,
824             Window confine_to,
825             Cursor cursor)
826{
827    WIDGET_TO_APPCON(widget);
828
829    LOCK_APP(app);
830    GrabKeyOrButton(widget, (KeyCode) button, modifiers, (Boolean) owner_events,
831                    pointer_mode, keyboard_mode,
832                    (Mask) event_mask, confine_to, cursor, POINTER);
833    UNLOCK_APP(app);
834}
835
836/*
837 * Routine used by an application to clear a passive grab for a key/modifier
838 * combination.
839 */
840
841void
842XtUngrabKey(Widget widget, _XtKeyCode keycode, Modifiers modifiers)
843{
844    WIDGET_TO_APPCON(widget);
845
846    LOCK_APP(app);
847    UngrabKeyOrButton(widget, (int) keycode, modifiers, KEYBOARD);
848    UNLOCK_APP(app);
849}
850
851void
852XtUngrabButton(Widget widget, unsigned int button, Modifiers modifiers)
853{
854    WIDGET_TO_APPCON(widget);
855
856    LOCK_APP(app);
857    UngrabKeyOrButton(widget, (KeyCode) button, modifiers, POINTER);
858    UNLOCK_APP(app);
859}
860
861/*
862 * Active grab of Device. clear any client side grabs so we don't lock
863 */
864static int
865GrabDevice(Widget widget,
866           Boolean owner_events,
867           int pointer_mode,
868           int keyboard_mode,
869           Mask event_mask,
870           Window confine_to,
871           Cursor cursor,
872           Time time,
873           Boolean isKeyboard)
874{
875    XtPerDisplayInput pdi;
876    int returnVal;
877
878    XtCheckSubclass(widget, coreWidgetClass,
879                    "in XtGrabKeyboard or XtGrabPointer");
880    if (!XtIsRealized(widget))
881        return GrabNotViewable;
882    LOCK_PROCESS;
883    pdi = _XtGetPerDisplayInput(XtDisplay(widget));
884    UNLOCK_PROCESS;
885    if (!isKeyboard)
886        returnVal = XGrabPointer(XtDisplay(widget), XtWindow(widget),
887                                 owner_events, (unsigned) event_mask,
888                                 pointer_mode, keyboard_mode,
889                                 confine_to, cursor, time);
890    else
891        returnVal = XGrabKeyboard(XtDisplay(widget), XtWindow(widget),
892                                  owner_events, pointer_mode,
893                                  keyboard_mode, time);
894
895    if (returnVal == GrabSuccess) {
896        XtDevice device;
897
898        device = isKeyboard ? &pdi->keyboard : &pdi->pointer;
899
900        /* fill in the server grab rec */
901        device->grab.widget = widget;
902        device->grab.modifiers = 0;
903        device->grab.keybut = 0;
904        XtSetBit(device->grab.ownerEvents, owner_events);
905        XtSetBit(device->grab.pointerMode, pointer_mode);
906        XtSetBit(device->grab.keyboardMode, keyboard_mode);
907        device->grab.hasExt = False;
908        device->grabType = XtActiveServerGrab;
909        pdi->activatingKey = (KeyCode) 0;
910    }
911    return returnVal;
912}
913
914static void
915UngrabDevice(Widget widget, Time time, Boolean isKeyboard)
916{
917    XtPerDisplayInput pdi;
918    XtDevice device;
919
920    LOCK_PROCESS;
921    pdi = _XtGetPerDisplayInput(XtDisplay(widget));
922    UNLOCK_PROCESS;
923    device = isKeyboard ? &pdi->keyboard : &pdi->pointer;
924
925    XtCheckSubclass(widget, coreWidgetClass,
926                    "in XtUngrabKeyboard or XtUngrabPointer");
927
928    if (device->grabType != XtNoServerGrab) {
929
930        if (device->grabType != XtPseudoPassiveServerGrab
931            && XtIsRealized(widget)) {
932            if (isKeyboard)
933                XUngrabKeyboard(XtDisplay(widget), time);
934            else
935                XUngrabPointer(XtDisplay(widget), time);
936        }
937        device->grabType = XtNoServerGrab;
938        pdi->activatingKey = (KeyCode) 0;
939    }
940}
941
942/*
943 * Active grab of keyboard. clear any client side grabs so we don't lock
944 */
945int
946XtGrabKeyboard(Widget widget,
947               _XtBoolean owner_events,
948               int pointer_mode,
949               int keyboard_mode,
950               Time time)
951{
952    int retval;
953
954    WIDGET_TO_APPCON(widget);
955
956    LOCK_APP(app);
957    retval = GrabDevice(widget, (Boolean) owner_events,
958                        pointer_mode, keyboard_mode,
959                        (Mask) 0, (Window) None, (Cursor) None, time, KEYBOARD);
960    UNLOCK_APP(app);
961    return retval;
962}
963
964/*
965 * Ungrab the keyboard
966 */
967
968void
969XtUngrabKeyboard(Widget widget, Time time)
970{
971    WIDGET_TO_APPCON(widget);
972
973    LOCK_APP(app);
974    UngrabDevice(widget, time, KEYBOARD);
975    UNLOCK_APP(app);
976}
977
978/*
979 * grab the pointer
980 */
981int
982XtGrabPointer(Widget widget,
983              _XtBoolean owner_events,
984              unsigned int event_mask,
985              int pointer_mode,
986              int keyboard_mode,
987              Window confine_to,
988              Cursor cursor,
989              Time time)
990{
991    int retval;
992
993    WIDGET_TO_APPCON(widget);
994
995    LOCK_APP(app);
996    retval = GrabDevice(widget, (Boolean) owner_events,
997                        pointer_mode, keyboard_mode,
998                        (Mask) event_mask, confine_to, cursor, time, POINTER);
999    UNLOCK_APP(app);
1000    return retval;
1001}
1002
1003/*
1004 * Ungrab the pointer
1005 */
1006
1007void
1008XtUngrabPointer(Widget widget, Time time)
1009{
1010    WIDGET_TO_APPCON(widget);
1011
1012    LOCK_APP(app);
1013    UngrabDevice(widget, time, POINTER);
1014    UNLOCK_APP(app);
1015}
1016
1017void
1018_XtRegisterPassiveGrabs(Widget widget)
1019{
1020    XtPerWidgetInput pwi = _XtGetPerWidgetInput(widget, FALSE);
1021
1022    if (pwi != NULL && !pwi->realize_handler_added) {
1023        XtAddEventHandler(widget, StructureNotifyMask, FALSE,
1024                          RealizeHandler, (XtPointer) pwi);
1025        pwi->realize_handler_added = TRUE;
1026    }
1027}
1028