3366 lines
		
	
	
		
			105 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			3366 lines
		
	
	
		
			105 KiB
		
	
	
	
		
			C
		
	
	
	
| /************************************************************
 | |
| 
 | |
| Copyright 1989, 1998  The Open Group
 | |
| 
 | |
| Permission to use, copy, modify, distribute, and sell this software and its
 | |
| documentation for any purpose is hereby granted without fee, provided that
 | |
| the above copyright notice appear in all copies and that both that
 | |
| copyright notice and this permission notice appear in supporting
 | |
| documentation.
 | |
| 
 | |
| The above copyright notice and this permission notice shall be included in
 | |
| all copies or substantial portions of the Software.
 | |
| 
 | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 | |
| OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 | |
| AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
| 
 | |
| Except as contained in this notice, the name of The Open Group shall not be
 | |
| used in advertising or otherwise to promote the sale, use or other dealings
 | |
| in this Software without prior written authorization from The Open Group.
 | |
| 
 | |
| Copyright 1989 by Hewlett-Packard Company, Palo Alto, California.
 | |
| 
 | |
| 			All Rights Reserved
 | |
| 
 | |
| Permission to use, copy, modify, and distribute this software and its
 | |
| documentation for any purpose and without fee is hereby granted,
 | |
| provided that the above copyright notice appear in all copies and that
 | |
| both that copyright notice and this permission notice appear in
 | |
| supporting documentation, and that the name of Hewlett-Packard not be
 | |
| used in advertising or publicity pertaining to distribution of the
 | |
| software without specific, written prior permission.
 | |
| 
 | |
| HEWLETT-PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 | |
| ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 | |
| HEWLETT-PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 | |
| ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 | |
| WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 | |
| ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 | |
| SOFTWARE.
 | |
| 
 | |
| ********************************************************/
 | |
| 
 | |
| /*
 | |
|  * Copyright © 2010 Collabora Ltd.
 | |
|  * Copyright © 2011 Red Hat, Inc.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice (including the next
 | |
|  * paragraph) shall be included in all copies or substantial portions of the
 | |
|  * Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | |
|  * DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  * Author: Daniel Stone <daniel@fooishbar.org>
 | |
|  */
 | |
| 
 | |
| /********************************************************************
 | |
|  *
 | |
|  *  Routines to register and initialize extension input devices.
 | |
|  *  This also contains ProcessOtherEvent, the routine called from DDX
 | |
|  *  to route extension events.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <dix-config.h>
 | |
| 
 | |
| #include "dix/cursor_priv.h"
 | |
| #include "os/bug_priv.h"
 | |
| 
 | |
| #include <X11/X.h>
 | |
| #include <X11/Xproto.h>
 | |
| #include <X11/extensions/geproto.h>
 | |
| #include <X11/extensions/XI.h>
 | |
| #include <X11/extensions/XIproto.h>
 | |
| #include <X11/extensions/XI2proto.h>
 | |
| #include <X11/extensions/XKBproto.h>
 | |
| 
 | |
| #include "dix/dix_priv.h"
 | |
| #include "dix/dixgrabs_priv.h"
 | |
| #include "dix/eventconvert.h"
 | |
| #include "dix/exevents_priv.h"
 | |
| #include "dix/input_priv.h"
 | |
| #include "dix/resource_priv.h"
 | |
| #include "mi/mi_priv.h"
 | |
| #include "xkb/xkbsrv_priv.h"
 | |
| 
 | |
| #include "inputstr.h"
 | |
| #include "windowstr.h"
 | |
| #include "miscstruct.h"
 | |
| #include "region.h"
 | |
| #include "extnsionst.h"
 | |
| #include "exglobals.h"
 | |
| #include "eventstr.h"
 | |
| #include "scrnintstr.h"
 | |
| #include "listdev.h"            /* for CopySwapXXXClass */
 | |
| #include "xace.h"
 | |
| #include "xiquerydevice.h"      /* For List*Info */
 | |
| #include "eventstr.h"
 | |
| #include "inpututils.h"
 | |
| 
 | |
| #define WID(w) ((w) ? ((w)->drawable.id) : 0)
 | |
| #define AllModifiersMask ( \
 | |
| 	ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | \
 | |
| 	Mod3Mask | Mod4Mask | Mod5Mask )
 | |
| #define AllButtonsMask ( \
 | |
| 	Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask )
 | |
| 
 | |
| Bool ShouldFreeInputMasks(WindowPtr /* pWin */ ,
 | |
|                           Bool  /* ignoreSelectedEvents */
 | |
|     );
 | |
| static Bool MakeInputMasks(WindowPtr    /* pWin */
 | |
|     );
 | |
| 
 | |
| /*
 | |
|  * Only let the given client know of core events which will affect its
 | |
|  * interpretation of input events, if the client's ClientPointer (or the
 | |
|  * paired keyboard) is the current device.
 | |
|  */
 | |
| int
 | |
| XIShouldNotify(ClientPtr client, DeviceIntPtr dev)
 | |
| {
 | |
|     DeviceIntPtr current_ptr = PickPointer(client);
 | |
|     DeviceIntPtr current_kbd = GetMaster(current_ptr, KEYBOARD_OR_FLOAT);
 | |
| 
 | |
|     if (dev == current_kbd || dev == current_ptr)
 | |
|         return 1;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| IsPointerEvent(InternalEvent *event)
 | |
| {
 | |
|     switch (event->any.type) {
 | |
|     case ET_ButtonPress:
 | |
|     case ET_ButtonRelease:
 | |
|     case ET_Motion:
 | |
|         /* XXX: enter/leave ?? */
 | |
|         return TRUE;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| IsTouchEvent(InternalEvent *event)
 | |
| {
 | |
|     switch (event->any.type) {
 | |
|     case ET_TouchBegin:
 | |
|     case ET_TouchUpdate:
 | |
|     case ET_TouchEnd:
 | |
|         return TRUE;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| IsGestureEvent(InternalEvent *event)
 | |
| {
 | |
|     switch (event->any.type) {
 | |
|     case ET_GesturePinchBegin:
 | |
|     case ET_GesturePinchUpdate:
 | |
|     case ET_GesturePinchEnd:
 | |
|     case ET_GestureSwipeBegin:
 | |
|     case ET_GestureSwipeUpdate:
 | |
|     case ET_GestureSwipeEnd:
 | |
|         return TRUE;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| IsGestureBeginEvent(InternalEvent *event)
 | |
| {
 | |
|     switch (event->any.type) {
 | |
|     case ET_GesturePinchBegin:
 | |
|     case ET_GestureSwipeBegin:
 | |
|         return TRUE;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| IsGestureEndEvent(InternalEvent *event)
 | |
| {
 | |
|     switch (event->any.type) {
 | |
|     case ET_GesturePinchEnd:
 | |
|     case ET_GestureSwipeEnd:
 | |
|         return TRUE;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @return the device matching the deviceid of the device set in the event, or
 | |
|  * NULL if the event is not an XInput event.
 | |
|  */
 | |
| DeviceIntPtr
 | |
| XIGetDevice(xEvent *xE)
 | |
| {
 | |
|     DeviceIntPtr pDev = NULL;
 | |
| 
 | |
|     if (xE->u.u.type == DeviceButtonPress ||
 | |
|         xE->u.u.type == DeviceButtonRelease ||
 | |
|         xE->u.u.type == DeviceMotionNotify ||
 | |
|         xE->u.u.type == ProximityIn ||
 | |
|         xE->u.u.type == ProximityOut || xE->u.u.type == DevicePropertyNotify) {
 | |
|         int rc;
 | |
|         int id;
 | |
| 
 | |
|         id = ((deviceKeyButtonPointer *) xE)->deviceid & ~MORE_EVENTS;
 | |
| 
 | |
|         rc = dixLookupDevice(&pDev, id, serverClient, DixUnknownAccess);
 | |
|         if (rc != Success)
 | |
|             ErrorF("[dix] XIGetDevice failed on XACE restrictions (%d)\n", rc);
 | |
|     }
 | |
|     return pDev;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Copy the device->key into master->key and send a mapping notify to the
 | |
|  * clients if appropriate.
 | |
|  * master->key needs to be allocated by the caller.
 | |
|  *
 | |
|  * Device is the slave device. If it is attached to a master device, we may
 | |
|  * need to send a mapping notify to the client because it causes the MD
 | |
|  * to change state.
 | |
|  *
 | |
|  * Mapping notify needs to be sent in the following cases:
 | |
|  *      - different slave device on same master
 | |
|  *      - different master
 | |
|  *
 | |
|  * XXX: They way how the code is we also send a map notify if the slave device
 | |
|  * stays the same, but the master changes. This isn't really necessary though.
 | |
|  *
 | |
|  * XXX: this gives you funny behaviour with the ClientPointer. When a
 | |
|  * MappingNotify is sent to the client, the client usually responds with a
 | |
|  * GetKeyboardMapping. This will retrieve the ClientPointer's keyboard
 | |
|  * mapping, regardless of which keyboard sent the last mapping notify request.
 | |
|  * So depending on the CP setting, your keyboard may change layout in each
 | |
|  * app...
 | |
|  *
 | |
|  * This code is basically the old SwitchCoreKeyboard.
 | |
|  */
 | |
| 
 | |
| void
 | |
| CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master)
 | |
| {
 | |
|     KeyClassPtr mk = master->key;
 | |
| 
 | |
|     if (device == master)
 | |
|         return;
 | |
| 
 | |
|     mk->sourceid = device->id;
 | |
| 
 | |
|     if (!XkbDeviceApplyKeymap(master, device->key->xkbInfo->desc))
 | |
|         FatalError("Couldn't pivot keymap from device to core!\n");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Copies the feedback classes from device "from" into device "to". Classes
 | |
|  * are duplicated (not just flipping the pointers). All feedback classes are
 | |
|  * linked lists, the full list is duplicated.
 | |
|  */
 | |
| static void
 | |
| DeepCopyFeedbackClasses(DeviceIntPtr from, DeviceIntPtr to)
 | |
| {
 | |
|     ClassesPtr classes;
 | |
| 
 | |
|     if (from->intfeed) {
 | |
|         IntegerFeedbackPtr *i, it;
 | |
| 
 | |
|         if (!to->intfeed) {
 | |
|             classes = to->unused_classes;
 | |
|             to->intfeed = classes->intfeed;
 | |
|             classes->intfeed = NULL;
 | |
|         }
 | |
| 
 | |
|         i = &to->intfeed;
 | |
|         for (it = from->intfeed; it; it = it->next) {
 | |
|             if (!(*i)) {
 | |
|                 *i = calloc(1, sizeof(IntegerFeedbackClassRec));
 | |
|                 if (!(*i)) {
 | |
|                     ErrorF("[Xi] Cannot alloc memory for class copy.");
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             (*i)->CtrlProc = it->CtrlProc;
 | |
|             (*i)->ctrl = it->ctrl;
 | |
| 
 | |
|             i = &(*i)->next;
 | |
|         }
 | |
|     }
 | |
|     else if (to->intfeed && !from->intfeed) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->intfeed = to->intfeed;
 | |
|         to->intfeed = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->stringfeed) {
 | |
|         StringFeedbackPtr *s, it;
 | |
| 
 | |
|         if (!to->stringfeed) {
 | |
|             classes = to->unused_classes;
 | |
|             to->stringfeed = classes->stringfeed;
 | |
|             classes->stringfeed = NULL;
 | |
|         }
 | |
| 
 | |
|         s = &to->stringfeed;
 | |
|         for (it = from->stringfeed; it; it = it->next) {
 | |
|             if (!(*s)) {
 | |
|                 *s = calloc(1, sizeof(StringFeedbackClassRec));
 | |
|                 if (!(*s)) {
 | |
|                     ErrorF("[Xi] Cannot alloc memory for class copy.");
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             (*s)->CtrlProc = it->CtrlProc;
 | |
|             (*s)->ctrl = it->ctrl;
 | |
| 
 | |
|             s = &(*s)->next;
 | |
|         }
 | |
|     }
 | |
|     else if (to->stringfeed && !from->stringfeed) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->stringfeed = to->stringfeed;
 | |
|         to->stringfeed = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->bell) {
 | |
|         BellFeedbackPtr *b, it;
 | |
| 
 | |
|         if (!to->bell) {
 | |
|             classes = to->unused_classes;
 | |
|             to->bell = classes->bell;
 | |
|             classes->bell = NULL;
 | |
|         }
 | |
| 
 | |
|         b = &to->bell;
 | |
|         for (it = from->bell; it; it = it->next) {
 | |
|             if (!(*b)) {
 | |
|                 *b = calloc(1, sizeof(BellFeedbackClassRec));
 | |
|                 if (!(*b)) {
 | |
|                     ErrorF("[Xi] Cannot alloc memory for class copy.");
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             (*b)->BellProc = it->BellProc;
 | |
|             (*b)->CtrlProc = it->CtrlProc;
 | |
|             (*b)->ctrl = it->ctrl;
 | |
| 
 | |
|             b = &(*b)->next;
 | |
|         }
 | |
|     }
 | |
|     else if (to->bell && !from->bell) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->bell = to->bell;
 | |
|         to->bell = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->leds) {
 | |
|         LedFeedbackPtr *l, it;
 | |
| 
 | |
|         if (!to->leds) {
 | |
|             classes = to->unused_classes;
 | |
|             to->leds = classes->leds;
 | |
|             classes->leds = NULL;
 | |
|         }
 | |
| 
 | |
|         l = &to->leds;
 | |
|         for (it = from->leds; it; it = it->next) {
 | |
|             if (!(*l)) {
 | |
|                 *l = calloc(1, sizeof(LedFeedbackClassRec));
 | |
|                 if (!(*l)) {
 | |
|                     ErrorF("[Xi] Cannot alloc memory for class copy.");
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             (*l)->CtrlProc = it->CtrlProc;
 | |
|             (*l)->ctrl = it->ctrl;
 | |
|             if ((*l)->xkb_sli)
 | |
|                 XkbFreeSrvLedInfo((*l)->xkb_sli);
 | |
|             (*l)->xkb_sli = XkbCopySrvLedInfo(from, it->xkb_sli, NULL, *l);
 | |
| 
 | |
|             l = &(*l)->next;
 | |
|         }
 | |
|     }
 | |
|     else if (to->leds && !from->leds) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->leds = to->leds;
 | |
|         to->leds = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| DeepCopyKeyboardClasses(DeviceIntPtr from, DeviceIntPtr to)
 | |
| {
 | |
|     ClassesPtr classes;
 | |
| 
 | |
|     /* XkbInitDevice (->XkbInitIndicatorMap->XkbFindSrvLedInfo) relies on the
 | |
|      * kbdfeed to be set up properly, so let's do the feedback classes first.
 | |
|      */
 | |
|     if (from->kbdfeed) {
 | |
|         KbdFeedbackPtr *k, it;
 | |
| 
 | |
|         if (!to->kbdfeed) {
 | |
|             classes = to->unused_classes;
 | |
| 
 | |
|             to->kbdfeed = classes->kbdfeed;
 | |
|             if (!to->kbdfeed)
 | |
|                 InitKeyboardDeviceStruct(to, NULL, NULL, NULL);
 | |
|             classes->kbdfeed = NULL;
 | |
|         }
 | |
| 
 | |
|         k = &to->kbdfeed;
 | |
|         for (it = from->kbdfeed; it; it = it->next) {
 | |
|             if (!(*k)) {
 | |
|                 *k = calloc(1, sizeof(KbdFeedbackClassRec));
 | |
|                 if (!*k) {
 | |
|                     ErrorF("[Xi] Cannot alloc memory for class copy.");
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             (*k)->BellProc = it->BellProc;
 | |
|             (*k)->CtrlProc = it->CtrlProc;
 | |
|             (*k)->ctrl = it->ctrl;
 | |
|             if ((*k)->xkb_sli)
 | |
|                 XkbFreeSrvLedInfo((*k)->xkb_sli);
 | |
|             (*k)->xkb_sli = XkbCopySrvLedInfo(from, it->xkb_sli, *k, NULL);
 | |
| 
 | |
|             k = &(*k)->next;
 | |
|         }
 | |
|     }
 | |
|     else if (to->kbdfeed && !from->kbdfeed) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->kbdfeed = to->kbdfeed;
 | |
|         to->kbdfeed = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->key) {
 | |
|         if (!to->key) {
 | |
|             classes = to->unused_classes;
 | |
|             to->key = classes->key;
 | |
|             if (!to->key)
 | |
|                 InitKeyboardDeviceStruct(to, NULL, NULL, NULL);
 | |
|             else
 | |
|                 classes->key = NULL;
 | |
|         }
 | |
| 
 | |
|         CopyKeyClass(from, to);
 | |
|     }
 | |
|     else if (to->key && !from->key) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->key = to->key;
 | |
|         to->key = NULL;
 | |
|     }
 | |
| 
 | |
|     /* If a SrvLedInfoPtr's flags are XkbSLI_IsDefault, the names and maps
 | |
|      * pointer point into the xkbInfo->desc struct.  XkbCopySrvLedInfo
 | |
|      * didn't update the pointers so we need to do it manually here.
 | |
|      */
 | |
|     if (to->kbdfeed) {
 | |
|         KbdFeedbackPtr k;
 | |
| 
 | |
|         for (k = to->kbdfeed; k; k = k->next) {
 | |
|             if (!k->xkb_sli)
 | |
|                 continue;
 | |
|             if (k->xkb_sli->flags & XkbSLI_IsDefault) {
 | |
|                 assert(to->key);
 | |
|                 k->xkb_sli->names = to->key->xkbInfo->desc->names->indicators;
 | |
|                 k->xkb_sli->maps = to->key->xkbInfo->desc->indicators->maps;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* We can't just copy over the focus class. When an app sets the focus,
 | |
|      * it'll do so on the master device. Copying the SDs focus means losing
 | |
|      * the focus.
 | |
|      * So we only copy the focus class if the device didn't have one,
 | |
|      * otherwise we leave it as it is.
 | |
|      */
 | |
|     if (from->focus) {
 | |
|         if (!to->focus) {
 | |
|             WindowPtr *oldTrace;
 | |
| 
 | |
|             classes = to->unused_classes;
 | |
|             to->focus = classes->focus;
 | |
|             if (!to->focus) {
 | |
|                 to->focus = calloc(1, sizeof(FocusClassRec));
 | |
|                 if (!to->focus)
 | |
|                     FatalError("[Xi] no memory for class shift.\n");
 | |
|             }
 | |
|             else
 | |
|                 classes->focus = NULL;
 | |
| 
 | |
|             oldTrace = to->focus->trace;
 | |
|             memcpy(to->focus, from->focus, sizeof(FocusClassRec));
 | |
|             to->focus->trace = reallocarray(oldTrace,
 | |
|                                             to->focus->traceSize,
 | |
|                                             sizeof(WindowPtr));
 | |
|             if (!to->focus->trace && to->focus->traceSize)
 | |
|                 FatalError("[Xi] no memory for trace.\n");
 | |
|             memcpy(to->focus->trace, from->focus->trace,
 | |
|                    from->focus->traceSize * sizeof(WindowPtr));
 | |
|             to->focus->sourceid = from->id;
 | |
|         }
 | |
|     }
 | |
|     else if (to->focus) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->focus = to->focus;
 | |
|         to->focus = NULL;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| /* FIXME: this should really be shared with the InitValuatorAxisClassRec and
 | |
|  * similar */
 | |
| static void
 | |
| DeepCopyPointerClasses(DeviceIntPtr from, DeviceIntPtr to)
 | |
| {
 | |
|     ClassesPtr classes;
 | |
| 
 | |
|     /* Feedback classes must be copied first */
 | |
|     if (from->ptrfeed) {
 | |
|         PtrFeedbackPtr *p, it;
 | |
| 
 | |
|         if (!to->ptrfeed) {
 | |
|             classes = to->unused_classes;
 | |
|             to->ptrfeed = classes->ptrfeed;
 | |
|             classes->ptrfeed = NULL;
 | |
|         }
 | |
| 
 | |
|         p = &to->ptrfeed;
 | |
|         for (it = from->ptrfeed; it; it = it->next) {
 | |
|             if (!(*p)) {
 | |
|                 *p = calloc(1, sizeof(PtrFeedbackClassRec));
 | |
|                 if (!*p) {
 | |
|                     ErrorF("[Xi] Cannot alloc memory for class copy.");
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             (*p)->CtrlProc = it->CtrlProc;
 | |
|             (*p)->ctrl = it->ctrl;
 | |
| 
 | |
|             p = &(*p)->next;
 | |
|         }
 | |
|     }
 | |
|     else if (to->ptrfeed && !from->ptrfeed) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->ptrfeed = to->ptrfeed;
 | |
|         to->ptrfeed = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->valuator) {
 | |
|         ValuatorClassPtr v;
 | |
| 
 | |
|         if (!to->valuator) {
 | |
|             classes = to->unused_classes;
 | |
|             to->valuator = classes->valuator;
 | |
|             if (to->valuator)
 | |
|                 classes->valuator = NULL;
 | |
|         }
 | |
| 
 | |
|         v = AllocValuatorClass(to->valuator, from->valuator->numAxes);
 | |
| 
 | |
|         if (!v)
 | |
|             FatalError("[Xi] no memory for class shift.\n");
 | |
| 
 | |
|         to->valuator = v;
 | |
|         memcpy(v->axes, from->valuator->axes, v->numAxes * sizeof(AxisInfo));
 | |
| 
 | |
|         v->sourceid = from->id;
 | |
|     }
 | |
|     else if (to->valuator && !from->valuator) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->valuator = to->valuator;
 | |
|         to->valuator = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->button) {
 | |
|         if (!to->button) {
 | |
|             classes = to->unused_classes;
 | |
|             to->button = classes->button;
 | |
|             if (!to->button) {
 | |
|                 to->button = calloc(1, sizeof(ButtonClassRec));
 | |
|                 if (!to->button)
 | |
|                     FatalError("[Xi] no memory for class shift.\n");
 | |
|                 to->button->numButtons = from->button->numButtons;
 | |
|             }
 | |
|             else
 | |
|                 classes->button = NULL;
 | |
|         }
 | |
| 
 | |
|         if (from->button->xkb_acts) {
 | |
|             size_t maxbuttons = max(to->button->numButtons, from->button->numButtons);
 | |
|             to->button->xkb_acts = XNFreallocarray(to->button->xkb_acts,
 | |
|                                                    maxbuttons,
 | |
|                                                    sizeof(XkbAction));
 | |
|             memset(to->button->xkb_acts, 0, maxbuttons * sizeof(XkbAction));
 | |
|             memcpy(to->button->xkb_acts, from->button->xkb_acts,
 | |
|                    from->button->numButtons * sizeof(XkbAction));
 | |
|         }
 | |
|         else {
 | |
|             free(to->button->xkb_acts);
 | |
|             to->button->xkb_acts = NULL;
 | |
|         }
 | |
| 
 | |
|         memcpy(to->button->labels, from->button->labels,
 | |
|                from->button->numButtons * sizeof(Atom));
 | |
|         to->button->sourceid = from->id;
 | |
|     }
 | |
|     else if (to->button && !from->button) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->button = to->button;
 | |
|         to->button = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->proximity) {
 | |
|         if (!to->proximity) {
 | |
|             classes = to->unused_classes;
 | |
|             to->proximity = classes->proximity;
 | |
|             if (!to->proximity) {
 | |
|                 to->proximity = calloc(1, sizeof(ProximityClassRec));
 | |
|                 if (!to->proximity)
 | |
|                     FatalError("[Xi] no memory for class shift.\n");
 | |
|             }
 | |
|             else
 | |
|                 classes->proximity = NULL;
 | |
|         }
 | |
|         memcpy(to->proximity, from->proximity, sizeof(ProximityClassRec));
 | |
|         to->proximity->sourceid = from->id;
 | |
|     }
 | |
|     else if (to->proximity) {
 | |
|         classes = to->unused_classes;
 | |
|         classes->proximity = to->proximity;
 | |
|         to->proximity = NULL;
 | |
|     }
 | |
| 
 | |
|     if (from->touch) {
 | |
|         TouchClassPtr t, f;
 | |
| 
 | |
|         if (!to->touch) {
 | |
|             classes = to->unused_classes;
 | |
|             to->touch = classes->touch;
 | |
|             if (!to->touch) {
 | |
|                 int i;
 | |
| 
 | |
|                 to->touch = calloc(1, sizeof(TouchClassRec));
 | |
|                 if (!to->touch)
 | |
|                     FatalError("[Xi] no memory for class shift.\n");
 | |
|                 to->touch->num_touches = from->touch->num_touches;
 | |
|                 to->touch->touches = calloc(to->touch->num_touches,
 | |
|                                             sizeof(TouchPointInfoRec));
 | |
|                 for (i = 0; i < to->touch->num_touches; i++)
 | |
|                     TouchInitTouchPoint(to->touch, to->valuator, i);
 | |
|                 if (!to->touch)
 | |
|                     FatalError("[Xi] no memory for class shift.\n");
 | |
|             }
 | |
|             else
 | |
|                 classes->touch = NULL;
 | |
|         }
 | |
| 
 | |
|         t = to->touch;
 | |
|         f = from->touch;
 | |
|         t->sourceid = f->sourceid;
 | |
|         t->max_touches = f->max_touches;
 | |
|         t->mode = f->mode;
 | |
|         t->buttonsDown = f->buttonsDown;
 | |
|         t->state = f->state;
 | |
|         t->motionMask = f->motionMask;
 | |
|         /* to->touches and to->num_touches are separate on the master,
 | |
|          * don't copy */
 | |
|     }
 | |
|     /* Don't remove touch class if from->touch is non-existent. The to device
 | |
|      * may have an active touch grab, so we need to keep the touch class record
 | |
|      * around. */
 | |
| 
 | |
|     if (from->gesture) {
 | |
|         if (!to->gesture) {
 | |
|             classes = to->unused_classes;
 | |
|             to->gesture = classes->gesture;
 | |
|             if (!to->gesture) {
 | |
|                 if (!InitGestureClassDeviceStruct(to, from->gesture->max_touches))
 | |
|                     FatalError("[Xi] no memory for class shift.\n");
 | |
|             }
 | |
|             else
 | |
|                 classes->gesture = NULL;
 | |
|         }
 | |
| 
 | |
|         to->gesture->sourceid = from->gesture->sourceid;
 | |
|         /* to->gesture->gesture is separate on the master,  don't copy */
 | |
|     }
 | |
|     /* Don't remove gesture class if from->gesture is non-existent. The to device
 | |
|      * may have an active gesture grab, so we need to keep the gesture class record
 | |
|      * around. */
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Copies the CONTENT of the classes of device from into the classes in device
 | |
|  * to. From and to are identical after finishing.
 | |
|  *
 | |
|  * If to does not have classes from currently has, the classes are stored in
 | |
|  * to's devPrivates system. Later, we recover it again from there if needed.
 | |
|  * Saves a few memory allocations.
 | |
|  */
 | |
| void
 | |
| DeepCopyDeviceClasses(DeviceIntPtr from, DeviceIntPtr to,
 | |
|                       DeviceChangedEvent *dce)
 | |
| {
 | |
|     input_lock();
 | |
| 
 | |
|     /* generic feedback classes, not tied to pointer and/or keyboard */
 | |
|     DeepCopyFeedbackClasses(from, to);
 | |
| 
 | |
|     if ((dce->flags & DEVCHANGE_KEYBOARD_EVENT))
 | |
|         DeepCopyKeyboardClasses(from, to);
 | |
|     if ((dce->flags & DEVCHANGE_POINTER_EVENT))
 | |
|         DeepCopyPointerClasses(from, to);
 | |
| 
 | |
|     input_unlock();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Send an XI2 DeviceChangedEvent to all interested clients.
 | |
|  */
 | |
| void
 | |
| XISendDeviceChangedEvent(DeviceIntPtr device, DeviceChangedEvent *dce)
 | |
| {
 | |
|     xXIDeviceChangedEvent *dcce;
 | |
|     int rc;
 | |
| 
 | |
|     rc = EventToXI2((InternalEvent *) dce, (xEvent **) &dcce);
 | |
|     if (rc != Success) {
 | |
|         ErrorF("[Xi] event conversion from DCE failed with code %d\n", rc);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     /* we don't actually swap if there's a NullClient, swapping is done
 | |
|      * later when event is delivered. */
 | |
|     SendEventToAllWindows(device, XI_DeviceChangedMask, (xEvent *) dcce, 1);
 | |
|     free(dcce);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ChangeMasterDeviceClasses(DeviceIntPtr device, DeviceChangedEvent *dce)
 | |
| {
 | |
|     DeviceIntPtr slave;
 | |
|     int rc;
 | |
| 
 | |
|     /* For now, we don't have devices that change physically. */
 | |
|     if (!InputDevIsMaster(device))
 | |
|         return;
 | |
| 
 | |
|     rc = dixLookupDevice(&slave, dce->sourceid, serverClient, DixReadAccess);
 | |
| 
 | |
|     if (rc != Success)
 | |
|         return;                 /* Device has disappeared */
 | |
| 
 | |
|     if (InputDevIsMaster(slave))
 | |
|         return;
 | |
| 
 | |
|     if (InputDevIsFloating(slave))
 | |
|         return;                 /* set floating since the event */
 | |
| 
 | |
|     if (GetMaster(slave, MASTER_ATTACHED)->id != dce->masterid)
 | |
|         return;                 /* not our slave anymore, don't care */
 | |
| 
 | |
|     /* FIXME: we probably need to send a DCE for the new slave now */
 | |
| 
 | |
|     device->public.devicePrivate = slave->public.devicePrivate;
 | |
| 
 | |
|     /* FIXME: the classes may have changed since we generated the event. */
 | |
|     DeepCopyDeviceClasses(slave, device, dce);
 | |
|     dce->deviceid = device->id;
 | |
|     XISendDeviceChangedEvent(device, dce);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Add state and motionMask to the filter for this event. The protocol
 | |
|  * supports some extra masks for motion when a button is down:
 | |
|  * ButtonXMotionMask and the DeviceButtonMotionMask to trigger only when at
 | |
|  * least one button (or that specific button is down). These masks need to
 | |
|  * be added to the filters for core/XI motion events.
 | |
|  *
 | |
|  * @param device The device to update the mask for
 | |
|  * @param state The current button state mask
 | |
|  * @param motion_mask The motion mask (DeviceButtonMotionMask or 0)
 | |
|  */
 | |
| static void
 | |
| UpdateDeviceMotionMask(DeviceIntPtr device, unsigned short state,
 | |
|                        Mask motion_mask)
 | |
| {
 | |
|     Mask mask;
 | |
| 
 | |
|     mask = PointerMotionMask | state | motion_mask;
 | |
|     SetMaskForEvent(device->id, mask, DeviceMotionNotify);
 | |
|     SetMaskForEvent(device->id, mask, MotionNotify);
 | |
| }
 | |
| 
 | |
| static void
 | |
| IncreaseButtonCount(DeviceIntPtr dev, int key, CARD8 *buttons_down,
 | |
|                     Mask *motion_mask, unsigned short *state)
 | |
| {
 | |
|     if (dev->valuator)
 | |
|         dev->valuator->motionHintWindow = NullWindow;
 | |
| 
 | |
|     (*buttons_down)++;
 | |
|     *motion_mask = DeviceButtonMotionMask;
 | |
|     if (dev->button->map[key] <= 5)
 | |
|         *state |= (Button1Mask >> 1) << dev->button->map[key];
 | |
| }
 | |
| 
 | |
| static void
 | |
| DecreaseButtonCount(DeviceIntPtr dev, int key, CARD8 *buttons_down,
 | |
|                     Mask *motion_mask, unsigned short *state)
 | |
| {
 | |
|     if (dev->valuator)
 | |
|         dev->valuator->motionHintWindow = NullWindow;
 | |
| 
 | |
|     if (*buttons_down >= 1 && !--(*buttons_down))
 | |
|         *motion_mask = 0;
 | |
|     if (dev->button->map[key] <= 5)
 | |
|         *state &= ~((Button1Mask >> 1) << dev->button->map[key]);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Update the device state according to the data in the event.
 | |
|  *
 | |
|  * return values are
 | |
|  *   DEFAULT ... process as normal
 | |
|  *   DONT_PROCESS ... return immediately from caller
 | |
|  */
 | |
| #define DEFAULT 0
 | |
| #define DONT_PROCESS 1
 | |
| int
 | |
| UpdateDeviceState(DeviceIntPtr device, DeviceEvent *event)
 | |
| {
 | |
|     int i;
 | |
|     int key = 0, last_valuator;
 | |
| 
 | |
|     KeyClassPtr k = NULL;
 | |
|     ButtonClassPtr b = NULL;
 | |
|     ValuatorClassPtr v = NULL;
 | |
|     TouchClassPtr t = NULL;
 | |
| 
 | |
|     /* This event is always the first we get, before the actual events with
 | |
|      * the data. However, the way how the DDX is set up, "device" will
 | |
|      * actually be the slave device that caused the event.
 | |
|      */
 | |
|     switch (event->type) {
 | |
|     case ET_DeviceChanged:
 | |
|         ChangeMasterDeviceClasses(device, (DeviceChangedEvent *) event);
 | |
|         return DONT_PROCESS;    /* event has been sent already */
 | |
|     case ET_Motion:
 | |
|     case ET_ButtonPress:
 | |
|     case ET_ButtonRelease:
 | |
|     case ET_KeyPress:
 | |
|     case ET_KeyRelease:
 | |
|     case ET_ProximityIn:
 | |
|     case ET_ProximityOut:
 | |
|     case ET_TouchBegin:
 | |
|     case ET_TouchUpdate:
 | |
|     case ET_TouchEnd:
 | |
|         break;
 | |
|     default:
 | |
|         /* other events don't update the device */
 | |
|         return DEFAULT;
 | |
|     }
 | |
| 
 | |
|     k = device->key;
 | |
|     v = device->valuator;
 | |
|     b = device->button;
 | |
|     t = device->touch;
 | |
| 
 | |
|     key = event->detail.key;
 | |
| 
 | |
|     /* Update device axis */
 | |
|     /* Check valuators first */
 | |
|     last_valuator = -1;
 | |
|     for (i = 0; i < MAX_VALUATORS; i++) {
 | |
|         if (BitIsOn(&event->valuators.mask, i)) {
 | |
|             if (!v) {
 | |
|                 ErrorF("[Xi] Valuators reported for non-valuator device '%s'. "
 | |
|                        "Ignoring event.\n", device->name);
 | |
|                 return DONT_PROCESS;
 | |
|             }
 | |
|             else if (v->numAxes < i) {
 | |
|                 ErrorF("[Xi] Too many valuators reported for device '%s'. "
 | |
|                        "Ignoring event.\n", device->name);
 | |
|                 return DONT_PROCESS;
 | |
|             }
 | |
|             last_valuator = i;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i <= last_valuator && i < v->numAxes; i++) {
 | |
|         /* XXX: Relative/Absolute mode */
 | |
|         if (BitIsOn(&event->valuators.mask, i))
 | |
|             v->axisVal[i] = event->valuators.data[i];
 | |
|     }
 | |
| 
 | |
|     if (event->type == ET_KeyPress) {
 | |
|         if (!k)
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         /* don't allow ddx to generate multiple downs, but repeats are okay */
 | |
|         if (key_is_down(device, key, KEY_PROCESSED) && !event->key_repeat)
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         if (device->valuator)
 | |
|             device->valuator->motionHintWindow = NullWindow;
 | |
|         set_key_down(device, key, KEY_PROCESSED);
 | |
|     }
 | |
|     else if (event->type == ET_KeyRelease) {
 | |
|         if (!k)
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         if (!key_is_down(device, key, KEY_PROCESSED))   /* guard against duplicates */
 | |
|             return DONT_PROCESS;
 | |
|         if (device->valuator)
 | |
|             device->valuator->motionHintWindow = NullWindow;
 | |
|         set_key_up(device, key, KEY_PROCESSED);
 | |
|     }
 | |
|     else if (event->type == ET_ButtonPress) {
 | |
|         if (!b)
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         if (button_is_down(device, key, BUTTON_PROCESSED))
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         set_button_down(device, key, BUTTON_PROCESSED);
 | |
| 
 | |
|         if (!b->map[key])
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         IncreaseButtonCount(device, key, &b->buttonsDown, &b->motionMask,
 | |
|                             &b->state);
 | |
|         UpdateDeviceMotionMask(device, b->state, b->motionMask);
 | |
|     }
 | |
|     else if (event->type == ET_ButtonRelease) {
 | |
|         if (!b)
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         if (!button_is_down(device, key, BUTTON_PROCESSED))
 | |
|             return DONT_PROCESS;
 | |
|         if (InputDevIsMaster(device)) {
 | |
|             DeviceIntPtr sd;
 | |
| 
 | |
|             /*
 | |
|              * Leave the button down if any slave has the
 | |
|              * button still down. Note that this depends on the
 | |
|              * event being delivered through the slave first
 | |
|              */
 | |
|             for (sd = inputInfo.devices; sd; sd = sd->next) {
 | |
|                 if (InputDevIsMaster(sd) || GetMaster(sd, MASTER_POINTER) != device)
 | |
|                     continue;
 | |
|                 if (!sd->button)
 | |
|                     continue;
 | |
|                 for (i = 1; i <= sd->button->numButtons; i++)
 | |
|                     if (sd->button->map[i] == key &&
 | |
|                         button_is_down(sd, i, BUTTON_PROCESSED))
 | |
|                         return DONT_PROCESS;
 | |
|             }
 | |
|         }
 | |
|         set_button_up(device, key, BUTTON_PROCESSED);
 | |
|         if (!b->map[key])
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         DecreaseButtonCount(device, key, &b->buttonsDown, &b->motionMask,
 | |
|                             &b->state);
 | |
|         UpdateDeviceMotionMask(device, b->state, b->motionMask);
 | |
|     }
 | |
|     else if (event->type == ET_ProximityIn)
 | |
|         device->proximity->in_proximity = TRUE;
 | |
|     else if (event->type == ET_ProximityOut)
 | |
|         device->proximity->in_proximity = FALSE;
 | |
|     else if (event->type == ET_TouchBegin) {
 | |
|         BUG_RETURN_VAL(!b || !v, DONT_PROCESS);
 | |
|         BUG_RETURN_VAL(!t, DONT_PROCESS);
 | |
| 
 | |
|         if (!b->map[key])
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         if (!(event->flags & TOUCH_POINTER_EMULATED) ||
 | |
|             (event->flags & TOUCH_REPLAYING))
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         IncreaseButtonCount(device, key, &t->buttonsDown, &t->motionMask,
 | |
|                             &t->state);
 | |
|         UpdateDeviceMotionMask(device, t->state, DeviceButtonMotionMask);
 | |
|     }
 | |
|     else if (event->type == ET_TouchEnd) {
 | |
|         BUG_RETURN_VAL(!b || !v, DONT_PROCESS);
 | |
|         BUG_RETURN_VAL(!t, DONT_PROCESS);
 | |
| 
 | |
|         if (t->buttonsDown <= 0 || !b->map[key])
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         if (!(event->flags & TOUCH_POINTER_EMULATED))
 | |
|             return DONT_PROCESS;
 | |
| 
 | |
|         DecreaseButtonCount(device, key, &t->buttonsDown, &t->motionMask,
 | |
|                             &t->state);
 | |
|         UpdateDeviceMotionMask(device, t->state, DeviceButtonMotionMask);
 | |
|     }
 | |
| 
 | |
|     return DEFAULT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A client that does not have the TouchOwnership mask set may not receive a
 | |
|  * TouchBegin event if there is at least one grab active.
 | |
|  *
 | |
|  * @return TRUE if the client selected for ownership events on the given
 | |
|  * window for this device, FALSE otherwise
 | |
|  */
 | |
| static inline Bool
 | |
| TouchClientWantsOwnershipEvents(ClientPtr client, DeviceIntPtr dev,
 | |
|                                 WindowPtr win)
 | |
| {
 | |
|     InputClients *iclient;
 | |
| 
 | |
|     assert(wOtherInputMasks(win));
 | |
|     nt_list_for_each_entry(iclient, wOtherInputMasks(win)->inputClients, next) {
 | |
|         if (dixClientForInputClients(iclient) != client)
 | |
|             continue;
 | |
| 
 | |
|         return xi2mask_isset(iclient->xi2mask, dev, XI_TouchOwnership);
 | |
|     }
 | |
| 
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| TouchSendOwnershipEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, int reason,
 | |
|                         XID resource)
 | |
| {
 | |
|     int nev, i;
 | |
|     InternalEvent *tel = InitEventList(GetMaximumEventsNum());
 | |
| 
 | |
|     nev = GetTouchOwnershipEvents(tel, dev, ti, reason, resource, 0);
 | |
|     for (i = 0; i < nev; i++)
 | |
|         mieqProcessDeviceEvent(dev, tel + i, NULL);
 | |
| 
 | |
|     FreeEventList(tel, GetMaximumEventsNum());
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Attempts to deliver a touch event to the given client.
 | |
|  */
 | |
| static Bool
 | |
| DeliverOneTouchEvent(ClientPtr client, DeviceIntPtr dev, TouchPointInfoPtr ti,
 | |
|                      GrabPtr grab, WindowPtr win, InternalEvent *ev)
 | |
| {
 | |
|     int err;
 | |
|     xEvent *xi2;
 | |
|     Mask filter;
 | |
|     Window child = DeepestSpriteWin(&ti->sprite)->drawable.id;
 | |
| 
 | |
|     /* FIXME: owner event handling */
 | |
| 
 | |
|     /* If the client does not have the ownership mask set and is not
 | |
|      * the current owner of the touch, only pretend we delivered */
 | |
|     if (!grab && ti->num_grabs != 0 &&
 | |
|         !TouchClientWantsOwnershipEvents(client, dev, win))
 | |
|         return TRUE;
 | |
| 
 | |
|     /* If we fail here, we're going to leave a client hanging. */
 | |
|     err = EventToXI2(ev, &xi2);
 | |
|     if (err != Success)
 | |
|         FatalError("[Xi] %s: XI2 conversion failed in %s"
 | |
|                    " (%d)\n", dev->name, __func__, err);
 | |
| 
 | |
|     FixUpEventFromWindow(&ti->sprite, xi2, win, child, FALSE);
 | |
|     filter = GetEventFilter(dev, xi2);
 | |
|     if (XaceHookReceiveAccess(client, win, xi2, 1) != Success)
 | |
|         return FALSE;
 | |
|     TryClientEvents(client, dev, xi2, 1, filter, filter, NullGrab);
 | |
|     free(xi2);
 | |
| 
 | |
|     /* Returning the value from TryClientEvents isn't useful, since all our
 | |
|      * resource-gone cleanups will update the delivery list anyway. */
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| ActivateEarlyAccept(DeviceIntPtr dev, TouchPointInfoPtr ti)
 | |
| {
 | |
|     ClientPtr client;
 | |
|     XID error;
 | |
|     GrabPtr grab = ti->listeners[0].grab;
 | |
| 
 | |
|     BUG_RETURN(ti->listeners[0].type != TOUCH_LISTENER_GRAB &&
 | |
|                ti->listeners[0].type != TOUCH_LISTENER_POINTER_GRAB);
 | |
|     BUG_RETURN(!grab);
 | |
| 
 | |
|     client = dixClientForGrab(grab);
 | |
| 
 | |
|     if (TouchAcceptReject(client, dev, XIAcceptTouch, ti->client_id,
 | |
|                           ti->listeners[0].window->drawable.id, &error) != Success)
 | |
|         ErrorF("[Xi] Failed to accept touch grab after early acceptance.\n");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Find the oldest touch that still has a pointer emulation client.
 | |
|  *
 | |
|  * Pointer emulation can only be performed for the oldest touch. Otherwise, the
 | |
|  * order of events seen by the client will be wrong. This function helps us find
 | |
|  * the next touch to be emulated.
 | |
|  *
 | |
|  * @param dev The device to find touches for.
 | |
|  */
 | |
| static TouchPointInfoPtr
 | |
| FindOldestPointerEmulatedTouch(DeviceIntPtr dev)
 | |
| {
 | |
|     TouchPointInfoPtr oldest = NULL;
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < dev->touch->num_touches; i++) {
 | |
|         TouchPointInfoPtr ti = dev->touch->touches + i;
 | |
|         int j;
 | |
| 
 | |
|         if (!ti->active || !ti->emulate_pointer)
 | |
|             continue;
 | |
| 
 | |
|         for (j = 0; j < ti->num_listeners; j++) {
 | |
|             if (ti->listeners[j].type == TOUCH_LISTENER_POINTER_GRAB ||
 | |
|                 ti->listeners[j].type == TOUCH_LISTENER_POINTER_REGULAR)
 | |
|                 break;
 | |
|         }
 | |
|         if (j == ti->num_listeners)
 | |
|             continue;
 | |
| 
 | |
|         if (!oldest) {
 | |
|             oldest = ti;
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if (oldest->client_id - ti->client_id < UINT_MAX / 2)
 | |
|             oldest = ti;
 | |
|     }
 | |
| 
 | |
|     return oldest;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * If the current owner has rejected the event, deliver the
 | |
|  * TouchOwnership/TouchBegin to the next item in the sprite stack.
 | |
|  */
 | |
| static void
 | |
| TouchPuntToNextOwner(DeviceIntPtr dev, TouchPointInfoPtr ti,
 | |
|                      TouchOwnershipEvent *ev)
 | |
| {
 | |
|     TouchListener *listener = &ti->listeners[0]; /* new owner */
 | |
|     int accepted_early = listener->state == TOUCH_LISTENER_EARLY_ACCEPT;
 | |
| 
 | |
|     /* Deliver the ownership */
 | |
|     if (listener->state == TOUCH_LISTENER_AWAITING_OWNER || accepted_early)
 | |
|         DeliverTouchEvents(dev, ti, (InternalEvent *) ev,
 | |
|                            listener->listener);
 | |
|     else if (listener->state == TOUCH_LISTENER_AWAITING_BEGIN) {
 | |
|         /* We can't punt to a pointer listener unless all older pointer
 | |
|          * emulated touches have been seen already. */
 | |
|         if ((listener->type == TOUCH_LISTENER_POINTER_GRAB ||
 | |
|              listener->type == TOUCH_LISTENER_POINTER_REGULAR) &&
 | |
|             ti != FindOldestPointerEmulatedTouch(dev))
 | |
|             return;
 | |
| 
 | |
|         TouchEventHistoryReplay(ti, dev, listener->listener);
 | |
|     }
 | |
| 
 | |
|     /* New owner has Begin/Update but not end. If touch is pending_finish,
 | |
|      * emulate the TouchEnd now */
 | |
|     if (ti->pending_finish) {
 | |
|         TouchEmitTouchEnd(dev, ti, 0, 0);
 | |
| 
 | |
|         /* If the last owner is not a touch grab, finalise the touch, we
 | |
|            won't get more correspondence on this.
 | |
|          */
 | |
|         if (ti->num_listeners == 1 &&
 | |
|             (ti->num_grabs == 0 ||
 | |
|              listener->grab->grabtype != XI2 ||
 | |
|              !xi2mask_isset(listener->grab->xi2mask, dev, XI_TouchBegin))) {
 | |
|             TouchEndTouch(dev, ti);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (accepted_early)
 | |
|         ActivateEarlyAccept(dev, ti);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Check the oldest touch to see if it needs to be replayed to its pointer
 | |
|  * owner.
 | |
|  *
 | |
|  * Touch event propagation is paused if it hits a pointer listener while an
 | |
|  * older touch with a pointer listener is waiting on accept or reject. This
 | |
|  * function will restart propagation of a paused touch if needed.
 | |
|  *
 | |
|  * @param dev The device to check touches for.
 | |
|  */
 | |
| static void
 | |
| CheckOldestTouch(DeviceIntPtr dev)
 | |
| {
 | |
|     TouchPointInfoPtr oldest = FindOldestPointerEmulatedTouch(dev);
 | |
| 
 | |
|     if (oldest && oldest->listeners[0].state == TOUCH_LISTENER_AWAITING_BEGIN)
 | |
|         TouchPuntToNextOwner(dev, oldest, NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Process a touch rejection.
 | |
|  *
 | |
|  * @param sourcedev The source device of the touch sequence.
 | |
|  * @param ti The touchpoint info record.
 | |
|  * @param resource The resource of the client rejecting the touch.
 | |
|  * @param ev TouchOwnership event to send. Set to NULL if no event should be
 | |
|  *        sent.
 | |
|  */
 | |
| void
 | |
| TouchRejected(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, XID resource,
 | |
|               TouchOwnershipEvent *ev)
 | |
| {
 | |
|     Bool was_owner = (resource == ti->listeners[0].listener);
 | |
|     int i;
 | |
| 
 | |
|     /* Send a TouchEnd event to the resource being removed, but only if they
 | |
|      * haven't received one yet already */
 | |
|     for (i = 0; i < ti->num_listeners; i++) {
 | |
|         if (ti->listeners[i].listener == resource) {
 | |
|             if (ti->listeners[i].state != TOUCH_LISTENER_HAS_END)
 | |
|                 TouchEmitTouchEnd(sourcedev, ti, TOUCH_REJECT, resource);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Remove the resource from the listener list, updating
 | |
|      * ti->num_listeners, as well as ti->num_grabs if it was a grab. */
 | |
|     TouchRemoveListener(ti, resource);
 | |
| 
 | |
|     /* If the current owner was removed and there are further listeners, deliver
 | |
|      * the TouchOwnership or TouchBegin event to the new owner. */
 | |
|     if (ev && ti->num_listeners > 0 && was_owner)
 | |
|         TouchPuntToNextOwner(sourcedev, ti, ev);
 | |
|     else if (ti->num_listeners == 0)
 | |
|         TouchEndTouch(sourcedev, ti);
 | |
| 
 | |
|     CheckOldestTouch(sourcedev);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Processes a TouchOwnership event, indicating a grab has accepted the touch
 | |
|  * it currently owns, or a grab or selection has been removed.  Will generate
 | |
|  * and send TouchEnd events to all clients removed from the delivery list, as
 | |
|  * well as possibly sending the new TouchOwnership event.  May end the
 | |
|  * touchpoint if it is pending finish.
 | |
|  */
 | |
| static void
 | |
| ProcessTouchOwnershipEvent(TouchOwnershipEvent *ev,
 | |
|                            DeviceIntPtr dev)
 | |
| {
 | |
|     TouchPointInfoPtr ti = TouchFindByClientID(dev, ev->touchid);
 | |
| 
 | |
|     if (!ti) {
 | |
|         DebugF("[Xi] %s: Failed to get event %d for touchpoint %d\n",
 | |
|                dev->name, ev->type, ev->touchid);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (ev->reason == XIRejectTouch)
 | |
|         TouchRejected(dev, ti, ev->resource, ev);
 | |
|     else if (ev->reason == XIAcceptTouch) {
 | |
|         int i;
 | |
| 
 | |
| 
 | |
|         /* For pointer-emulated listeners that ungrabbed the active grab,
 | |
|          * the state was forced to TOUCH_LISTENER_HAS_END. Still go
 | |
|          * through the motions of ending the touch if the listener has
 | |
|          * already seen the end. This ensures that the touch record is ended in
 | |
|          * the server.
 | |
|          */
 | |
|         if (ti->listeners[0].state == TOUCH_LISTENER_HAS_END)
 | |
|             TouchEmitTouchEnd(dev, ti, TOUCH_ACCEPT, ti->listeners[0].listener);
 | |
| 
 | |
|         /* The touch owner has accepted the touch.  Send TouchEnd events to
 | |
|          * everyone else, and truncate the list of listeners. */
 | |
|         for (i = 1; i < ti->num_listeners; i++)
 | |
|             TouchEmitTouchEnd(dev, ti, TOUCH_ACCEPT, ti->listeners[i].listener);
 | |
| 
 | |
|         while (ti->num_listeners > 1)
 | |
|             TouchRemoveListener(ti, ti->listeners[1].listener);
 | |
|         /* Owner accepted after receiving end */
 | |
|         if (ti->listeners[0].state == TOUCH_LISTENER_HAS_END)
 | |
|             TouchEndTouch(dev, ti);
 | |
|         else
 | |
|             ti->listeners[0].state = TOUCH_LISTENER_HAS_ACCEPTED;
 | |
|     }
 | |
|     else {  /* this is the very first ownership event for a grab */
 | |
|         DeliverTouchEvents(dev, ti, (InternalEvent *) ev, ev->resource);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Copy the event's valuator information into the touchpoint, we may need
 | |
|  * this for emulated TouchEnd events.
 | |
|  */
 | |
| static void
 | |
| TouchCopyValuatorData(DeviceEvent *ev, TouchPointInfoPtr ti)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < ARRAY_SIZE(ev->valuators.data); i++)
 | |
|         if (BitIsOn(ev->valuators.mask, i))
 | |
|             valuator_mask_set_double(ti->valuators, i, ev->valuators.data[i]);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Given a touch event and a potential listener, retrieve info needed for
 | |
|  * processing the event.
 | |
|  *
 | |
|  * @param dev The device generating the touch event.
 | |
|  * @param ti The touch point info record for the touch event.
 | |
|  * @param ev The touch event to process.
 | |
|  * @param listener The touch event listener that may receive the touch event.
 | |
|  * @param[out] client The client that should receive the touch event.
 | |
|  * @param[out] win The window to deliver the event on.
 | |
|  * @param[out] grab The grab to deliver the event through, if any.
 | |
|  * @param[out] mask The XI 2.x event mask of the grab or selection, if any.
 | |
|  * @return TRUE if an event should be delivered to the listener, FALSE
 | |
|  *         otherwise.
 | |
|  */
 | |
| static Bool
 | |
| RetrieveTouchDeliveryData(DeviceIntPtr dev, TouchPointInfoPtr ti,
 | |
|                           InternalEvent *ev, TouchListener * listener,
 | |
|                           ClientPtr *client, WindowPtr *win, GrabPtr *grab,
 | |
|                           XI2Mask **mask)
 | |
| {
 | |
|     int rc;
 | |
|     InputClients *iclients = NULL;
 | |
|     *mask = NULL;
 | |
| 
 | |
|     if (listener->type == TOUCH_LISTENER_GRAB ||
 | |
|         listener->type == TOUCH_LISTENER_POINTER_GRAB) {
 | |
|         *grab = listener->grab;
 | |
| 
 | |
|         BUG_RETURN_VAL(!*grab, FALSE);
 | |
| 
 | |
|         *client = dixClientForGrab(*grab);
 | |
|         *win = (*grab)->window;
 | |
|         *mask = (*grab)->xi2mask;
 | |
|     }
 | |
|     else {
 | |
|         rc = dixLookupResourceByType((void **) win, listener->listener,
 | |
|                                      listener->resource_type,
 | |
|                                      serverClient, DixSendAccess);
 | |
|         if (rc != Success)
 | |
|             return FALSE;
 | |
| 
 | |
|         if (listener->level == XI2) {
 | |
|             int evtype;
 | |
| 
 | |
|             if (ti->emulate_pointer &&
 | |
|                 listener->type == TOUCH_LISTENER_POINTER_REGULAR)
 | |
|                 evtype = GetXI2Type(TouchGetPointerEventType(ev));
 | |
|             else
 | |
|                 evtype = GetXI2Type(ev->any.type);
 | |
| 
 | |
|             assert(iclients);
 | |
|             assert(wOtherInputMasks(*win));
 | |
|             nt_list_for_each_entry(iclients,
 | |
|                                    wOtherInputMasks(*win)->inputClients, next)
 | |
|                 if (xi2mask_isset(iclients->xi2mask, dev, evtype))
 | |
|                 break;
 | |
| 
 | |
|             BUG_RETURN_VAL(!iclients, FALSE);
 | |
| 
 | |
|             *mask = iclients->xi2mask;
 | |
|             *client = dixClientForInputClients(iclients);
 | |
|         }
 | |
|         else if (listener->level == XI) {
 | |
|             int xi_type = GetXIType(TouchGetPointerEventType(ev));
 | |
|             Mask xi_filter = event_get_filter_from_type(dev, xi_type);
 | |
| 
 | |
|             assert(wOtherInputMasks(*win));
 | |
|             nt_list_for_each_entry(iclients,
 | |
|                                    wOtherInputMasks(*win)->inputClients, next)
 | |
|                 if (iclients->mask[dev->id] & xi_filter)
 | |
|                 break;
 | |
|             BUG_RETURN_VAL(!iclients, FALSE);
 | |
| 
 | |
|             *client = dixClientForInputClients(iclients);
 | |
|         }
 | |
|         else {
 | |
|             int coretype = GetCoreType(TouchGetPointerEventType(ev));
 | |
|             Mask core_filter = event_get_filter_from_type(dev, coretype);
 | |
|             OtherClients *oclients;
 | |
| 
 | |
|             /* all others */
 | |
|             nt_list_for_each_entry(oclients,
 | |
|                                    (OtherClients *) wOtherClients(*win), next)
 | |
|                 if (oclients->mask & core_filter)
 | |
|                     break;
 | |
| 
 | |
|             /* if owner selected, oclients is NULL */
 | |
|             *client = oclients ? dixClientForOtherClients(oclients) : dixClientForWindow(*win);
 | |
|         }
 | |
| 
 | |
|         *grab = NULL;
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static int
 | |
| DeliverTouchEmulatedEvent(DeviceIntPtr dev, TouchPointInfoPtr ti,
 | |
|                           InternalEvent *ev, TouchListener * listener,
 | |
|                           ClientPtr client, WindowPtr win, GrabPtr grab,
 | |
|                           XI2Mask *xi2mask)
 | |
| {
 | |
|     InternalEvent motion, button;
 | |
|     InternalEvent *ptrev = &motion;
 | |
|     int nevents;
 | |
|     DeviceIntPtr kbd;
 | |
| 
 | |
|     /* There may be a pointer grab on the device */
 | |
|     if (!grab) {
 | |
|         grab = dev->deviceGrab.grab;
 | |
|         if (grab) {
 | |
|             win = grab->window;
 | |
|             xi2mask = grab->xi2mask;
 | |
|             client = dixClientForGrab(grab);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* We don't deliver pointer events to non-owners */
 | |
|     if (!TouchResourceIsOwner(ti, listener->listener))
 | |
|         return !Success;
 | |
| 
 | |
|     if (!ti->emulate_pointer)
 | |
|         return !Success;
 | |
| 
 | |
|     nevents = TouchConvertToPointerEvent(ev, &motion, &button);
 | |
|     BUG_RETURN_VAL(nevents == 0, BadValue);
 | |
| 
 | |
|     /* Note that here we deliver only part of the events that are generated by the touch event:
 | |
|      *
 | |
|      * TouchBegin results in ButtonPress (motion is handled in DeliverEmulatedMotionEvent)
 | |
|      * TouchUpdate results in Motion
 | |
|      * TouchEnd results in ButtonRelease (motion is handled in DeliverEmulatedMotionEvent)
 | |
|      */
 | |
|     if (nevents > 1)
 | |
|         ptrev = &button;
 | |
| 
 | |
|     kbd = GetMaster(dev, KEYBOARD_OR_FLOAT);
 | |
|     event_set_state(dev, kbd, &ptrev->device_event);
 | |
|     ptrev->device_event.corestate = event_get_corestate(dev, kbd);
 | |
| 
 | |
|     if (grab) {
 | |
|         /* this side-steps the usual activation mechanisms, but... */
 | |
|         if (ev->any.type == ET_TouchBegin && !dev->deviceGrab.grab)
 | |
|             ActivatePassiveGrab(dev, grab, ptrev, ev);  /* also delivers the event */
 | |
|         else {
 | |
|             int deliveries = 0;
 | |
| 
 | |
|             /* 'grab' is the passive grab, but if the grab isn't active,
 | |
|              * don't deliver */
 | |
|             if (!dev->deviceGrab.grab)
 | |
|                 return !Success;
 | |
| 
 | |
|             if (grab->ownerEvents) {
 | |
|                 WindowPtr focus = NullWindow;
 | |
|                 WindowPtr sprite_win = DeepestSpriteWin(dev->spriteInfo->sprite);
 | |
| 
 | |
|                 deliveries = DeliverDeviceEvents(sprite_win, ptrev, grab, focus, dev);
 | |
|             }
 | |
| 
 | |
|             if (!deliveries)
 | |
|                 deliveries = DeliverOneGrabbedEvent(ptrev, dev, grab->grabtype);
 | |
| 
 | |
|             /* We must accept the touch sequence once a pointer listener has
 | |
|              * received one event past ButtonPress. */
 | |
|             if (deliveries && ev->any.type != ET_TouchBegin &&
 | |
|                 !(ev->device_event.flags & TOUCH_CLIENT_ID))
 | |
|                 TouchListenerAcceptReject(dev, ti, 0, XIAcceptTouch);
 | |
| 
 | |
|             if (ev->any.type == ET_TouchEnd &&
 | |
|                 ti->num_listeners == 1 &&
 | |
|                 !dev->button->buttonsDown &&
 | |
|                 dev->deviceGrab.fromPassiveGrab && GrabIsPointerGrab(grab)) {
 | |
|                 (*dev->deviceGrab.DeactivateGrab) (dev);
 | |
|                 CheckOldestTouch(dev);
 | |
|                 return Success;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         GrabPtr devgrab = dev->deviceGrab.grab;
 | |
|         WindowPtr sprite_win = DeepestSpriteWin(dev->spriteInfo->sprite);
 | |
| 
 | |
|         DeliverDeviceEvents(sprite_win, ptrev, grab, win, dev);
 | |
|         /* FIXME: bad hack
 | |
|          * Implicit passive grab activated in response to this event. Store
 | |
|          * the event.
 | |
|          */
 | |
|         if (!devgrab && dev->deviceGrab.grab && dev->deviceGrab.implicitGrab) {
 | |
|             TouchListener *l;
 | |
|             GrabPtr g;
 | |
| 
 | |
|             devgrab = dev->deviceGrab.grab;
 | |
|             g = AllocGrab(devgrab);
 | |
|             BUG_WARN(!g);
 | |
| 
 | |
|             CopyPartialInternalEvent(dev->deviceGrab.sync.event, ev);
 | |
| 
 | |
|             /* The listener array has a sequence of grabs and then one event
 | |
|              * selection. Implicit grab activation occurs through delivering an
 | |
|              * event selection. Thus, we update the last listener in the array.
 | |
|              */
 | |
|             l = &ti->listeners[ti->num_listeners - 1];
 | |
|             l->listener = g->resource;
 | |
|             l->grab = g;
 | |
|             //l->resource_type = X11_RESTYPE_NONE;
 | |
| 
 | |
|             if (devgrab->grabtype != XI2 || devgrab->type != XI_TouchBegin)
 | |
|                 l->type = TOUCH_LISTENER_POINTER_GRAB;
 | |
|             else
 | |
|                 l->type = TOUCH_LISTENER_GRAB;
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     if (ev->any.type == ET_TouchBegin)
 | |
|         listener->state = TOUCH_LISTENER_IS_OWNER;
 | |
|     else if (ev->any.type == ET_TouchEnd)
 | |
|         listener->state = TOUCH_LISTENER_HAS_END;
 | |
| 
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| static void
 | |
| DeliverEmulatedMotionEvent(DeviceIntPtr dev, TouchPointInfoPtr ti,
 | |
|                            InternalEvent *ev)
 | |
| {
 | |
|     DeviceEvent motion;
 | |
| 
 | |
|     if (ti->num_listeners) {
 | |
|         ClientPtr client;
 | |
|         WindowPtr win;
 | |
|         GrabPtr grab;
 | |
|         XI2Mask *mask;
 | |
| 
 | |
|         if (ti->listeners[0].type != TOUCH_LISTENER_POINTER_REGULAR &&
 | |
|             ti->listeners[0].type != TOUCH_LISTENER_POINTER_GRAB)
 | |
|             return;
 | |
| 
 | |
|         motion = ev->device_event;
 | |
|         motion.type = ET_TouchUpdate;
 | |
|         motion.detail.button = 0;
 | |
| 
 | |
|         if (!RetrieveTouchDeliveryData(dev, ti, (InternalEvent*)&motion,
 | |
|                                        &ti->listeners[0], &client, &win, &grab,
 | |
|                                        &mask))
 | |
|             return;
 | |
| 
 | |
|         DeliverTouchEmulatedEvent(dev, ti, (InternalEvent*)&motion, &ti->listeners[0], client,
 | |
|                                   win, grab, mask);
 | |
|     }
 | |
|     else {
 | |
|         InternalEvent button;
 | |
|         int converted;
 | |
| 
 | |
|         converted = TouchConvertToPointerEvent(ev, (InternalEvent*)&motion, &button);
 | |
| 
 | |
|         BUG_WARN(converted == 0);
 | |
|         if (converted)
 | |
|             ProcessOtherEvent((InternalEvent*)&motion, dev);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Processes and delivers a TouchBegin, TouchUpdate, or a
 | |
|  * TouchEnd event.
 | |
|  *
 | |
|  * Due to having rather different delivery semantics (see the Xi 2.2 protocol
 | |
|  * spec for more information), this implements its own grab and event-selection
 | |
|  * delivery logic.
 | |
|  */
 | |
| static void
 | |
| ProcessTouchEvent(InternalEvent *ev, DeviceIntPtr dev)
 | |
| {
 | |
|     TouchClassPtr t = dev->touch;
 | |
|     TouchPointInfoPtr ti;
 | |
|     uint32_t touchid;
 | |
|     int type = ev->any.type;
 | |
|     int emulate_pointer = ! !(ev->device_event.flags & TOUCH_POINTER_EMULATED);
 | |
|     DeviceIntPtr kbd;
 | |
| 
 | |
|     if (!t)
 | |
|         return;
 | |
| 
 | |
|     touchid = ev->device_event.touchid;
 | |
| 
 | |
|     if (type == ET_TouchBegin && !(ev->device_event.flags & TOUCH_REPLAYING)) {
 | |
|         ti = TouchBeginTouch(dev, ev->device_event.sourceid, touchid,
 | |
|                              emulate_pointer);
 | |
|     }
 | |
|     else
 | |
|         ti = TouchFindByClientID(dev, touchid);
 | |
| 
 | |
|     /* Active pointer grab */
 | |
|     if (emulate_pointer && dev->deviceGrab.grab && !dev->deviceGrab.fromPassiveGrab &&
 | |
|         (dev->deviceGrab.grab->grabtype == CORE ||
 | |
|          dev->deviceGrab.grab->grabtype == XI ||
 | |
|          !xi2mask_isset(dev->deviceGrab.grab->xi2mask, dev, XI_TouchBegin)))
 | |
|     {
 | |
|         /* Active pointer grab on touch point and we get a TouchEnd - claim this
 | |
|          * touchpoint accepted, otherwise clients waiting for ownership will
 | |
|          * wait on this touchpoint until this client ungrabs, or the cows come
 | |
|          * home, whichever is earlier */
 | |
|         if (ti && type == ET_TouchEnd)
 | |
|             TouchListenerAcceptReject(dev, ti, 0, XIAcceptTouch);
 | |
|         else if (!ti && type != ET_TouchBegin) {
 | |
|             /* Under the following circumstances we create a new touch record for an
 | |
|              * existing touch:
 | |
|              *
 | |
|              * - The touch may be pointer emulated
 | |
|              * - An explicit grab is active on the device
 | |
|              * - The grab is a pointer grab
 | |
|              *
 | |
|              * This allows for an explicit grab to receive pointer events for an already
 | |
|              * active touch.
 | |
|              */
 | |
|             ti = TouchBeginTouch(dev, ev->device_event.sourceid, touchid,
 | |
|                                  emulate_pointer);
 | |
|             if (!ti) {
 | |
|                 DebugF("[Xi] %s: Failed to create new dix record for explicitly "
 | |
|                        "grabbed touchpoint %d\n",
 | |
|                        dev->name, touchid);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             TouchBuildSprite(dev, ti, ev);
 | |
|             TouchSetupListeners(dev, ti, ev);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!ti) {
 | |
|         DebugF("[Xi] %s: Failed to get event %d for touchpoint %d\n",
 | |
|                dev->name, type, touchid);
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     /* if emulate_pointer is set, emulate the motion event right
 | |
|      * here, so we can ignore it for button event emulation. TouchUpdate
 | |
|      * events which _only_ emulate motion just work normally */
 | |
|     if (emulate_pointer && (ev->any.type == ET_TouchBegin ||
 | |
|                            (ev->any.type == ET_TouchEnd && ti->num_listeners > 0)))
 | |
|         DeliverEmulatedMotionEvent(dev, ti, ev);
 | |
| 
 | |
|     if (emulate_pointer && InputDevIsMaster(dev))
 | |
|         CheckMotion(&ev->device_event, dev);
 | |
| 
 | |
|     kbd = GetMaster(dev, KEYBOARD_OR_FLOAT);
 | |
|     event_set_state(NULL, kbd, &ev->device_event);
 | |
|     ev->device_event.corestate = event_get_corestate(NULL, kbd);
 | |
| 
 | |
|     /* Make sure we have a valid window trace for event delivery; must be
 | |
|      * called after event type mutation. Touch end events are always processed
 | |
|      * in order to end touch records. */
 | |
|     /* FIXME: check this */
 | |
|     if ((type == ET_TouchBegin &&
 | |
|          !(ev->device_event.flags & TOUCH_REPLAYING) &&
 | |
|          !TouchBuildSprite(dev, ti, ev)) ||
 | |
|         (type != ET_TouchEnd && ti->sprite.spriteTraceGood == 0))
 | |
|         return;
 | |
| 
 | |
|     TouchCopyValuatorData(&ev->device_event, ti);
 | |
|     /* WARNING: the event type may change to TouchUpdate in
 | |
|      * DeliverTouchEvents if a TouchEnd was delivered to a grabbing
 | |
|      * owner */
 | |
|     DeliverTouchEvents(dev, ti, ev, ev->device_event.resource);
 | |
|     if (ev->any.type == ET_TouchEnd)
 | |
|         TouchEndTouch(dev, ti);
 | |
| 
 | |
|  out:
 | |
|     if (emulate_pointer)
 | |
|         UpdateDeviceState(dev, &ev->device_event);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ProcessBarrierEvent(InternalEvent *e, DeviceIntPtr dev)
 | |
| {
 | |
|     Mask filter;
 | |
|     WindowPtr pWin;
 | |
|     BarrierEvent *be = &e->barrier_event;
 | |
|     xEvent *ev;
 | |
|     int rc;
 | |
|     GrabPtr grab = dev->deviceGrab.grab;
 | |
| 
 | |
|     if (!InputDevIsMaster(dev))
 | |
|         return;
 | |
| 
 | |
|     if (dixLookupWindow(&pWin, be->window, serverClient, DixReadAccess) != Success)
 | |
|         return;
 | |
| 
 | |
|     if (grab)
 | |
|         be->flags |= XIBarrierDeviceIsGrabbed;
 | |
| 
 | |
|     rc = EventToXI2(e, &ev);
 | |
|     if (rc != Success) {
 | |
|         ErrorF("[Xi] event conversion from %s failed with code %d\n", __func__, rc);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     /* A client has a grab, deliver to this client if the grab_window is the
 | |
|        barrier window.
 | |
| 
 | |
|        Otherwise, deliver normally to the client.
 | |
|      */
 | |
|     if (grab &&
 | |
|         dixClientIdForXID(be->barrierid) == dixClientIdForXID(grab->resource) &&
 | |
|         grab->window->drawable.id == be->window) {
 | |
|         DeliverGrabbedEvent(e, dev, FALSE);
 | |
|     } else {
 | |
|         filter = GetEventFilter(dev, ev);
 | |
| 
 | |
|         DeliverEventsToWindow(dev, pWin, ev, 1,
 | |
|                               filter, NullGrab);
 | |
|     }
 | |
|     free(ev);
 | |
| }
 | |
| 
 | |
| static BOOL
 | |
| IsAnotherGestureActiveOnMaster(DeviceIntPtr dev, InternalEvent* ev)
 | |
| {
 | |
|     GestureClassPtr g = dev->gesture;
 | |
|     if (g->gesture.active && g->gesture.sourceid != ev->gesture_event.sourceid) {
 | |
|         return TRUE;
 | |
|     }
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Processes and delivers a Gesture{Pinch,Swipe}{Begin,Update,End}.
 | |
|  *
 | |
|  * Due to having rather different delivery semantics (see the Xi 2.4 protocol
 | |
|  * spec for more information), this implements its own grab and event-selection
 | |
|  * delivery logic.
 | |
|  */
 | |
| void
 | |
| ProcessGestureEvent(InternalEvent *ev, DeviceIntPtr dev)
 | |
| {
 | |
|     GestureInfoPtr gi;
 | |
|     DeviceIntPtr kbd;
 | |
|     Bool deactivateGestureGrab = FALSE;
 | |
|     Bool delivered = FALSE;
 | |
| 
 | |
|     if (!dev->gesture)
 | |
|         return;
 | |
| 
 | |
|     if (InputDevIsMaster(dev) && IsAnotherGestureActiveOnMaster(dev, ev))
 | |
|         return;
 | |
| 
 | |
|     if (IsGestureBeginEvent(ev))
 | |
|         gi = GestureBeginGesture(dev, ev);
 | |
|     else
 | |
|         gi = GestureFindActiveByEventType(dev, ev->any.type);
 | |
| 
 | |
|     if (!gi) {
 | |
|         /* This may happen if gesture is no longer active or was never started. */
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     kbd = GetMaster(dev, KEYBOARD_OR_FLOAT);
 | |
|     event_set_state_gesture(kbd, &ev->gesture_event);
 | |
| 
 | |
|     if (IsGestureBeginEvent(ev))
 | |
|         GestureSetupListener(dev, gi, ev);
 | |
| 
 | |
|     if (IsGestureEndEvent(ev) &&
 | |
|             dev->deviceGrab.grab &&
 | |
|             dev->deviceGrab.fromPassiveGrab &&
 | |
|             GrabIsGestureGrab(dev->deviceGrab.grab))
 | |
|         deactivateGestureGrab = TRUE;
 | |
| 
 | |
|     delivered = DeliverGestureEventToOwner(dev, gi, ev);
 | |
| 
 | |
|     if (delivered && !deactivateGestureGrab &&
 | |
|             (IsGestureBeginEvent(ev) || IsGestureEndEvent(ev)))
 | |
|         FreezeThisEventIfNeededForSyncGrab(dev, ev);
 | |
| 
 | |
|     if (IsGestureEndEvent(ev))
 | |
|         GestureEndGesture(gi);
 | |
| 
 | |
|     if (deactivateGestureGrab)
 | |
|         (*dev->deviceGrab.DeactivateGrab) (dev);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Process DeviceEvents and DeviceChangedEvents.
 | |
|  */
 | |
| static void
 | |
| ProcessDeviceEvent(InternalEvent *ev, DeviceIntPtr device)
 | |
| {
 | |
|     GrabPtr grab;
 | |
|     Bool deactivateDeviceGrab = FALSE;
 | |
|     int key = 0, rootX, rootY;
 | |
|     ButtonClassPtr b;
 | |
|     int ret = 0;
 | |
|     int corestate;
 | |
|     DeviceIntPtr mouse = NULL, kbd = NULL;
 | |
|     DeviceEvent *event = &ev->device_event;
 | |
| 
 | |
|     if (IsPointerDevice(device)) {
 | |
|         kbd = GetMaster(device, KEYBOARD_OR_FLOAT);
 | |
|         mouse = device;
 | |
|         if (!kbd->key)          /* can happen with floating SDs */
 | |
|             kbd = NULL;
 | |
|     }
 | |
|     else {
 | |
|         mouse = GetMaster(device, POINTER_OR_FLOAT);
 | |
|         kbd = device;
 | |
|         if (!mouse->valuator || !mouse->button) /* may be float. SDs */
 | |
|             mouse = NULL;
 | |
|     }
 | |
| 
 | |
|     corestate = event_get_corestate(mouse, kbd);
 | |
|     event_set_state(mouse, kbd, event);
 | |
| 
 | |
|     ret = UpdateDeviceState(device, event);
 | |
|     if (ret == DONT_PROCESS)
 | |
|         return;
 | |
| 
 | |
|     b = device->button;
 | |
| 
 | |
|     if (InputDevIsMaster(device) || InputDevIsFloating(device))
 | |
|         CheckMotion(event, device);
 | |
| 
 | |
|     switch (event->type) {
 | |
|     case ET_Motion:
 | |
|     case ET_ButtonPress:
 | |
|     case ET_ButtonRelease:
 | |
|     case ET_KeyPress:
 | |
|     case ET_KeyRelease:
 | |
|     case ET_ProximityIn:
 | |
|     case ET_ProximityOut:
 | |
|         GetSpritePosition(device, &rootX, &rootY);
 | |
|         event->root_x = rootX;
 | |
|         event->root_y = rootY;
 | |
|         NoticeEventTime((InternalEvent *) event, device);
 | |
|         event->corestate = corestate;
 | |
|         key = event->detail.key;
 | |
|         break;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     /* send KeyPress and KeyRelease events to XACE plugins */
 | |
|     if (XaceHookIsSet(XACE_KEY_AVAIL) &&
 | |
|             (event->type == ET_KeyPress || event->type == ET_KeyRelease)) {
 | |
|         xEvent *core;
 | |
|         int count;
 | |
| 
 | |
|         if (EventToCore(ev, &core, &count) == Success && count > 0) {
 | |
|             XaceHookKeyAvail(core, device, 0);
 | |
|             free(core);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (DeviceEventCallback && !syncEvents.playingEvents) {
 | |
|         DeviceEventInfoRec eventinfo;
 | |
|         SpritePtr pSprite = device->spriteInfo->sprite;
 | |
| 
 | |
|         /* see comment in EnqueueEvents regarding the next three lines */
 | |
|         if (ev->any.type == ET_Motion)
 | |
|             ev->device_event.root = pSprite->hotPhys.pScreen->root->drawable.id;
 | |
| 
 | |
|         eventinfo.device = device;
 | |
|         eventinfo.event = ev;
 | |
|         CallCallbacks(&DeviceEventCallback, (void *) &eventinfo);
 | |
|     }
 | |
| 
 | |
|     grab = device->deviceGrab.grab;
 | |
| 
 | |
|     switch (event->type) {
 | |
|     case ET_KeyPress:
 | |
|         /* Don't deliver focus events (e.g. from KeymapNotify when running
 | |
|          * nested) to clients. */
 | |
|         if (event->source_type == EVENT_SOURCE_FOCUS)
 | |
|             return;
 | |
|         if (!grab && CheckDeviceGrabs(device, ev, 0))
 | |
|             return;
 | |
|         break;
 | |
|     case ET_KeyRelease:
 | |
|         if (grab && device->deviceGrab.fromPassiveGrab &&
 | |
|             (key == device->deviceGrab.activatingKey) &&
 | |
|             GrabIsKeyboardGrab(device->deviceGrab.grab))
 | |
|             deactivateDeviceGrab = TRUE;
 | |
|         break;
 | |
|     case ET_ButtonPress:
 | |
|         if (b->map[key] == 0)   /* there's no button 0 */
 | |
|             return;
 | |
|         event->detail.button = b->map[key];
 | |
|         if (!grab && CheckDeviceGrabs(device, ev, 0)) {
 | |
|             /* if a passive grab was activated, the event has been sent
 | |
|              * already */
 | |
|             return;
 | |
|         }
 | |
|         break;
 | |
|     case ET_ButtonRelease:
 | |
|         if (b->map[key] == 0)   /* there's no button 0 */
 | |
|             return;
 | |
|         event->detail.button = b->map[key];
 | |
|         if (grab && !b->buttonsDown &&
 | |
|             device->deviceGrab.fromPassiveGrab &&
 | |
|             GrabIsPointerGrab(device->deviceGrab.grab))
 | |
|             deactivateDeviceGrab = TRUE;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     /* Don't deliver focus events (e.g. from KeymapNotify when running
 | |
|      * nested) to clients. */
 | |
|     if (event->source_type != EVENT_SOURCE_FOCUS) {
 | |
|         if (grab)
 | |
|             DeliverGrabbedEvent((InternalEvent *) event, device,
 | |
|                                 deactivateDeviceGrab);
 | |
|         else if (device->focus && !IsPointerEvent(ev))
 | |
|             DeliverFocusedEvent(device, (InternalEvent *) event,
 | |
|                                 InputDevSpriteWindow(device));
 | |
|         else
 | |
|             DeliverDeviceEvents(InputDevSpriteWindow(device), (InternalEvent *) event,
 | |
|                                 NullGrab, NullWindow, device);
 | |
|     }
 | |
| 
 | |
|     if (deactivateDeviceGrab == TRUE) {
 | |
|         (*device->deviceGrab.DeactivateGrab) (device);
 | |
| 
 | |
|         if (!InputDevIsMaster (device) && !InputDevIsFloating (device)) {
 | |
|             int flags, num_events = 0;
 | |
|             InternalEvent dce;
 | |
| 
 | |
|             flags = (IsPointerDevice (device)) ?
 | |
|                 DEVCHANGE_POINTER_EVENT : DEVCHANGE_KEYBOARD_EVENT;
 | |
|             UpdateFromMaster (&dce, device, flags, &num_events);
 | |
|             BUG_WARN(num_events > 1);
 | |
| 
 | |
|             if (num_events == 1)
 | |
|                 ChangeMasterDeviceClasses(GetMaster (device, MASTER_ATTACHED),
 | |
|                                           &dce.changed_event);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     event->detail.key = key;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Main device event processing function.
 | |
|  * Called from when processing the events from the event queue.
 | |
|  *
 | |
|  */
 | |
| void
 | |
| ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
 | |
| {
 | |
|     verify_internal_event(ev);
 | |
| 
 | |
|     switch (ev->any.type) {
 | |
|     case ET_RawKeyPress:
 | |
|     case ET_RawKeyRelease:
 | |
|     case ET_RawButtonPress:
 | |
|     case ET_RawButtonRelease:
 | |
|     case ET_RawMotion:
 | |
|     case ET_RawTouchBegin:
 | |
|     case ET_RawTouchUpdate:
 | |
|     case ET_RawTouchEnd:
 | |
|         DeliverRawEvent(&ev->raw_event, device);
 | |
|         break;
 | |
|     case ET_TouchBegin:
 | |
|     case ET_TouchUpdate:
 | |
|     case ET_TouchEnd:
 | |
|         ProcessTouchEvent(ev, device);
 | |
|         break;
 | |
|     case ET_TouchOwnership:
 | |
|         /* TouchOwnership events are handled separately from the rest, as they
 | |
|          * have more complex semantics. */
 | |
|         ProcessTouchOwnershipEvent(&ev->touch_ownership_event, device);
 | |
|         break;
 | |
|     case ET_BarrierHit:
 | |
|     case ET_BarrierLeave:
 | |
|         ProcessBarrierEvent(ev, device);
 | |
|         break;
 | |
|     case ET_GesturePinchBegin:
 | |
|     case ET_GesturePinchUpdate:
 | |
|     case ET_GesturePinchEnd:
 | |
|     case ET_GestureSwipeBegin:
 | |
|     case ET_GestureSwipeUpdate:
 | |
|     case ET_GestureSwipeEnd:
 | |
|         ProcessGestureEvent(ev, device);
 | |
|         break;
 | |
|     default:
 | |
|         ProcessDeviceEvent(ev, device);
 | |
|         break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int
 | |
| DeliverTouchBeginEvent(DeviceIntPtr dev, TouchPointInfoPtr ti,
 | |
|                        InternalEvent *ev, TouchListener * listener,
 | |
|                        ClientPtr client, WindowPtr win, GrabPtr grab,
 | |
|                        XI2Mask *xi2mask)
 | |
| {
 | |
|     enum TouchListenerState state;
 | |
|     int rc = Success;
 | |
|     Bool has_ownershipmask;
 | |
| 
 | |
|     if (listener->type == TOUCH_LISTENER_POINTER_REGULAR ||
 | |
|         listener->type == TOUCH_LISTENER_POINTER_GRAB) {
 | |
|         rc = DeliverTouchEmulatedEvent(dev, ti, ev, listener, client, win,
 | |
|                                        grab, xi2mask);
 | |
|         if (rc == Success) {
 | |
|             listener->state = TOUCH_LISTENER_IS_OWNER;
 | |
|             /* async grabs cannot replay, so automatically accept this touch */
 | |
|             if (listener->type == TOUCH_LISTENER_POINTER_GRAB &&
 | |
|                 dev->deviceGrab.grab &&
 | |
|                 dev->deviceGrab.fromPassiveGrab &&
 | |
|                 dev->deviceGrab.grab->pointerMode == GrabModeAsync)
 | |
|                 ActivateEarlyAccept(dev, ti);
 | |
|         }
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     has_ownershipmask = xi2mask_isset(xi2mask, dev, XI_TouchOwnership);
 | |
| 
 | |
|     if (TouchResourceIsOwner(ti, listener->listener) || has_ownershipmask)
 | |
|         rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
 | |
|     if (!TouchResourceIsOwner(ti, listener->listener)) {
 | |
|         if (has_ownershipmask)
 | |
|             state = TOUCH_LISTENER_AWAITING_OWNER;
 | |
|         else
 | |
|             state = TOUCH_LISTENER_AWAITING_BEGIN;
 | |
|     }
 | |
|     else {
 | |
|         if (has_ownershipmask)
 | |
|             TouchSendOwnershipEvent(dev, ti, 0, listener->listener);
 | |
| 
 | |
|         if (listener->type == TOUCH_LISTENER_REGULAR)
 | |
|             state = TOUCH_LISTENER_HAS_ACCEPTED;
 | |
|         else
 | |
|             state = TOUCH_LISTENER_IS_OWNER;
 | |
|     }
 | |
|     listener->state = state;
 | |
| 
 | |
|  out:
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| static int
 | |
| DeliverTouchEndEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev,
 | |
|                      TouchListener * listener, ClientPtr client,
 | |
|                      WindowPtr win, GrabPtr grab, XI2Mask *xi2mask)
 | |
| {
 | |
|     int rc = Success;
 | |
| 
 | |
|     if (listener->type == TOUCH_LISTENER_POINTER_REGULAR ||
 | |
|         listener->type == TOUCH_LISTENER_POINTER_GRAB) {
 | |
|         /* Note: If the active grab was ungrabbed, we already changed the
 | |
|          * state to TOUCH_LISTENER_HAS_END but still get here. So we mustn't
 | |
|          * actually send the event.
 | |
|          * This is part two of the hack in DeactivatePointerGrab
 | |
|          */
 | |
|         if (listener->state != TOUCH_LISTENER_HAS_END) {
 | |
|             rc = DeliverTouchEmulatedEvent(dev, ti, ev, listener, client, win,
 | |
|                                            grab, xi2mask);
 | |
| 
 | |
|              /* Once we send a TouchEnd to a legacy listener, we're already well
 | |
|               * past the accepting/rejecting stage (can only happen on
 | |
|               * GrabModeSync + replay. This listener now gets the end event,
 | |
|               * and we can continue.
 | |
|               */
 | |
|             if (rc == Success)
 | |
|                 listener->state = TOUCH_LISTENER_HAS_END;
 | |
|         }
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     /* A client is waiting for the begin, don't give it a TouchEnd */
 | |
|     if (listener->state == TOUCH_LISTENER_AWAITING_BEGIN) {
 | |
|         listener->state = TOUCH_LISTENER_HAS_END;
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     /* Event in response to reject */
 | |
|     if (ev->device_event.flags & TOUCH_REJECT ||
 | |
|         (ev->device_event.flags & TOUCH_ACCEPT && !TouchResourceIsOwner(ti, listener->listener))) {
 | |
|         /* Touch has been rejected, or accepted by its owner which is not this listener */
 | |
|         if (listener->state != TOUCH_LISTENER_HAS_END)
 | |
|             rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
 | |
|         listener->state = TOUCH_LISTENER_HAS_END;
 | |
|     }
 | |
|     else if (TouchResourceIsOwner(ti, listener->listener)) {
 | |
|         Bool normal_end = !(ev->device_event.flags & TOUCH_ACCEPT);
 | |
| 
 | |
|         /* FIXME: what about early acceptance */
 | |
|         if (normal_end && listener->state != TOUCH_LISTENER_HAS_END)
 | |
|             rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
 | |
| 
 | |
|         if ((ti->num_listeners > 1 ||
 | |
|              (ti->num_grabs > 0 && listener->state != TOUCH_LISTENER_HAS_ACCEPTED)) &&
 | |
|             (ev->device_event.flags & (TOUCH_ACCEPT | TOUCH_REJECT)) == 0) {
 | |
|             ev->any.type = ET_TouchUpdate;
 | |
|             ev->device_event.flags |= TOUCH_PENDING_END;
 | |
|             ti->pending_finish = TRUE;
 | |
|         }
 | |
| 
 | |
|         if (normal_end)
 | |
|             listener->state = TOUCH_LISTENER_HAS_END;
 | |
|     }
 | |
| 
 | |
|  out:
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| static int
 | |
| DeliverTouchEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev,
 | |
|                   TouchListener * listener, ClientPtr client,
 | |
|                   WindowPtr win, GrabPtr grab, XI2Mask *xi2mask)
 | |
| {
 | |
|     Bool has_ownershipmask = FALSE;
 | |
|     int rc = Success;
 | |
| 
 | |
|     if (xi2mask)
 | |
|         has_ownershipmask = xi2mask_isset(xi2mask, dev, XI_TouchOwnership);
 | |
| 
 | |
|     if (ev->any.type == ET_TouchOwnership) {
 | |
|         ev->touch_ownership_event.deviceid = dev->id;
 | |
|         if (!TouchResourceIsOwner(ti, listener->listener))
 | |
|             goto out;
 | |
|         rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
 | |
|         listener->state = TOUCH_LISTENER_IS_OWNER;
 | |
|     }
 | |
|     else
 | |
|         ev->device_event.deviceid = dev->id;
 | |
| 
 | |
|     if (ev->any.type == ET_TouchBegin) {
 | |
|         rc = DeliverTouchBeginEvent(dev, ti, ev, listener, client, win, grab,
 | |
|                                     xi2mask);
 | |
|     }
 | |
|     else if (ev->any.type == ET_TouchUpdate) {
 | |
|         if (listener->type == TOUCH_LISTENER_POINTER_REGULAR ||
 | |
|             listener->type == TOUCH_LISTENER_POINTER_GRAB)
 | |
|             DeliverTouchEmulatedEvent(dev, ti, ev, listener, client, win, grab,
 | |
|                                       xi2mask);
 | |
|         else if (TouchResourceIsOwner(ti, listener->listener) ||
 | |
|                  has_ownershipmask)
 | |
|             rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
 | |
|     }
 | |
|     else if (ev->any.type == ET_TouchEnd)
 | |
|         rc = DeliverTouchEndEvent(dev, ti, ev, listener, client, win, grab,
 | |
|                                   xi2mask);
 | |
| 
 | |
|  out:
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Delivers a touch events to all interested clients.  For TouchBegin events,
 | |
|  * will update ti->listeners, ti->num_listeners, and ti->num_grabs.
 | |
|  * May also mutate ev (type and flags) upon successful delivery.  If
 | |
|  * @resource is non-zero, will only attempt delivery to the owner of that
 | |
|  * resource.
 | |
|  *
 | |
|  * @return TRUE if the event was delivered at least once, FALSE otherwise
 | |
|  */
 | |
| void
 | |
| DeliverTouchEvents(DeviceIntPtr dev, TouchPointInfoPtr ti,
 | |
|                    InternalEvent *ev, XID resource)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     if (ev->any.type == ET_TouchBegin &&
 | |
|         !(ev->device_event.flags & (TOUCH_CLIENT_ID | TOUCH_REPLAYING)))
 | |
|         TouchSetupListeners(dev, ti, ev);
 | |
| 
 | |
|     TouchEventHistoryPush(ti, &ev->device_event);
 | |
| 
 | |
|     for (i = 0; i < ti->num_listeners; i++) {
 | |
|         GrabPtr grab = NULL;
 | |
|         ClientPtr client;
 | |
|         WindowPtr win;
 | |
|         XI2Mask *mask;
 | |
|         TouchListener *listener = &ti->listeners[i];
 | |
| 
 | |
|         if (resource && listener->listener != resource)
 | |
|             continue;
 | |
| 
 | |
|         if (!RetrieveTouchDeliveryData(dev, ti, ev, listener, &client, &win,
 | |
|                                        &grab, &mask))
 | |
|             continue;
 | |
| 
 | |
|         DeliverTouchEvent(dev, ti, ev, listener, client, win, grab, mask);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Attempts to deliver a gesture event to the given client.
 | |
|  */
 | |
| static Bool
 | |
| DeliverOneGestureEvent(ClientPtr client, DeviceIntPtr dev, GestureInfoPtr gi,
 | |
|                        GrabPtr grab, WindowPtr win, InternalEvent *ev)
 | |
| {
 | |
|     int err;
 | |
|     xEvent *xi2;
 | |
|     Mask filter;
 | |
|     Window child = DeepestSpriteWin(&gi->sprite)->drawable.id;
 | |
| 
 | |
|     /* If we fail here, we're going to leave a client hanging. */
 | |
|     err = EventToXI2(ev, &xi2);
 | |
|     if (err != Success)
 | |
|         FatalError("[Xi] %s: XI2 conversion failed in %s"
 | |
|                    " (%d)\n", dev->name, __func__, err);
 | |
| 
 | |
|     FixUpEventFromWindow(&gi->sprite, xi2, win, child, FALSE);
 | |
|     filter = GetEventFilter(dev, xi2);
 | |
|     if (XaceHookReceiveAccess(client, win, xi2, 1) != Success)
 | |
|         return FALSE;
 | |
|     TryClientEvents(client, dev, xi2, 1, filter, filter, NullGrab);
 | |
|     free(xi2);
 | |
| 
 | |
|     /* Returning the value from TryClientEvents isn't useful, since all our
 | |
|      * resource-gone cleanups will update the delivery list anyway. */
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Given a gesture event and a potential listener, retrieve info needed for processing the event.
 | |
|  *
 | |
|  * @param dev The device generating the gesture event.
 | |
|  * @param ev The gesture event to process.
 | |
|  * @param listener The gesture event listener that may receive the gesture event.
 | |
|  * @param[out] client The client that should receive the gesture event.
 | |
|  * @param[out] win The window to deliver the event on.
 | |
|  * @param[out] grab The grab to deliver the event through, if any.
 | |
|  * @return TRUE if an event should be delivered to the listener, FALSE
 | |
|  *         otherwise.
 | |
|  */
 | |
| static Bool
 | |
| RetrieveGestureDeliveryData(DeviceIntPtr dev, InternalEvent *ev, GestureListener* listener,
 | |
|                             ClientPtr *client, WindowPtr *win, GrabPtr *grab)
 | |
| {
 | |
|     int rc;
 | |
|     int evtype;
 | |
|     InputClients *iclients = NULL;
 | |
|     *grab = NULL;
 | |
| 
 | |
|     if (listener->type == GESTURE_LISTENER_GRAB ||
 | |
|         listener->type == GESTURE_LISTENER_NONGESTURE_GRAB) {
 | |
|         *grab = listener->grab;
 | |
| 
 | |
|         BUG_RETURN_VAL(!*grab, FALSE);
 | |
| 
 | |
|         *client = dixClientForGrab(*grab);
 | |
|         *win = (*grab)->window;
 | |
|     }
 | |
|     else {
 | |
|         rc = dixLookupResourceByType((void **) win, listener->listener, listener->resource_type,
 | |
|                                      serverClient, DixSendAccess);
 | |
|         if (rc != Success)
 | |
|             return FALSE;
 | |
| 
 | |
|         /* note that we only will have XI2 listeners as
 | |
|            listener->type == GESTURE_LISTENER_REGULAR */
 | |
|         evtype = GetXI2Type(ev->any.type);
 | |
| 
 | |
|         assert(wOtherInputMasks(*win));
 | |
|         nt_list_for_each_entry(iclients, wOtherInputMasks(*win)->inputClients, next)
 | |
|             if (xi2mask_isset(iclients->xi2mask, dev, evtype))
 | |
|                 break;
 | |
| 
 | |
|         BUG_RETURN_VAL(!iclients, FALSE);
 | |
| 
 | |
|         *client = dixClientForInputClients(iclients);
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Delivers a gesture to the owner, if possible and needed. Returns whether
 | |
|  * an event was delivered.
 | |
|  */
 | |
| Bool
 | |
| DeliverGestureEventToOwner(DeviceIntPtr dev, GestureInfoPtr gi, InternalEvent *ev)
 | |
| {
 | |
|     GrabPtr grab = NULL;
 | |
|     ClientPtr client;
 | |
|     WindowPtr win;
 | |
| 
 | |
|     if (!gi->has_listener || gi->listener.type == GESTURE_LISTENER_NONGESTURE_GRAB) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     if (!RetrieveGestureDeliveryData(dev, ev, &gi->listener, &client, &win, &grab))
 | |
|         return 0;
 | |
| 
 | |
|     ev->gesture_event.deviceid = dev->id;
 | |
| 
 | |
|     return DeliverOneGestureEvent(client, dev, gi, grab, win, ev);
 | |
| }
 | |
| 
 | |
| int
 | |
| InitProximityClassDeviceStruct(DeviceIntPtr dev)
 | |
| {
 | |
|     BUG_RETURN_VAL(dev == NULL, FALSE);
 | |
|     BUG_RETURN_VAL(dev->proximity != NULL, FALSE);
 | |
| 
 | |
|     ProximityClassPtr proxc = calloc(1, sizeof(ProximityClassRec));
 | |
|     if (!proxc)
 | |
|         return FALSE;
 | |
|     proxc->sourceid = dev->id;
 | |
|     proxc->in_proximity = TRUE;
 | |
|     dev->proximity = proxc;
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Initialise the device's valuators. The memory must already be allocated,
 | |
|  * this function merely inits the matching axis (specified through axnum) to
 | |
|  * sane values.
 | |
|  *
 | |
|  * It is a condition that (minval < maxval).
 | |
|  *
 | |
|  * @see InitValuatorClassDeviceStruct
 | |
|  */
 | |
| Bool
 | |
| InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
 | |
|                        int maxval, int resolution, int min_res, int max_res,
 | |
|                        int mode)
 | |
| {
 | |
|     AxisInfoPtr ax;
 | |
| 
 | |
|     BUG_RETURN_VAL(dev == NULL, FALSE);
 | |
|     BUG_RETURN_VAL(dev->valuator == NULL, FALSE);
 | |
|     BUG_RETURN_VAL(axnum >= dev->valuator->numAxes, FALSE);
 | |
|     BUG_RETURN_VAL(minval > maxval && mode == Absolute, FALSE);
 | |
| 
 | |
|     ax = dev->valuator->axes + axnum;
 | |
| 
 | |
|     ax->min_value = minval;
 | |
|     ax->max_value = maxval;
 | |
|     ax->resolution = resolution;
 | |
|     ax->min_resolution = min_res;
 | |
|     ax->max_resolution = max_res;
 | |
|     ax->label = label;
 | |
|     ax->mode = mode;
 | |
| 
 | |
|     if (mode & OutOfProximity)
 | |
|         dev->proximity->in_proximity = FALSE;
 | |
| 
 | |
|     return SetScrollValuator(dev, axnum, SCROLL_TYPE_NONE, 0, SCROLL_FLAG_NONE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the given axis number as a scrolling valuator.
 | |
|  */
 | |
| Bool
 | |
| SetScrollValuator(DeviceIntPtr dev, int axnum, enum ScrollType type,
 | |
|                   double increment, int flags)
 | |
| {
 | |
|     AxisInfoPtr ax;
 | |
|     int *current_ax;
 | |
|     InternalEvent dce;
 | |
|     DeviceIntPtr master;
 | |
| 
 | |
|     BUG_RETURN_VAL(dev == NULL, FALSE);
 | |
|     BUG_RETURN_VAL(dev->valuator == NULL, FALSE);
 | |
|     BUG_RETURN_VAL(axnum >= dev->valuator->numAxes, FALSE);
 | |
| 
 | |
|     switch (type) {
 | |
|     case SCROLL_TYPE_VERTICAL:
 | |
|         current_ax = &dev->valuator->v_scroll_axis;
 | |
|         break;
 | |
|     case SCROLL_TYPE_HORIZONTAL:
 | |
|         current_ax = &dev->valuator->h_scroll_axis;
 | |
|         break;
 | |
|     case SCROLL_TYPE_NONE:
 | |
|         ax = &dev->valuator->axes[axnum];
 | |
|         ax->scroll.type = type;
 | |
|         return TRUE;
 | |
|     default:
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (increment == 0.0)
 | |
|         return FALSE;
 | |
| 
 | |
|     if (*current_ax != -1 && axnum != *current_ax) {
 | |
|         ax = &dev->valuator->axes[*current_ax];
 | |
|         if (ax->scroll.type == type &&
 | |
|             (flags & SCROLL_FLAG_PREFERRED) &&
 | |
|             (ax->scroll.flags & SCROLL_FLAG_PREFERRED))
 | |
|             return FALSE;
 | |
|     }
 | |
|     *current_ax = axnum;
 | |
| 
 | |
|     ax = &dev->valuator->axes[axnum];
 | |
|     ax->scroll.type = type;
 | |
|     ax->scroll.increment = increment;
 | |
|     ax->scroll.flags = flags;
 | |
| 
 | |
|     master = GetMaster(dev, MASTER_ATTACHED);
 | |
|     CreateClassesChangedEvent(&dce, master, dev,
 | |
|                               DEVCHANGE_POINTER_EVENT |
 | |
|                               DEVCHANGE_DEVICE_CHANGE);
 | |
|     XISendDeviceChangedEvent(dev, &dce.changed_event);
 | |
| 
 | |
|     /* if the current slave is us, update the master. If not, we'll update
 | |
|      * whenever the next slave switch happens anyway. CMDC sends the event
 | |
|      * for us */
 | |
|     if (master && master->lastSlave == dev)
 | |
|         ChangeMasterDeviceClasses(master, &dce.changed_event);
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| int
 | |
| CheckGrabValues(ClientPtr client, GrabParameters *param)
 | |
| {
 | |
|     if (param->grabtype != CORE &&
 | |
|         param->grabtype != XI && param->grabtype != XI2) {
 | |
|         ErrorF("[Xi] grabtype is invalid. This is a bug.\n");
 | |
|         return BadImplementation;
 | |
|     }
 | |
| 
 | |
|     if ((param->this_device_mode != GrabModeSync) &&
 | |
|         (param->this_device_mode != GrabModeAsync) &&
 | |
|         (param->this_device_mode != XIGrabModeTouch)) {
 | |
|         client->errorValue = param->this_device_mode;
 | |
|         return BadValue;
 | |
|     }
 | |
|     if ((param->other_devices_mode != GrabModeSync) &&
 | |
|         (param->other_devices_mode != GrabModeAsync) &&
 | |
|         (param->other_devices_mode != XIGrabModeTouch)) {
 | |
|         client->errorValue = param->other_devices_mode;
 | |
|         return BadValue;
 | |
|     }
 | |
| 
 | |
|     if (param->modifiers != AnyModifier &&
 | |
|         param->modifiers != XIAnyModifier &&
 | |
|         (param->modifiers & ~AllModifiersMask)) {
 | |
|         client->errorValue = param->modifiers;
 | |
|         return BadValue;
 | |
|     }
 | |
| 
 | |
|     if ((param->ownerEvents != xFalse) && (param->ownerEvents != xTrue)) {
 | |
|         client->errorValue = param->ownerEvents;
 | |
|         return BadValue;
 | |
|     }
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| int
 | |
| GrabButton(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr modifier_device,
 | |
|            int button, GrabParameters *param, enum InputLevel grabtype,
 | |
|            GrabMask *mask)
 | |
| {
 | |
|     WindowPtr pWin, confineTo;
 | |
|     CursorPtr cursor;
 | |
|     GrabPtr grab;
 | |
|     int rc, type = -1;
 | |
|     Mask access_mode = DixGrabAccess;
 | |
| 
 | |
|     rc = CheckGrabValues(client, param);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
|     if (param->confineTo == None)
 | |
|         confineTo = NullWindow;
 | |
|     else {
 | |
|         rc = dixLookupWindow(&confineTo, param->confineTo, client,
 | |
|                              DixSetAttrAccess);
 | |
|         if (rc != Success)
 | |
|             return rc;
 | |
|     }
 | |
|     if (param->cursor == None)
 | |
|         cursor = NullCursor;
 | |
|     else {
 | |
|         rc = dixLookupResourceByType((void **) &cursor, param->cursor,
 | |
|                                      X11_RESTYPE_CURSOR, client, DixUseAccess);
 | |
|         if (rc != Success) {
 | |
|             client->errorValue = param->cursor;
 | |
|             return rc;
 | |
|         }
 | |
|         access_mode |= DixForceAccess;
 | |
|     }
 | |
|     if (param->this_device_mode == GrabModeSync ||
 | |
|         param->other_devices_mode == GrabModeSync)
 | |
|         access_mode |= DixFreezeAccess;
 | |
|     rc = XaceHookDeviceAccess(client, dev, access_mode);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
|     rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
| 
 | |
|     if (grabtype == XI)
 | |
|         type = DeviceButtonPress;
 | |
|     else if (grabtype == XI2)
 | |
|         type = XI_ButtonPress;
 | |
| 
 | |
|     grab = CreateGrab(client, dev, modifier_device, pWin, grabtype,
 | |
|                       mask, param, type, button, confineTo, cursor);
 | |
|     if (!grab)
 | |
|         return BadAlloc;
 | |
|     return AddPassiveGrabToList(client, grab);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Grab the given key.
 | |
|  */
 | |
| int
 | |
| GrabKey(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr modifier_device,
 | |
|         int key, GrabParameters *param, enum InputLevel grabtype,
 | |
|         GrabMask *mask)
 | |
| {
 | |
|     WindowPtr pWin;
 | |
|     GrabPtr grab;
 | |
|     KeyClassPtr k = dev->key;
 | |
|     Mask access_mode = DixGrabAccess;
 | |
|     int rc, type = -1;
 | |
| 
 | |
|     rc = CheckGrabValues(client, param);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
|     if ((dev->id != XIAllDevices && dev->id != XIAllMasterDevices) && k == NULL)
 | |
|         return BadMatch;
 | |
|     if (grabtype == XI) {
 | |
|         if ((key > k->xkbInfo->desc->max_key_code ||
 | |
|              key < k->xkbInfo->desc->min_key_code)
 | |
|             && (key != AnyKey)) {
 | |
|             client->errorValue = key;
 | |
|             return BadValue;
 | |
|         }
 | |
|         type = DeviceKeyPress;
 | |
|     }
 | |
|     else if (grabtype == XI2)
 | |
|         type = XI_KeyPress;
 | |
| 
 | |
|     rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
|     if (param->this_device_mode == GrabModeSync ||
 | |
|         param->other_devices_mode == GrabModeSync)
 | |
|         access_mode |= DixFreezeAccess;
 | |
|     rc = XaceHookDeviceAccess(client, dev, access_mode);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
| 
 | |
|     grab = CreateGrab(client, dev, modifier_device, pWin, grabtype,
 | |
|                       mask, param, type, key, NULL, NULL);
 | |
|     if (!grab)
 | |
|         return BadAlloc;
 | |
|     return AddPassiveGrabToList(client, grab);
 | |
| }
 | |
| 
 | |
| /* Enter/FocusIn grab */
 | |
| int
 | |
| GrabWindow(ClientPtr client, DeviceIntPtr dev, int type,
 | |
|            GrabParameters *param, GrabMask *mask)
 | |
| {
 | |
|     WindowPtr pWin;
 | |
|     CursorPtr cursor;
 | |
|     GrabPtr grab;
 | |
|     Mask access_mode = DixGrabAccess;
 | |
|     int rc;
 | |
| 
 | |
|     rc = CheckGrabValues(client, param);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
| 
 | |
|     rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
|     if (param->cursor == None)
 | |
|         cursor = NullCursor;
 | |
|     else {
 | |
|         rc = dixLookupResourceByType((void **) &cursor, param->cursor,
 | |
|                                      X11_RESTYPE_CURSOR, client, DixUseAccess);
 | |
|         if (rc != Success) {
 | |
|             client->errorValue = param->cursor;
 | |
|             return rc;
 | |
|         }
 | |
|         access_mode |= DixForceAccess;
 | |
|     }
 | |
|     if (param->this_device_mode == GrabModeSync ||
 | |
|         param->other_devices_mode == GrabModeSync)
 | |
|         access_mode |= DixFreezeAccess;
 | |
|     rc = XaceHookDeviceAccess(client, dev, access_mode);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
| 
 | |
|     grab = CreateGrab(client, dev, dev, pWin, XI2,
 | |
|                       mask, param,
 | |
|                       (type == XIGrabtypeEnter) ? XI_Enter : XI_FocusIn, 0,
 | |
|                       NULL, cursor);
 | |
| 
 | |
|     if (!grab)
 | |
|         return BadAlloc;
 | |
| 
 | |
|     return AddPassiveGrabToList(client, grab);
 | |
| }
 | |
| 
 | |
| /* Touch grab */
 | |
| int
 | |
| GrabTouchOrGesture(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev,
 | |
|                    int type, GrabParameters *param, GrabMask *mask)
 | |
| {
 | |
|     WindowPtr pWin;
 | |
|     GrabPtr grab;
 | |
|     int rc;
 | |
| 
 | |
|     rc = CheckGrabValues(client, param);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
| 
 | |
|     rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
|     rc = XaceHookDeviceAccess(client, dev, DixGrabAccess);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
| 
 | |
|     grab = CreateGrab(client, dev, mod_dev, pWin, XI2,
 | |
|                       mask, param, type, 0, NullWindow, NullCursor);
 | |
|     if (!grab)
 | |
|         return BadAlloc;
 | |
| 
 | |
|     return AddPassiveGrabToList(client, grab);
 | |
| }
 | |
| 
 | |
| int
 | |
| SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client,
 | |
|                 Mask mask, Mask exclusivemasks)
 | |
| {
 | |
|     int mskidx = dev->id;
 | |
|     int i, ret;
 | |
|     Mask check;
 | |
|     InputClientsPtr others;
 | |
| 
 | |
|     check = (mask & exclusivemasks);
 | |
|     if (wOtherInputMasks(pWin)) {
 | |
|         if (check & wOtherInputMasks(pWin)->inputEvents[mskidx]) {
 | |
|             /* It is illegal for two different clients to select on any of
 | |
|              * the events for maskcheck. However, it is OK, for some client
 | |
|              * to continue selecting on one of those events.
 | |
|              */
 | |
|             for (others = wOtherInputMasks(pWin)->inputClients; others;
 | |
|                  others = others->next) {
 | |
|                 if (!SameClient(others, client) && (check &
 | |
|                                                     others->mask[mskidx]))
 | |
|                     return BadAccess;
 | |
|             }
 | |
|         }
 | |
|         assert(wOtherInputMasks(pWin));
 | |
|         for (others = wOtherInputMasks(pWin)->inputClients; others;
 | |
|              others = others->next) {
 | |
|             if (SameClient(others, client)) {
 | |
|                 check = others->mask[mskidx];
 | |
|                 others->mask[mskidx] = mask;
 | |
|                 if (mask == 0) {
 | |
|                     for (i = 0; i < EMASKSIZE; i++)
 | |
|                         if (i != mskidx && others->mask[i] != 0)
 | |
|                             break;
 | |
|                     if (i == EMASKSIZE) {
 | |
|                         RecalculateDeviceDeliverableEvents(pWin);
 | |
|                         if (ShouldFreeInputMasks(pWin, FALSE))
 | |
|                             FreeResource(others->resource, X11_RESTYPE_NONE);
 | |
|                         return Success;
 | |
|                     }
 | |
|                 }
 | |
|                 goto maskSet;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     check = 0;
 | |
|     if ((ret = AddExtensionClient(pWin, client, mask, mskidx)) != Success)
 | |
|         return ret;
 | |
|  maskSet:
 | |
|     if (dev->valuator)
 | |
|         if ((dev->valuator->motionHintWindow == pWin) &&
 | |
|             (mask & DevicePointerMotionHintMask) &&
 | |
|             !(check & DevicePointerMotionHintMask) && !dev->deviceGrab.grab)
 | |
|             dev->valuator->motionHintWindow = NullWindow;
 | |
|     RecalculateDeviceDeliverableEvents(pWin);
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| static void
 | |
| FreeInputClient(InputClientsPtr * other)
 | |
| {
 | |
|     xi2mask_free(&(*other)->xi2mask);
 | |
|     free(*other);
 | |
|     *other = NULL;
 | |
| }
 | |
| 
 | |
| static InputClientsPtr
 | |
| AllocInputClient(void)
 | |
| {
 | |
|     return calloc(1, sizeof(InputClients));
 | |
| }
 | |
| 
 | |
| int
 | |
| AddExtensionClient(WindowPtr pWin, ClientPtr client, Mask mask, int mskidx)
 | |
| {
 | |
|     InputClientsPtr others;
 | |
| 
 | |
|     if (!pWin->optional && !MakeWindowOptional(pWin))
 | |
|         return BadAlloc;
 | |
|     others = AllocInputClient();
 | |
|     if (!others)
 | |
|         return BadAlloc;
 | |
|     if (!pWin->optional->inputMasks && !MakeInputMasks(pWin))
 | |
|         goto bail;
 | |
|     others->xi2mask = xi2mask_new();
 | |
|     if (!others->xi2mask)
 | |
|         goto bail;
 | |
|     others->mask[mskidx] = mask;
 | |
|     others->resource = FakeClientID(client->index);
 | |
|     others->next = pWin->optional->inputMasks->inputClients;
 | |
|     pWin->optional->inputMasks->inputClients = others;
 | |
|     if (!AddResource(others->resource, RT_INPUTCLIENT, (void *) pWin))
 | |
|         goto bail;
 | |
|     return Success;
 | |
| 
 | |
|  bail:
 | |
|     FreeInputClient(&others);
 | |
|     return BadAlloc;
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| MakeInputMasks(WindowPtr pWin)
 | |
| {
 | |
|     struct _OtherInputMasks *imasks;
 | |
| 
 | |
|     imasks = calloc(1, sizeof(struct _OtherInputMasks));
 | |
|     if (!imasks)
 | |
|         return FALSE;
 | |
|     imasks->xi2mask = xi2mask_new();
 | |
|     if (!imasks->xi2mask) {
 | |
|         free(imasks);
 | |
|         return FALSE;
 | |
|     }
 | |
|     pWin->optional->inputMasks = imasks;
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| FreeInputMask(OtherInputMasks ** imask)
 | |
| {
 | |
|     xi2mask_free(&(*imask)->xi2mask);
 | |
|     free(*imask);
 | |
|     *imask = NULL;
 | |
| }
 | |
| 
 | |
| #define XIPropagateMask (KeyPressMask | \
 | |
|                          KeyReleaseMask | \
 | |
|                          ButtonPressMask | \
 | |
|                          ButtonReleaseMask | \
 | |
|                          PointerMotionMask)
 | |
| 
 | |
| void
 | |
| RecalculateDeviceDeliverableEvents(WindowPtr pWin)
 | |
| {
 | |
|     InputClientsPtr others;
 | |
|     struct _OtherInputMasks *inputMasks;        /* default: NULL */
 | |
|     WindowPtr pChild, tmp;
 | |
|     int i;
 | |
| 
 | |
|     pChild = pWin;
 | |
|     while (1) {
 | |
|         if ((inputMasks = wOtherInputMasks(pChild)) != 0) {
 | |
|             xi2mask_zero(inputMasks->xi2mask, -1);
 | |
|             for (others = inputMasks->inputClients; others;
 | |
|                  others = others->next) {
 | |
|                 for (i = 0; i < EMASKSIZE; i++)
 | |
|                     inputMasks->inputEvents[i] |= others->mask[i];
 | |
|                 xi2mask_merge(inputMasks->xi2mask, others->xi2mask);
 | |
|             }
 | |
|             for (i = 0; i < EMASKSIZE; i++)
 | |
|                 inputMasks->deliverableEvents[i] = inputMasks->inputEvents[i];
 | |
|             for (tmp = pChild->parent; tmp; tmp = tmp->parent)
 | |
|                 if (wOtherInputMasks(tmp))
 | |
|                     for (i = 0; i < EMASKSIZE; i++)
 | |
|                         inputMasks->deliverableEvents[i] |=
 | |
|                             (wOtherInputMasks(tmp)->deliverableEvents[i]
 | |
|                              & ~inputMasks->dontPropagateMask[i] &
 | |
|                              XIPropagateMask);
 | |
|         }
 | |
|         if (pChild->firstChild) {
 | |
|             pChild = pChild->firstChild;
 | |
|             continue;
 | |
|         }
 | |
|         while (!pChild->nextSib && (pChild != pWin))
 | |
|             pChild = pChild->parent;
 | |
|         if (pChild == pWin)
 | |
|             break;
 | |
|         pChild = pChild->nextSib;
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| InputClientGone(WindowPtr pWin, XID id)
 | |
| {
 | |
|     InputClientsPtr other, prev;
 | |
| 
 | |
|     if (!wOtherInputMasks(pWin))
 | |
|         return Success;
 | |
|     prev = 0;
 | |
|     for (other = wOtherInputMasks(pWin)->inputClients; other;
 | |
|          other = other->next) {
 | |
|         if (other->resource == id) {
 | |
|             if (prev) {
 | |
|                 prev->next = other->next;
 | |
|                 FreeInputClient(&other);
 | |
|             }
 | |
|             else if (!(other->next)) {
 | |
|                 if (ShouldFreeInputMasks(pWin, TRUE)) {
 | |
|                     OtherInputMasks *mask = wOtherInputMasks(pWin);
 | |
| 
 | |
|                     mask->inputClients = other->next;
 | |
|                     FreeInputMask(&mask);
 | |
|                     pWin->optional->inputMasks = (OtherInputMasks *) NULL;
 | |
|                     CheckWindowOptionalNeed(pWin);
 | |
|                     FreeInputClient(&other);
 | |
|                 }
 | |
|                 else {
 | |
|                     other->resource = FakeClientID(0);
 | |
|                     if (!AddResource(other->resource, RT_INPUTCLIENT,
 | |
|                                      (void *) pWin))
 | |
|                         return BadAlloc;
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 wOtherInputMasks(pWin)->inputClients = other->next;
 | |
|                 FreeInputClient(&other);
 | |
|             }
 | |
|             RecalculateDeviceDeliverableEvents(pWin);
 | |
|             return Success;
 | |
|         }
 | |
|         prev = other;
 | |
|     }
 | |
|     FatalError("client not on device event list");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Search for window in each touch trace for each device. Remove the window
 | |
|  * and all its subwindows from the trace when found. The initial window
 | |
|  * order is preserved.
 | |
|  */
 | |
| void
 | |
| WindowGone(WindowPtr win)
 | |
| {
 | |
|     DeviceIntPtr dev;
 | |
| 
 | |
|     for (dev = inputInfo.devices; dev; dev = dev->next) {
 | |
|         TouchClassPtr t = dev->touch;
 | |
|         int i;
 | |
| 
 | |
|         if (!t)
 | |
|             continue;
 | |
| 
 | |
|         for (i = 0; i < t->num_touches; i++) {
 | |
|             SpritePtr sprite = &t->touches[i].sprite;
 | |
|             int j;
 | |
| 
 | |
|             for (j = 0; j < sprite->spriteTraceGood; j++) {
 | |
|                 if (sprite->spriteTrace[j] == win) {
 | |
|                     sprite->spriteTraceGood = j;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| SendEvent(ClientPtr client, DeviceIntPtr d, Window dest, Bool propagate,
 | |
|           xEvent *ev, Mask mask, int count)
 | |
| {
 | |
|     WindowPtr pWin;
 | |
|     WindowPtr effectiveFocus = NullWindow;      /* only set if dest==InputFocus */
 | |
|     WindowPtr spriteWin = InputDevSpriteWindow(d);
 | |
| 
 | |
|     if (dest == PointerWindow)
 | |
|         pWin = spriteWin;
 | |
|     else if (dest == InputFocus) {
 | |
|         WindowPtr inputFocus;
 | |
| 
 | |
|         if (!d->focus)
 | |
|             inputFocus = spriteWin;
 | |
|         else
 | |
|             inputFocus = d->focus->win;
 | |
| 
 | |
|         if (inputFocus == FollowKeyboardWin)
 | |
|             inputFocus = inputInfo.keyboard->focus->win;
 | |
| 
 | |
|         if (inputFocus == NoneWin)
 | |
|             return Success;
 | |
| 
 | |
|         /* If the input focus is PointerRootWin, send the event to where
 | |
|          * the pointer is if possible, then perhaps propagate up to root. */
 | |
|         if (inputFocus == PointerRootWin)
 | |
|             inputFocus = InputDevCurrentRootWindow(d);
 | |
| 
 | |
|         if (WindowIsParent(inputFocus, spriteWin)) {
 | |
|             effectiveFocus = inputFocus;
 | |
|             pWin = spriteWin;
 | |
|         }
 | |
|         else
 | |
|             effectiveFocus = pWin = inputFocus;
 | |
|     }
 | |
|     else
 | |
|         dixLookupWindow(&pWin, dest, client, DixSendAccess);
 | |
|     if (!pWin)
 | |
|         return BadWindow;
 | |
|     if ((propagate != xFalse) && (propagate != xTrue)) {
 | |
|         client->errorValue = propagate;
 | |
|         return BadValue;
 | |
|     }
 | |
|     ev->u.u.type |= 0x80;
 | |
|     if (propagate) {
 | |
|         for (; pWin; pWin = pWin->parent) {
 | |
|             if (DeliverEventsToWindow(d, pWin, ev, count, mask, NullGrab))
 | |
|                 return Success;
 | |
|             if (pWin == effectiveFocus)
 | |
|                 return Success;
 | |
|             if (wOtherInputMasks(pWin))
 | |
|                 mask &= ~wOtherInputMasks(pWin)->dontPropagateMask[d->id];
 | |
|             if (!mask)
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
|     else if (!XaceHookSendAccess(client, NULL, pWin, ev, count))
 | |
|         DeliverEventsToWindow(d, pWin, ev, count, mask, NullGrab);
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| int
 | |
| SetButtonMapping(ClientPtr client, DeviceIntPtr dev, int nElts, BYTE * map)
 | |
| {
 | |
|     int i;
 | |
|     ButtonClassPtr b = dev->button;
 | |
| 
 | |
|     if (b == NULL)
 | |
|         return BadMatch;
 | |
| 
 | |
|     if (nElts != b->numButtons) {
 | |
|         client->errorValue = nElts;
 | |
|         return BadValue;
 | |
|     }
 | |
|     if (BadDeviceMap(&map[0], nElts, 1, 255, &client->errorValue))
 | |
|         return BadValue;
 | |
|     for (i = 0; i < nElts; i++)
 | |
|         if ((b->map[i + 1] != map[i]) && BitIsOn(b->down, i + 1))
 | |
|             return MappingBusy;
 | |
|     for (i = 0; i < nElts; i++)
 | |
|         b->map[i + 1] = map[i];
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| int
 | |
| ChangeKeyMapping(ClientPtr client,
 | |
|                  DeviceIntPtr dev,
 | |
|                  unsigned len,
 | |
|                  int type,
 | |
|                  KeyCode firstKeyCode,
 | |
|                  CARD8 keyCodes, CARD8 keySymsPerKeyCode, KeySym * map)
 | |
| {
 | |
|     KeySymsRec keysyms;
 | |
|     KeyClassPtr k = dev->key;
 | |
| 
 | |
|     if (k == NULL)
 | |
|         return BadMatch;
 | |
| 
 | |
|     if (len != (keyCodes * keySymsPerKeyCode))
 | |
|         return BadLength;
 | |
| 
 | |
|     if ((firstKeyCode < k->xkbInfo->desc->min_key_code) ||
 | |
|         (firstKeyCode + keyCodes - 1 > k->xkbInfo->desc->max_key_code)) {
 | |
|         client->errorValue = firstKeyCode;
 | |
|         return BadValue;
 | |
|     }
 | |
|     if (keySymsPerKeyCode == 0) {
 | |
|         client->errorValue = 0;
 | |
|         return BadValue;
 | |
|     }
 | |
|     keysyms.minKeyCode = firstKeyCode;
 | |
|     keysyms.maxKeyCode = firstKeyCode + keyCodes - 1;
 | |
|     keysyms.mapWidth = keySymsPerKeyCode;
 | |
|     keysyms.map = map;
 | |
| 
 | |
|     XkbApplyMappingChange(dev, &keysyms, firstKeyCode, keyCodes, NULL,
 | |
|                           serverClient);
 | |
| 
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| static void
 | |
| DeleteDeviceFromAnyExtEvents(WindowPtr pWin, DeviceIntPtr dev)
 | |
| {
 | |
|     WindowPtr parent;
 | |
| 
 | |
|     /* Deactivate any grabs performed on this window, before making
 | |
|      * any input focus changes.
 | |
|      * Deactivating a device grab should cause focus events. */
 | |
| 
 | |
|     if (dev->deviceGrab.grab && (dev->deviceGrab.grab->window == pWin))
 | |
|         (*dev->deviceGrab.DeactivateGrab) (dev);
 | |
| 
 | |
|     /* If the focus window is a root window (ie. has no parent)
 | |
|      * then don't delete the focus from it. */
 | |
| 
 | |
|     if (dev->focus && (pWin == dev->focus->win) && (pWin->parent != NullWindow)) {
 | |
|         int focusEventMode = NotifyNormal;
 | |
| 
 | |
|         /* If a grab is in progress, then alter the mode of focus events. */
 | |
| 
 | |
|         if (dev->deviceGrab.grab)
 | |
|             focusEventMode = NotifyWhileGrabbed;
 | |
| 
 | |
|         switch (dev->focus->revert) {
 | |
|         case RevertToNone:
 | |
|             if (!ActivateFocusInGrab(dev, pWin, NoneWin))
 | |
|                 DoFocusEvents(dev, pWin, NoneWin, focusEventMode);
 | |
|             dev->focus->win = NoneWin;
 | |
|             dev->focus->traceGood = 0;
 | |
|             break;
 | |
|         case RevertToParent:
 | |
|             parent = pWin;
 | |
|             do {
 | |
|                 parent = parent->parent;
 | |
|                 dev->focus->traceGood--;
 | |
|             }
 | |
|             while (!parent->realized);
 | |
|             if (!ActivateFocusInGrab(dev, pWin, parent))
 | |
|                 DoFocusEvents(dev, pWin, parent, focusEventMode);
 | |
|             dev->focus->win = parent;
 | |
|             dev->focus->revert = RevertToNone;
 | |
|             break;
 | |
|         case RevertToPointerRoot:
 | |
|             if (!ActivateFocusInGrab(dev, pWin, PointerRootWin))
 | |
|                 DoFocusEvents(dev, pWin, PointerRootWin, focusEventMode);
 | |
|             dev->focus->win = PointerRootWin;
 | |
|             dev->focus->traceGood = 0;
 | |
|             break;
 | |
|         case RevertToFollowKeyboard:
 | |
|         {
 | |
|             DeviceIntPtr kbd = GetMaster(dev, MASTER_KEYBOARD);
 | |
| 
 | |
|             if (!kbd || (kbd == dev && kbd != inputInfo.keyboard))
 | |
|                 kbd = inputInfo.keyboard;
 | |
|             if (kbd->focus->win) {
 | |
|                 if (!ActivateFocusInGrab(dev, pWin, kbd->focus->win))
 | |
|                     DoFocusEvents(dev, pWin, kbd->focus->win, focusEventMode);
 | |
|                 dev->focus->win = FollowKeyboardWin;
 | |
|                 dev->focus->traceGood = 0;
 | |
|             }
 | |
|             else {
 | |
|                 if (!ActivateFocusInGrab(dev, pWin, NoneWin))
 | |
|                     DoFocusEvents(dev, pWin, NoneWin, focusEventMode);
 | |
|                 dev->focus->win = NoneWin;
 | |
|                 dev->focus->traceGood = 0;
 | |
|             }
 | |
|         }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (dev->valuator)
 | |
|         if (dev->valuator->motionHintWindow == pWin)
 | |
|             dev->valuator->motionHintWindow = NullWindow;
 | |
| }
 | |
| 
 | |
| void
 | |
| DeleteWindowFromAnyExtEvents(WindowPtr pWin, Bool freeResources)
 | |
| {
 | |
|     int i;
 | |
|     DeviceIntPtr dev;
 | |
|     InputClientsPtr ic;
 | |
|     struct _OtherInputMasks *inputMasks;
 | |
| 
 | |
|     for (dev = inputInfo.devices; dev; dev = dev->next) {
 | |
|         DeleteDeviceFromAnyExtEvents(pWin, dev);
 | |
|     }
 | |
| 
 | |
|     for (dev = inputInfo.off_devices; dev; dev = dev->next)
 | |
|         DeleteDeviceFromAnyExtEvents(pWin, dev);
 | |
| 
 | |
|     if (freeResources)
 | |
|         while ((inputMasks = wOtherInputMasks(pWin)) != 0) {
 | |
|             ic = inputMasks->inputClients;
 | |
|             for (i = 0; i < EMASKSIZE; i++)
 | |
|                 inputMasks->dontPropagateMask[i] = 0;
 | |
|             FreeResource(ic->resource, X11_RESTYPE_NONE);
 | |
|         }
 | |
| }
 | |
| 
 | |
| int
 | |
| MaybeSendDeviceMotionNotifyHint(deviceKeyButtonPointer *pEvents, Mask mask)
 | |
| {
 | |
|     DeviceIntPtr dev;
 | |
| 
 | |
|     dixLookupDevice(&dev, pEvents->deviceid & DEVICE_BITS, serverClient,
 | |
|                     DixReadAccess);
 | |
|     if (!dev)
 | |
|         return 0;
 | |
| 
 | |
|     if (pEvents->type == DeviceMotionNotify) {
 | |
|         if (mask & DevicePointerMotionHintMask) {
 | |
|             if (WID(dev->valuator->motionHintWindow) == pEvents->event) {
 | |
|                 return 1;       /* don't send, but pretend we did */
 | |
|             }
 | |
|             pEvents->detail = NotifyHint;
 | |
|         }
 | |
|         else {
 | |
|             pEvents->detail = NotifyNormal;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| CheckDeviceGrabAndHintWindow(WindowPtr pWin, int type,
 | |
|                              deviceKeyButtonPointer *xE, GrabPtr grab,
 | |
|                              ClientPtr client, Mask deliveryMask)
 | |
| {
 | |
|     DeviceIntPtr dev;
 | |
| 
 | |
|     dixLookupDevice(&dev, xE->deviceid & DEVICE_BITS, serverClient,
 | |
|                     DixGrabAccess);
 | |
|     if (!dev)
 | |
|         return;
 | |
| 
 | |
|     if (type == DeviceMotionNotify)
 | |
|         dev->valuator->motionHintWindow = pWin;
 | |
|     else if ((type == DeviceButtonPress) && (!grab) &&
 | |
|              (deliveryMask & DeviceButtonGrabMask)) {
 | |
|         GrabPtr tempGrab;
 | |
| 
 | |
|         tempGrab = AllocGrab(NULL);
 | |
|         if (!tempGrab)
 | |
|             return;
 | |
| 
 | |
|         tempGrab->device = dev;
 | |
|         tempGrab->resource = client->clientAsMask;
 | |
|         tempGrab->window = pWin;
 | |
|         tempGrab->ownerEvents =
 | |
|             (deliveryMask & DeviceOwnerGrabButtonMask) ? TRUE : FALSE;
 | |
|         tempGrab->eventMask = deliveryMask;
 | |
|         tempGrab->keyboardMode = GrabModeAsync;
 | |
|         tempGrab->pointerMode = GrabModeAsync;
 | |
|         tempGrab->confineTo = NullWindow;
 | |
|         tempGrab->cursor = NullCursor;
 | |
|         tempGrab->next = NULL;
 | |
|         (*dev->deviceGrab.ActivateGrab) (dev, tempGrab, currentTime, TRUE);
 | |
|         FreeGrab(tempGrab);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static Mask
 | |
| DeviceEventMaskForClient(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client)
 | |
| {
 | |
|     InputClientsPtr other;
 | |
| 
 | |
|     if (!wOtherInputMasks(pWin))
 | |
|         return 0;
 | |
|     for (other = wOtherInputMasks(pWin)->inputClients; other;
 | |
|          other = other->next) {
 | |
|         if (SameClient(other, client))
 | |
|             return other->mask[dev->id];
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| MaybeStopDeviceHint(DeviceIntPtr dev, ClientPtr client)
 | |
| {
 | |
|     WindowPtr pWin;
 | |
|     GrabPtr grab = dev->deviceGrab.grab;
 | |
| 
 | |
|     pWin = dev->valuator->motionHintWindow;
 | |
| 
 | |
|     if ((grab && SameClient(grab, client) &&
 | |
|          ((grab->eventMask & DevicePointerMotionHintMask) ||
 | |
|           (grab->ownerEvents &&
 | |
|            (DeviceEventMaskForClient(dev, pWin, client) &
 | |
|             DevicePointerMotionHintMask)))) ||
 | |
|         (!grab &&
 | |
|          (DeviceEventMaskForClient(dev, pWin, client) &
 | |
|           DevicePointerMotionHintMask)))
 | |
|         dev->valuator->motionHintWindow = NullWindow;
 | |
| }
 | |
| 
 | |
| int
 | |
| DeviceEventSuppressForWindow(WindowPtr pWin, ClientPtr client, Mask mask,
 | |
|                              int maskndx)
 | |
| {
 | |
|     struct _OtherInputMasks *inputMasks = wOtherInputMasks(pWin);
 | |
| 
 | |
|     if (mask & ~XIPropagateMask) {
 | |
|         client->errorValue = mask;
 | |
|         return BadValue;
 | |
|     }
 | |
| 
 | |
|     if (mask == 0) {
 | |
|         if (inputMasks)
 | |
|             inputMasks->dontPropagateMask[maskndx] = mask;
 | |
|     }
 | |
|     else {
 | |
|         if (!inputMasks)
 | |
|             AddExtensionClient(pWin, client, 0, 0);
 | |
|         inputMasks = wOtherInputMasks(pWin);
 | |
|         inputMasks->dontPropagateMask[maskndx] = mask;
 | |
|     }
 | |
|     RecalculateDeviceDeliverableEvents(pWin);
 | |
|     if (ShouldFreeInputMasks(pWin, FALSE)) {
 | |
|         BUG_RETURN_VAL(!inputMasks, BadImplementation);
 | |
|         BUG_RETURN_VAL(!inputMasks->inputClients, BadImplementation);
 | |
|         FreeResource(inputMasks->inputClients->resource, X11_RESTYPE_NONE);
 | |
|     }
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| ShouldFreeInputMasks(WindowPtr pWin, Bool ignoreSelectedEvents)
 | |
| {
 | |
|     int i;
 | |
|     Mask allInputEventMasks = 0;
 | |
|     struct _OtherInputMasks *inputMasks = wOtherInputMasks(pWin);
 | |
| 
 | |
|     for (i = 0; i < EMASKSIZE; i++)
 | |
|         allInputEventMasks |= inputMasks->dontPropagateMask[i];
 | |
|     if (!ignoreSelectedEvents)
 | |
|         for (i = 0; i < EMASKSIZE; i++)
 | |
|             allInputEventMasks |= inputMasks->inputEvents[i];
 | |
|     if (allInputEventMasks == 0)
 | |
|         return TRUE;
 | |
|     else
 | |
|         return FALSE;
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *
 | |
|  * Walk through the window tree, finding all clients that want to know
 | |
|  * about the Event.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| static void
 | |
| FindInterestedChildren(DeviceIntPtr dev, WindowPtr p1, Mask mask,
 | |
|                        xEvent *ev, int count)
 | |
| {
 | |
|     WindowPtr p2;
 | |
| 
 | |
|     while (p1) {
 | |
|         p2 = p1->firstChild;
 | |
|         DeliverEventsToWindow(dev, p1, ev, count, mask, NullGrab);
 | |
|         FindInterestedChildren(dev, p2, mask, ev, count);
 | |
|         p1 = p1->nextSib;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *
 | |
|  * Send an event to interested clients in all windows on all screens.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| void
 | |
| SendEventToAllWindows(DeviceIntPtr dev, Mask mask, xEvent *ev, int count)
 | |
| {
 | |
|     int i;
 | |
|     WindowPtr pWin, p1;
 | |
| 
 | |
|     for (i = 0; i < screenInfo.numScreens; i++) {
 | |
|         pWin = screenInfo.screens[i]->root;
 | |
|         if (!pWin)
 | |
|             continue;
 | |
|         DeliverEventsToWindow(dev, pWin, ev, count, mask, NullGrab);
 | |
|         p1 = pWin->firstChild;
 | |
|         FindInterestedChildren(dev, p1, mask, ev, count);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the XI2 mask for the given client on the given window.
 | |
|  * @param dev The device to set the mask for.
 | |
|  * @param win The window to set the mask on.
 | |
|  * @param client The client setting the mask.
 | |
|  * @param len Number of bytes in mask.
 | |
|  * @param mask Event mask in the form of (1 << eventtype)
 | |
|  */
 | |
| int
 | |
| XISetEventMask(DeviceIntPtr dev, WindowPtr win, ClientPtr client,
 | |
|                unsigned int len, unsigned char *mask)
 | |
| {
 | |
|     OtherInputMasks *masks;
 | |
|     InputClientsPtr others = NULL;
 | |
| 
 | |
|     masks = wOtherInputMasks(win);
 | |
|     if (masks) {
 | |
|         for (others = wOtherInputMasks(win)->inputClients; others;
 | |
|              others = others->next) {
 | |
|             if (SameClient(others, client)) {
 | |
|                 xi2mask_zero(others->xi2mask, dev->id);
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (len && !others) {
 | |
|         if (AddExtensionClient(win, client, 0, 0) != Success)
 | |
|             return BadAlloc;
 | |
|         assert(wOtherInputMasks(win));
 | |
|         others = wOtherInputMasks(win)->inputClients;
 | |
|     }
 | |
| 
 | |
|     if (others) {
 | |
|         xi2mask_zero(others->xi2mask, dev->id);
 | |
|         len = min(len, xi2mask_mask_size(others->xi2mask));
 | |
|     }
 | |
| 
 | |
|     if (len) {
 | |
|         assert(others);
 | |
|         xi2mask_set_one_mask(others->xi2mask, dev->id, mask, len);
 | |
|     }
 | |
| 
 | |
|     RecalculateDeviceDeliverableEvents(win);
 | |
| 
 | |
|     return Success;
 | |
| }
 |