/************************************************************ Copyright 1987, 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 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 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 Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL 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. ********************************************************/ /* The panoramix components contained the following notice */ /***************************************************************** Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts. 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. 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 DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING, BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL 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 Digital Equipment Corporation shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Digital Equipment Corporation. ******************************************************************/ /***************************************************************** Copyright 2003-2005 Sun Microsystems, Inc. All rights reserved. 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, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. 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 OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR 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. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. ******************************************************************/ /** @file * This file handles event delivery and a big part of the server-side protocol * handling (the parts for input devices). */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include #include "misc.h" #include "resource.h" #define NEED_EVENTS #define NEED_REPLIES #include #include "windowstr.h" #include "inputstr.h" #include "scrnintstr.h" #include "cursorstr.h" #include "dixstruct.h" #ifdef PANORAMIX #include "panoramiX.h" #include "panoramiXsrv.h" #endif #include "globals.h" #ifdef XKB #include #include extern Bool XkbFilterEvents(ClientPtr, int, xEvent *); #endif #include "xace.h" #ifdef XSERVER_DTRACE #include typedef const char *string; #include "Xserver-dtrace.h" #endif #ifdef XEVIE extern WindowPtr *WindowTable; extern int xevieFlag; extern int xevieClientIndex; extern DeviceIntPtr xeviemouse; extern DeviceIntPtr xeviekb; extern Mask xevieMask; extern Mask xevieFilters[128]; extern int xevieEventSent; extern int xevieKBEventSent; int xeviegrabState = 0; #endif #include #include "exglobals.h" #include "exevents.h" #include "exglobals.h" #include "extnsionst.h" #include "dixevents.h" #include "dixgrabs.h" #include "dispatch.h" /** * Extension events type numbering starts at EXTENSION_EVENT_BASE. */ #define EXTENSION_EVENT_BASE 64 #define NoSuchEvent 0x80000000 /* so doesn't match NoEventMask */ #define StructureAndSubMask ( StructureNotifyMask | SubstructureNotifyMask ) #define AllButtonsMask ( \ Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask ) #define MotionMask ( \ PointerMotionMask | Button1MotionMask | \ Button2MotionMask | Button3MotionMask | Button4MotionMask | \ Button5MotionMask | ButtonMotionMask ) #define PropagateMask ( \ KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | \ MotionMask ) #define PointerGrabMask ( \ ButtonPressMask | ButtonReleaseMask | \ EnterWindowMask | LeaveWindowMask | \ PointerMotionHintMask | KeymapStateMask | \ MotionMask ) #define AllModifiersMask ( \ ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | \ Mod3Mask | Mod4Mask | Mod5Mask ) #define AllEventMasks (lastEventMask|(lastEventMask-1)) /* * The following relies on the fact that the ButtonMotionMasks are equal * to the corresponding ButtonMasks from the current modifier/button state. */ #define Motion_Filter(class) (PointerMotionMask | \ (class)->state | (class)->motionMask) #define WID(w) ((w) ? ((w)->drawable.id) : 0) #define XE_KBPTR (xE->u.keyButtonPointer) #define rClient(obj) (clients[CLIENT_ID((obj)->resource)]) _X_EXPORT CallbackListPtr EventCallback; _X_EXPORT CallbackListPtr DeviceEventCallback; #define DNPMCOUNT 8 Mask DontPropagateMasks[DNPMCOUNT]; static int DontPropagateRefCnts[DNPMCOUNT]; /** * Main input device struct. * inputInfo.pointer * is the core pointer. Referred to as "virtual core pointer", "VCP", * "core pointer" or inputInfo.pointer. There is exactly one core pointer, * but multiple devices may send core events. If a device generates core * events, those events will appear to originate from the core pointer. * * inputInfo.keyboard * is the core keyboard ("virtual core keyboard", "VCK", "core keyboard"). * See inputInfo.pointer. * * inputInfo.devices * linked list containing all devices including VCK and VCP. The VCK will * always be the first entry, the VCP the second entry in the device list. * * inputInfo.off_devices * Devices that have not been initialized and are thus turned off. * * inputInfo.numDevices * Total number of devices. */ _X_EXPORT InputInfo inputInfo; static struct { QdEventPtr pending, *pendtail; DeviceIntPtr replayDev; /* kludgy rock to put flag for */ WindowPtr replayWin; /* ComputeFreezes */ Bool playingEvents; TimeStamp time; } syncEvents; /* * The window trace information is used to avoid having to compute all the * windows between the root and the current pointer window each time a button * or key goes down. The grabs on each of those windows must be checked. * * @see XYToWindow() for a documentation on how the array is set up. */ static WindowPtr *spriteTrace = (WindowPtr *)NULL; #define ROOT spriteTrace[0] static int spriteTraceSize = 0; static int spriteTraceGood; /** * DIX sprite information. This is the sprite as seen from the DIX. It does * not represent the actual sprite rendered to the screen. * */ static struct { CursorPtr current; BoxRec hotLimits; /* logical constraints of hot spot */ Bool confined; /* confined to screen */ #if defined(SHAPE) || defined(PANORAMIX) RegionPtr hotShape; /* additional logical shape constraint */ #endif BoxRec physLimits; /* physical constraints of hot spot */ WindowPtr win; /* window of logical position */ HotSpot hot; /* logical pointer position */ HotSpot hotPhys; /* physical pointer position */ #ifdef PANORAMIX ScreenPtr screen; /* all others are in Screen 0 coordinates */ RegionRec Reg1; /* Region 1 for confining motion */ RegionRec Reg2; /* Region 2 for confining virtual motion */ WindowPtr windows[MAXSCREENS]; WindowPtr confineWin; /* confine window */ #endif } sprite; /* info about the cursor sprite */ #ifdef XEVIE _X_EXPORT WindowPtr xeviewin; _X_EXPORT HotSpot xeviehot; #endif static void DoEnterLeaveEvents( WindowPtr fromWin, WindowPtr toWin, int mode ); static WindowPtr XYToWindow( int x, int y ); /** * Max event opcode. */ extern int lastEvent; static Mask lastEventMask; #ifdef XINPUT extern int DeviceMotionNotify; #endif #define CantBeFiltered NoEventMask static Mask filters[128] = { NoSuchEvent, /* 0 */ NoSuchEvent, /* 1 */ KeyPressMask, /* KeyPress */ KeyReleaseMask, /* KeyRelease */ ButtonPressMask, /* ButtonPress */ ButtonReleaseMask, /* ButtonRelease */ PointerMotionMask, /* MotionNotify (initial state) */ EnterWindowMask, /* EnterNotify */ LeaveWindowMask, /* LeaveNotify */ FocusChangeMask, /* FocusIn */ FocusChangeMask, /* FocusOut */ KeymapStateMask, /* KeymapNotify */ ExposureMask, /* Expose */ CantBeFiltered, /* GraphicsExpose */ CantBeFiltered, /* NoExpose */ VisibilityChangeMask, /* VisibilityNotify */ SubstructureNotifyMask, /* CreateNotify */ StructureAndSubMask, /* DestroyNotify */ StructureAndSubMask, /* UnmapNotify */ StructureAndSubMask, /* MapNotify */ SubstructureRedirectMask, /* MapRequest */ StructureAndSubMask, /* ReparentNotify */ StructureAndSubMask, /* ConfigureNotify */ SubstructureRedirectMask, /* ConfigureRequest */ StructureAndSubMask, /* GravityNotify */ ResizeRedirectMask, /* ResizeRequest */ StructureAndSubMask, /* CirculateNotify */ SubstructureRedirectMask, /* CirculateRequest */ PropertyChangeMask, /* PropertyNotify */ CantBeFiltered, /* SelectionClear */ CantBeFiltered, /* SelectionRequest */ CantBeFiltered, /* SelectionNotify */ ColormapChangeMask, /* ColormapNotify */ CantBeFiltered, /* ClientMessage */ CantBeFiltered /* MappingNotify */ }; static CARD8 criticalEvents[32] = { 0x7c /* key and button events */ }; #ifdef PANORAMIX static void ConfineToShape(RegionPtr shape, int *px, int *py); static void PostNewCursor(void); #define SyntheticMotion(x, y) \ PostSyntheticMotion(x, y, noPanoramiXExtension ? 0 : \ sprite.screen->myNum, \ syncEvents.playingEvents ? \ syncEvents.time.milliseconds : \ currentTime.milliseconds); static Bool XineramaSetCursorPosition( int x, int y, Bool generateEvent ){ ScreenPtr pScreen; BoxRec box; int i; /* x,y are in Screen 0 coordinates. We need to decide what Screen to send the message too and what the coordinates relative to that screen are. */ pScreen = sprite.screen; x += panoramiXdataPtr[0].x; y += panoramiXdataPtr[0].y; if(!POINT_IN_REGION(pScreen, &XineramaScreenRegions[pScreen->myNum], x, y, &box)) { FOR_NSCREENS(i) { if(i == pScreen->myNum) continue; if(POINT_IN_REGION(pScreen, &XineramaScreenRegions[i], x, y, &box)) { pScreen = screenInfo.screens[i]; break; } } } sprite.screen = pScreen; sprite.hotPhys.x = x - panoramiXdataPtr[0].x; sprite.hotPhys.y = y - panoramiXdataPtr[0].y; x -= panoramiXdataPtr[pScreen->myNum].x; y -= panoramiXdataPtr[pScreen->myNum].y; return (*pScreen->SetCursorPosition)(pScreen, x, y, generateEvent); } static void XineramaConstrainCursor(void) { ScreenPtr pScreen = sprite.screen; BoxRec newBox = sprite.physLimits; /* Translate the constraining box to the screen the sprite is actually on */ newBox.x1 += panoramiXdataPtr[0].x - panoramiXdataPtr[pScreen->myNum].x; newBox.x2 += panoramiXdataPtr[0].x - panoramiXdataPtr[pScreen->myNum].x; newBox.y1 += panoramiXdataPtr[0].y - panoramiXdataPtr[pScreen->myNum].y; newBox.y2 += panoramiXdataPtr[0].y - panoramiXdataPtr[pScreen->myNum].y; (* pScreen->ConstrainCursor)(pScreen, &newBox); } static void XineramaCheckPhysLimits( CursorPtr cursor, Bool generateEvents ){ HotSpot new; if (!cursor) return; new = sprite.hotPhys; /* I don't care what the DDX has to say about it */ sprite.physLimits = sprite.hotLimits; /* constrain the pointer to those limits */ if (new.x < sprite.physLimits.x1) new.x = sprite.physLimits.x1; else if (new.x >= sprite.physLimits.x2) new.x = sprite.physLimits.x2 - 1; if (new.y < sprite.physLimits.y1) new.y = sprite.physLimits.y1; else if (new.y >= sprite.physLimits.y2) new.y = sprite.physLimits.y2 - 1; if (sprite.hotShape) /* more work if the shape is a mess */ ConfineToShape(sprite.hotShape, &new.x, &new.y); if((new.x != sprite.hotPhys.x) || (new.y != sprite.hotPhys.y)) { XineramaSetCursorPosition (new.x, new.y, generateEvents); if (!generateEvents) SyntheticMotion(new.x, new.y); } /* Tell DDX what the limits are */ XineramaConstrainCursor(); } static Bool XineramaSetWindowPntrs(WindowPtr pWin) { if(pWin == WindowTable[0]) { memcpy(sprite.windows, WindowTable, PanoramiXNumScreens*sizeof(WindowPtr)); } else { PanoramiXRes *win; int i; win = (PanoramiXRes*)LookupIDByType(pWin->drawable.id, XRT_WINDOW); if(!win) return FALSE; for(i = 0; i < PanoramiXNumScreens; i++) { sprite.windows[i] = LookupIDByType(win->info[i].id, RT_WINDOW); if(!sprite.windows[i]) /* window is being unmapped */ return FALSE; } } return TRUE; } static void XineramaCheckVirtualMotion( QdEventPtr qe, WindowPtr pWin ){ if (qe) { sprite.hot.pScreen = qe->pScreen; /* should always be Screen 0 */ sprite.hot.x = qe->event->u.keyButtonPointer.rootX; sprite.hot.y = qe->event->u.keyButtonPointer.rootY; pWin = inputInfo.pointer->grab ? inputInfo.pointer->grab->confineTo : NullWindow; } if (pWin) { int x, y, off_x, off_y, i; BoxRec lims; if(!XineramaSetWindowPntrs(pWin)) return; i = PanoramiXNumScreens - 1; REGION_COPY(sprite.screen, &sprite.Reg2, &sprite.windows[i]->borderSize); off_x = panoramiXdataPtr[i].x; off_y = panoramiXdataPtr[i].y; while(i--) { x = off_x - panoramiXdataPtr[i].x; y = off_y - panoramiXdataPtr[i].y; if(x || y) REGION_TRANSLATE(sprite.screen, &sprite.Reg2, x, y); REGION_UNION(sprite.screen, &sprite.Reg2, &sprite.Reg2, &sprite.windows[i]->borderSize); off_x = panoramiXdataPtr[i].x; off_y = panoramiXdataPtr[i].y; } lims = *REGION_EXTENTS(sprite.screen, &sprite.Reg2); if (sprite.hot.x < lims.x1) sprite.hot.x = lims.x1; else if (sprite.hot.x >= lims.x2) sprite.hot.x = lims.x2 - 1; if (sprite.hot.y < lims.y1) sprite.hot.y = lims.y1; else if (sprite.hot.y >= lims.y2) sprite.hot.y = lims.y2 - 1; if (REGION_NUM_RECTS(&sprite.Reg2) > 1) ConfineToShape(&sprite.Reg2, &sprite.hot.x, &sprite.hot.y); if (qe) { qe->pScreen = sprite.hot.pScreen; qe->event->u.keyButtonPointer.rootX = sprite.hot.x; qe->event->u.keyButtonPointer.rootY = sprite.hot.y; } } #ifdef XEVIE xeviehot.x = sprite.hot.x; xeviehot.y = sprite.hot.y; #endif } static Bool XineramaCheckMotion(xEvent *xE) { WindowPtr prevSpriteWin = sprite.win; if (xE && !syncEvents.playingEvents) { /* Motion events entering DIX get translated to Screen 0 coordinates. Replayed events have already been translated since they've entered DIX before */ XE_KBPTR.rootX += panoramiXdataPtr[sprite.screen->myNum].x - panoramiXdataPtr[0].x; XE_KBPTR.rootY += panoramiXdataPtr[sprite.screen->myNum].y - panoramiXdataPtr[0].y; sprite.hot.x = XE_KBPTR.rootX; sprite.hot.y = XE_KBPTR.rootY; if (sprite.hot.x < sprite.physLimits.x1) sprite.hot.x = sprite.physLimits.x1; else if (sprite.hot.x >= sprite.physLimits.x2) sprite.hot.x = sprite.physLimits.x2 - 1; if (sprite.hot.y < sprite.physLimits.y1) sprite.hot.y = sprite.physLimits.y1; else if (sprite.hot.y >= sprite.physLimits.y2) sprite.hot.y = sprite.physLimits.y2 - 1; if (sprite.hotShape) ConfineToShape(sprite.hotShape, &sprite.hot.x, &sprite.hot.y); sprite.hotPhys = sprite.hot; if ((sprite.hotPhys.x != XE_KBPTR.rootX) || (sprite.hotPhys.y != XE_KBPTR.rootY)) { XineramaSetCursorPosition( sprite.hotPhys.x, sprite.hotPhys.y, FALSE); } XE_KBPTR.rootX = sprite.hot.x; XE_KBPTR.rootY = sprite.hot.y; } #ifdef XEVIE xeviehot.x = sprite.hot.x; xeviehot.y = sprite.hot.y; xeviewin = #endif sprite.win = XYToWindow(sprite.hot.x, sprite.hot.y); if (sprite.win != prevSpriteWin) { if (prevSpriteWin != NullWindow) { if (!xE) UpdateCurrentTimeIf(); DoEnterLeaveEvents(prevSpriteWin, sprite.win, NotifyNormal); } PostNewCursor(); return FALSE; } return TRUE; } static void XineramaConfineCursorToWindow(WindowPtr pWin, Bool generateEvents) { if (syncEvents.playingEvents) { XineramaCheckVirtualMotion((QdEventPtr)NULL, pWin); SyntheticMotion(sprite.hot.x, sprite.hot.y); } else { int x, y, off_x, off_y, i; if(!XineramaSetWindowPntrs(pWin)) return; i = PanoramiXNumScreens - 1; REGION_COPY(sprite.screen, &sprite.Reg1, &sprite.windows[i]->borderSize); off_x = panoramiXdataPtr[i].x; off_y = panoramiXdataPtr[i].y; while(i--) { x = off_x - panoramiXdataPtr[i].x; y = off_y - panoramiXdataPtr[i].y; if(x || y) REGION_TRANSLATE(sprite.screen, &sprite.Reg1, x, y); REGION_UNION(sprite.screen, &sprite.Reg1, &sprite.Reg1, &sprite.windows[i]->borderSize); off_x = panoramiXdataPtr[i].x; off_y = panoramiXdataPtr[i].y; } sprite.hotLimits = *REGION_EXTENTS(sprite.screen, &sprite.Reg1); if(REGION_NUM_RECTS(&sprite.Reg1) > 1) sprite.hotShape = &sprite.Reg1; else sprite.hotShape = NullRegion; sprite.confined = FALSE; sprite.confineWin = (pWin == WindowTable[0]) ? NullWindow : pWin; XineramaCheckPhysLimits(sprite.current, generateEvents); } } static void XineramaChangeToCursor(CursorPtr cursor) { if (cursor != sprite.current) { if ((sprite.current->bits->xhot != cursor->bits->xhot) || (sprite.current->bits->yhot != cursor->bits->yhot)) XineramaCheckPhysLimits(cursor, FALSE); (*sprite.screen->DisplayCursor)(sprite.screen, cursor); FreeCursor(sprite.current, (Cursor)0); sprite.current = cursor; sprite.current->refcnt++; } } #else #define SyntheticMotion(x, y) \ PostSyntheticMotion(x, y, \ 0, \ syncEvents.playingEvents ? \ syncEvents.time.milliseconds : \ currentTime.milliseconds); #endif /* PANORAMIX */ void SetMaskForEvent(Mask mask, int event) { if ((event < LASTEvent) || (event >= 128)) FatalError("SetMaskForEvent: bogus event number"); filters[event] = mask; } _X_EXPORT void SetCriticalEvent(int event) { if (event >= 128) FatalError("SetCriticalEvent: bogus event number"); criticalEvents[event >> 3] |= 1 << (event & 7); } #ifdef SHAPE static void ConfineToShape(RegionPtr shape, int *px, int *py) { BoxRec box; int x = *px, y = *py; int incx = 1, incy = 1; if (POINT_IN_REGION(sprite.hot.pScreen, shape, x, y, &box)) return; box = *REGION_EXTENTS(sprite.hot.pScreen, shape); /* this is rather crude */ do { x += incx; if (x >= box.x2) { incx = -1; x = *px - 1; } else if (x < box.x1) { incx = 1; x = *px; y += incy; if (y >= box.y2) { incy = -1; y = *py - 1; } else if (y < box.y1) return; /* should never get here! */ } } while (!POINT_IN_REGION(sprite.hot.pScreen, shape, x, y, &box)); *px = x; *py = y; } #endif static void CheckPhysLimits( CursorPtr cursor, Bool generateEvents, Bool confineToScreen, ScreenPtr pScreen) { HotSpot new; if (!cursor) return; new = sprite.hotPhys; if (pScreen) new.pScreen = pScreen; else pScreen = new.pScreen; (*pScreen->CursorLimits) (pScreen, cursor, &sprite.hotLimits, &sprite.physLimits); sprite.confined = confineToScreen; (* pScreen->ConstrainCursor)(pScreen, &sprite.physLimits); if (new.x < sprite.physLimits.x1) new.x = sprite.physLimits.x1; else if (new.x >= sprite.physLimits.x2) new.x = sprite.physLimits.x2 - 1; if (new.y < sprite.physLimits.y1) new.y = sprite.physLimits.y1; else if (new.y >= sprite.physLimits.y2) new.y = sprite.physLimits.y2 - 1; #ifdef SHAPE if (sprite.hotShape) ConfineToShape(sprite.hotShape, &new.x, &new.y); #endif if ((pScreen != sprite.hotPhys.pScreen) || (new.x != sprite.hotPhys.x) || (new.y != sprite.hotPhys.y)) { if (pScreen != sprite.hotPhys.pScreen) sprite.hotPhys = new; (*pScreen->SetCursorPosition) (pScreen, new.x, new.y, generateEvents); if (!generateEvents) SyntheticMotion(new.x, new.y); } } static void CheckVirtualMotion( QdEventPtr qe, WindowPtr pWin) { #ifdef PANORAMIX if(!noPanoramiXExtension) { XineramaCheckVirtualMotion(qe, pWin); return; } #endif if (qe) { sprite.hot.pScreen = qe->pScreen; sprite.hot.x = qe->event->u.keyButtonPointer.rootX; sprite.hot.y = qe->event->u.keyButtonPointer.rootY; pWin = inputInfo.pointer->grab ? inputInfo.pointer->grab->confineTo : NullWindow; } if (pWin) { BoxRec lims; if (sprite.hot.pScreen != pWin->drawable.pScreen) { sprite.hot.pScreen = pWin->drawable.pScreen; sprite.hot.x = sprite.hot.y = 0; } lims = *REGION_EXTENTS(pWin->drawable.pScreen, &pWin->borderSize); if (sprite.hot.x < lims.x1) sprite.hot.x = lims.x1; else if (sprite.hot.x >= lims.x2) sprite.hot.x = lims.x2 - 1; if (sprite.hot.y < lims.y1) sprite.hot.y = lims.y1; else if (sprite.hot.y >= lims.y2) sprite.hot.y = lims.y2 - 1; #ifdef SHAPE if (wBoundingShape(pWin)) ConfineToShape(&pWin->borderSize, &sprite.hot.x, &sprite.hot.y); #endif if (qe) { qe->pScreen = sprite.hot.pScreen; qe->event->u.keyButtonPointer.rootX = sprite.hot.x; qe->event->u.keyButtonPointer.rootY = sprite.hot.y; } } #ifdef XEVIE xeviehot.x = sprite.hot.x; xeviehot.y = sprite.hot.y; #endif ROOT = WindowTable[sprite.hot.pScreen->myNum]; } static void ConfineCursorToWindow(WindowPtr pWin, Bool generateEvents, Bool confineToScreen) { ScreenPtr pScreen = pWin->drawable.pScreen; #ifdef PANORAMIX if(!noPanoramiXExtension) { XineramaConfineCursorToWindow(pWin, generateEvents); return; } #endif if (syncEvents.playingEvents) { CheckVirtualMotion((QdEventPtr)NULL, pWin); SyntheticMotion(sprite.hot.x, sprite.hot.y); } else { sprite.hotLimits = *REGION_EXTENTS( pScreen, &pWin->borderSize); #ifdef SHAPE sprite.hotShape = wBoundingShape(pWin) ? &pWin->borderSize : NullRegion; #endif CheckPhysLimits(sprite.current, generateEvents, confineToScreen, pScreen); } } _X_EXPORT Bool PointerConfinedToScreen(void) { return sprite.confined; } /** * Update the sprite cursor to the given cursor. * * ChangeToCursor() will display the new cursor and free the old cursor (if * applicable). If the provided cursor is already the updated cursor, nothing * happens. */ static void ChangeToCursor(CursorPtr cursor) { #ifdef PANORAMIX if(!noPanoramiXExtension) { XineramaChangeToCursor(cursor); return; } #endif if (cursor != sprite.current) { if ((sprite.current->bits->xhot != cursor->bits->xhot) || (sprite.current->bits->yhot != cursor->bits->yhot)) CheckPhysLimits(cursor, FALSE, sprite.confined, (ScreenPtr)NULL); (*sprite.hotPhys.pScreen->DisplayCursor) (sprite.hotPhys.pScreen, cursor); FreeCursor(sprite.current, (Cursor)0); sprite.current = cursor; sprite.current->refcnt++; } } /** * @returns true if b is a descendent of a */ Bool IsParent(WindowPtr a, WindowPtr b) { for (b = b->parent; b; b = b->parent) if (b == a) return TRUE; return FALSE; } /** * Update the cursor displayed on the screen. * * Called whenever a cursor may have changed shape or position. */ static void PostNewCursor(void) { WindowPtr win; GrabPtr grab = inputInfo.pointer->grab; if (syncEvents.playingEvents) return; if (grab) { if (grab->cursor) { ChangeToCursor(grab->cursor); return; } if (IsParent(grab->window, sprite.win)) win = sprite.win; else win = grab->window; } else win = sprite.win; for (; win; win = win->parent) if (win->optional && win->optional->cursor != NullCursor) { ChangeToCursor(win->optional->cursor); return; } } /** * @return root window of current active screen. */ _X_EXPORT WindowPtr GetCurrentRootWindow(void) { return ROOT; } /** * @return window underneath the cursor sprite. */ _X_EXPORT WindowPtr GetSpriteWindow(void) { return sprite.win; } /** * @return current sprite cursor. */ _X_EXPORT CursorPtr GetSpriteCursor(void) { return sprite.current; } /** * Set x/y current sprite position in screen coordinates. */ _X_EXPORT void GetSpritePosition(int *px, int *py) { *px = sprite.hotPhys.x; *py = sprite.hotPhys.y; } #ifdef PANORAMIX _X_EXPORT int XineramaGetCursorScreen(void) { if(!noPanoramiXExtension) { return sprite.screen->myNum; } else { return 0; } } #endif /* PANORAMIX */ #define TIMESLOP (5 * 60 * 1000) /* 5 minutes */ static void MonthChangedOrBadTime(xEvent *xE) { /* If the ddx/OS is careless about not processing timestamped events from * different sources in sorted order, then it's possible for time to go * backwards when it should not. Here we ensure a decent time. */ if ((currentTime.milliseconds - XE_KBPTR.time) > TIMESLOP) currentTime.months++; else XE_KBPTR.time = currentTime.milliseconds; } #define NoticeTime(xE) { \ if ((xE)->u.keyButtonPointer.time < currentTime.milliseconds) \ MonthChangedOrBadTime(xE); \ currentTime.milliseconds = (xE)->u.keyButtonPointer.time; \ lastDeviceEventTime = currentTime; } void NoticeEventTime(xEvent *xE) { if (!syncEvents.playingEvents) NoticeTime(xE); } /************************************************************************** * The following procedures deal with synchronous events * **************************************************************************/ void EnqueueEvent(xEvent *xE, DeviceIntPtr device, int count) { QdEventPtr tail = *syncEvents.pendtail; QdEventPtr qe; xEvent *qxE; NoticeTime(xE); #ifdef XKB /* Fix for key repeating bug. */ if (device->key != NULL && device->key->xkbInfo != NULL && xE->u.u.type == KeyRelease) AccessXCancelRepeatKey(device->key->xkbInfo, xE->u.u.detail); #endif if (DeviceEventCallback) { DeviceEventInfoRec eventinfo; /* The RECORD spec says that the root window field of motion events * must be valid. At this point, it hasn't been filled in yet, so * we do it here. The long expression below is necessary to get * the current root window; the apparently reasonable alternative * GetCurrentRootWindow()->drawable.id doesn't give you the right * answer on the first motion event after a screen change because * the data that GetCurrentRootWindow relies on hasn't been * updated yet. */ if (xE->u.u.type == MotionNotify) XE_KBPTR.root = WindowTable[sprite.hotPhys.pScreen->myNum]->drawable.id; eventinfo.events = xE; eventinfo.count = count; CallCallbacks(&DeviceEventCallback, (pointer)&eventinfo); } if (xE->u.u.type == MotionNotify) { #ifdef PANORAMIX if(!noPanoramiXExtension) { XE_KBPTR.rootX += panoramiXdataPtr[sprite.screen->myNum].x - panoramiXdataPtr[0].x; XE_KBPTR.rootY += panoramiXdataPtr[sprite.screen->myNum].y - panoramiXdataPtr[0].y; } #endif sprite.hotPhys.x = XE_KBPTR.rootX; sprite.hotPhys.y = XE_KBPTR.rootY; /* do motion compression, but not if from different devices */ if (tail && (tail->event->u.u.type == MotionNotify) && (tail->device == device) && (tail->pScreen == sprite.hotPhys.pScreen)) { tail->event->u.keyButtonPointer.rootX = sprite.hotPhys.x; tail->event->u.keyButtonPointer.rootY = sprite.hotPhys.y; tail->event->u.keyButtonPointer.time = XE_KBPTR.time; tail->months = currentTime.months; return; } } qe = (QdEventPtr)xalloc(sizeof(QdEventRec) + (count * sizeof(xEvent))); if (!qe) return; qe->next = (QdEventPtr)NULL; qe->device = device; qe->pScreen = sprite.hotPhys.pScreen; qe->months = currentTime.months; qe->event = (xEvent *)(qe + 1); qe->evcount = count; for (qxE = qe->event; --count >= 0; qxE++, xE++) *qxE = *xE; if (tail) syncEvents.pendtail = &tail->next; *syncEvents.pendtail = qe; } static void PlayReleasedEvents(void) { QdEventPtr *prev, qe; DeviceIntPtr dev; prev = &syncEvents.pending; while ( (qe = *prev) ) { if (!qe->device->sync.frozen) { *prev = qe->next; if (*syncEvents.pendtail == *prev) syncEvents.pendtail = prev; if (qe->event->u.u.type == MotionNotify) CheckVirtualMotion(qe, NullWindow); syncEvents.time.months = qe->months; syncEvents.time.milliseconds = qe->event->u.keyButtonPointer.time; #ifdef PANORAMIX /* Translate back to the sprite screen since processInputProc will translate from sprite screen to screen 0 upon reentry to the DIX layer */ if(!noPanoramiXExtension) { qe->event->u.keyButtonPointer.rootX += panoramiXdataPtr[0].x - panoramiXdataPtr[sprite.screen->myNum].x; qe->event->u.keyButtonPointer.rootY += panoramiXdataPtr[0].y - panoramiXdataPtr[sprite.screen->myNum].y; } #endif (*qe->device->public.processInputProc)(qe->event, qe->device, qe->evcount); xfree(qe); for (dev = inputInfo.devices; dev && dev->sync.frozen; dev = dev->next) ; if (!dev) break; /* Playing the event may have unfrozen another device. */ /* So to play it safe, restart at the head of the queue */ prev = &syncEvents.pending; } else prev = &qe->next; } } static void FreezeThaw(DeviceIntPtr dev, Bool frozen) { dev->sync.frozen = frozen; if (frozen) dev->public.processInputProc = dev->public.enqueueInputProc; else dev->public.processInputProc = dev->public.realInputProc; } void ComputeFreezes(void) { DeviceIntPtr replayDev = syncEvents.replayDev; int i; WindowPtr w; xEvent *xE; int count; GrabPtr grab; DeviceIntPtr dev; for (dev = inputInfo.devices; dev; dev = dev->next) FreezeThaw(dev, dev->sync.other || (dev->sync.state >= FROZEN)); if (syncEvents.playingEvents || (!replayDev && !syncEvents.pending)) return; syncEvents.playingEvents = TRUE; if (replayDev) { xE = replayDev->sync.event; count = replayDev->sync.evcount; syncEvents.replayDev = (DeviceIntPtr)NULL; w = XYToWindow( XE_KBPTR.rootX, XE_KBPTR.rootY); for (i = 0; i < spriteTraceGood; i++) { if (syncEvents.replayWin == spriteTrace[i]) { if (!CheckDeviceGrabs(replayDev, xE, i+1, count)) { if (replayDev->focus) DeliverFocusedEvent(replayDev, xE, w, count); else DeliverDeviceEvents(w, xE, NullGrab, NullWindow, replayDev, count); } goto playmore; } } /* must not still be in the same stack */ if (replayDev->focus) DeliverFocusedEvent(replayDev, xE, w, count); else DeliverDeviceEvents(w, xE, NullGrab, NullWindow, replayDev, count); } playmore: for (dev = inputInfo.devices; dev; dev = dev->next) { if (!dev->sync.frozen) { PlayReleasedEvents(); break; } } syncEvents.playingEvents = FALSE; /* the following may have been skipped during replay, so do it now */ if ((grab = inputInfo.pointer->grab) && grab->confineTo) { if (grab->confineTo->drawable.pScreen != sprite.hotPhys.pScreen) sprite.hotPhys.x = sprite.hotPhys.y = 0; ConfineCursorToWindow(grab->confineTo, TRUE, TRUE); } else ConfineCursorToWindow(WindowTable[sprite.hotPhys.pScreen->myNum], TRUE, FALSE); PostNewCursor(); } #ifdef RANDR void ScreenRestructured (ScreenPtr pScreen) { GrabPtr grab; if ((grab = inputInfo.pointer->grab) && grab->confineTo) { if (grab->confineTo->drawable.pScreen != sprite.hotPhys.pScreen) sprite.hotPhys.x = sprite.hotPhys.y = 0; ConfineCursorToWindow(grab->confineTo, TRUE, TRUE); } else ConfineCursorToWindow(WindowTable[sprite.hotPhys.pScreen->myNum], TRUE, FALSE); } #endif void CheckGrabForSyncs(DeviceIntPtr thisDev, Bool thisMode, Bool otherMode) { GrabPtr grab = thisDev->grab; DeviceIntPtr dev; if (thisMode == GrabModeSync) thisDev->sync.state = FROZEN_NO_EVENT; else { /* free both if same client owns both */ thisDev->sync.state = THAWED; if (thisDev->sync.other && (CLIENT_BITS(thisDev->sync.other->resource) == CLIENT_BITS(grab->resource))) thisDev->sync.other = NullGrab; } for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev != thisDev) { if (otherMode == GrabModeSync) dev->sync.other = grab; else { /* free both if same client owns both */ if (dev->sync.other && (CLIENT_BITS(dev->sync.other->resource) == CLIENT_BITS(grab->resource))) dev->sync.other = NullGrab; } } } ComputeFreezes(); } /** * Activate a pointer grab on the given device. A pointer grab will cause all * core pointer events to be delivered to the grabbing client only. Can cause * the cursor to change if a grab cursor is set. * * As a pointer grab can only be issued on the core devices, mouse is always * inputInfo.pointer. Extension devices are set up for ActivateKeyboardGrab(). * * @param mouse The device to grab. * @param grab The grab structure, needs to be setup. * @param autoGrab True if the grab was caused by a button down event and not * explicitely by a client. */ void ActivatePointerGrab(DeviceIntPtr mouse, GrabPtr grab, TimeStamp time, Bool autoGrab) { WindowPtr oldWin = (mouse->grab) ? mouse->grab->window : sprite.win; if (grab->confineTo) { if (grab->confineTo->drawable.pScreen != sprite.hotPhys.pScreen) sprite.hotPhys.x = sprite.hotPhys.y = 0; ConfineCursorToWindow(grab->confineTo, FALSE, TRUE); } DoEnterLeaveEvents(oldWin, grab->window, NotifyGrab); mouse->valuator->motionHintWindow = NullWindow; if (syncEvents.playingEvents) mouse->grabTime = syncEvents.time; else mouse->grabTime = time; if (grab->cursor) grab->cursor->refcnt++; mouse->activeGrab = *grab; mouse->grab = &mouse->activeGrab; mouse->fromPassiveGrab = autoGrab; PostNewCursor(); CheckGrabForSyncs(mouse,(Bool)grab->pointerMode, (Bool)grab->keyboardMode); } /** * Delete grab on given device, update the sprite. * * As a pointer grab can only be issued on the core devices, mouse is always * inputInfo.pointer. Extension devices are set up for ActivateKeyboardGrab(). */ void DeactivatePointerGrab(DeviceIntPtr mouse) { GrabPtr grab = mouse->grab; DeviceIntPtr dev; mouse->valuator->motionHintWindow = NullWindow; mouse->grab = NullGrab; mouse->sync.state = NOT_GRABBED; mouse->fromPassiveGrab = FALSE; for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev->sync.other == grab) dev->sync.other = NullGrab; } DoEnterLeaveEvents(grab->window, sprite.win, NotifyUngrab); if (grab->confineTo) ConfineCursorToWindow(ROOT, FALSE, FALSE); PostNewCursor(); if (grab->cursor) FreeCursor(grab->cursor, (Cursor)0); ComputeFreezes(); } /** * Activate a keyboard grab on the given device. * * Extension devices have ActivateKeyboardGrab() set as their grabbing proc. */ void ActivateKeyboardGrab(DeviceIntPtr keybd, GrabPtr grab, TimeStamp time, Bool passive) { WindowPtr oldWin; if (keybd->grab) oldWin = keybd->grab->window; else if (keybd->focus) oldWin = keybd->focus->win; else oldWin = sprite.win; if (oldWin == FollowKeyboardWin) oldWin = inputInfo.keyboard->focus->win; if (keybd->valuator) keybd->valuator->motionHintWindow = NullWindow; DoFocusEvents(keybd, oldWin, grab->window, NotifyGrab); if (syncEvents.playingEvents) keybd->grabTime = syncEvents.time; else keybd->grabTime = time; keybd->activeGrab = *grab; keybd->grab = &keybd->activeGrab; keybd->fromPassiveGrab = passive; CheckGrabForSyncs(keybd, (Bool)grab->keyboardMode, (Bool)grab->pointerMode); } /** * Delete keyboard grab for the given device. */ void DeactivateKeyboardGrab(DeviceIntPtr keybd) { GrabPtr grab = keybd->grab; DeviceIntPtr dev; WindowPtr focusWin = keybd->focus ? keybd->focus->win : sprite.win; if (focusWin == FollowKeyboardWin) focusWin = inputInfo.keyboard->focus->win; if (keybd->valuator) keybd->valuator->motionHintWindow = NullWindow; keybd->grab = NullGrab; keybd->sync.state = NOT_GRABBED; keybd->fromPassiveGrab = FALSE; for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev->sync.other == grab) dev->sync.other = NullGrab; } DoFocusEvents(keybd, grab->window, focusWin, NotifyUngrab); ComputeFreezes(); } void AllowSome(ClientPtr client, TimeStamp time, DeviceIntPtr thisDev, int newState) { Bool thisGrabbed, otherGrabbed, othersFrozen, thisSynced; TimeStamp grabTime; DeviceIntPtr dev; thisGrabbed = thisDev->grab && SameClient(thisDev->grab, client); thisSynced = FALSE; otherGrabbed = FALSE; othersFrozen = TRUE; grabTime = thisDev->grabTime; for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev == thisDev) continue; if (dev->grab && SameClient(dev->grab, client)) { if (!(thisGrabbed || otherGrabbed) || (CompareTimeStamps(dev->grabTime, grabTime) == LATER)) grabTime = dev->grabTime; otherGrabbed = TRUE; if (thisDev->sync.other == dev->grab) thisSynced = TRUE; if (dev->sync.state < FROZEN) othersFrozen = FALSE; } else if (!dev->sync.other || !SameClient(dev->sync.other, client)) othersFrozen = FALSE; } if (!((thisGrabbed && thisDev->sync.state >= FROZEN) || thisSynced)) return; if ((CompareTimeStamps(time, currentTime) == LATER) || (CompareTimeStamps(time, grabTime) == EARLIER)) return; switch (newState) { case THAWED: /* Async */ if (thisGrabbed) thisDev->sync.state = THAWED; if (thisSynced) thisDev->sync.other = NullGrab; ComputeFreezes(); break; case FREEZE_NEXT_EVENT: /* Sync */ if (thisGrabbed) { thisDev->sync.state = FREEZE_NEXT_EVENT; if (thisSynced) thisDev->sync.other = NullGrab; ComputeFreezes(); } break; case THAWED_BOTH: /* AsyncBoth */ if (othersFrozen) { for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev->grab && SameClient(dev->grab, client)) dev->sync.state = THAWED; if (dev->sync.other && SameClient(dev->sync.other, client)) dev->sync.other = NullGrab; } ComputeFreezes(); } break; case FREEZE_BOTH_NEXT_EVENT: /* SyncBoth */ if (othersFrozen) { for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev->grab && SameClient(dev->grab, client)) dev->sync.state = FREEZE_BOTH_NEXT_EVENT; if (dev->sync.other && SameClient(dev->sync.other, client)) dev->sync.other = NullGrab; } ComputeFreezes(); } break; case NOT_GRABBED: /* Replay */ if (thisGrabbed && thisDev->sync.state == FROZEN_WITH_EVENT) { if (thisSynced) thisDev->sync.other = NullGrab; syncEvents.replayDev = thisDev; syncEvents.replayWin = thisDev->grab->window; (*thisDev->DeactivateGrab)(thisDev); syncEvents.replayDev = (DeviceIntPtr)NULL; } break; case THAW_OTHERS: /* AsyncOthers */ if (othersFrozen) { for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev == thisDev) continue; if (dev->grab && SameClient(dev->grab, client)) dev->sync.state = THAWED; if (dev->sync.other && SameClient(dev->sync.other, client)) dev->sync.other = NullGrab; } ComputeFreezes(); } break; } } /** * Server-side protocol handling for AllowEvents request. * * Release some events from a frozen device. Only applicable for core devices. */ int ProcAllowEvents(ClientPtr client) { TimeStamp time; DeviceIntPtr mouse = inputInfo.pointer; DeviceIntPtr keybd = inputInfo.keyboard; REQUEST(xAllowEventsReq); REQUEST_SIZE_MATCH(xAllowEventsReq); time = ClientTimeToServerTime(stuff->time); switch (stuff->mode) { case ReplayPointer: AllowSome(client, time, mouse, NOT_GRABBED); break; case SyncPointer: AllowSome(client, time, mouse, FREEZE_NEXT_EVENT); break; case AsyncPointer: AllowSome(client, time, mouse, THAWED); break; case ReplayKeyboard: AllowSome(client, time, keybd, NOT_GRABBED); break; case SyncKeyboard: AllowSome(client, time, keybd, FREEZE_NEXT_EVENT); break; case AsyncKeyboard: AllowSome(client, time, keybd, THAWED); break; case SyncBoth: AllowSome(client, time, keybd, FREEZE_BOTH_NEXT_EVENT); break; case AsyncBoth: AllowSome(client, time, keybd, THAWED_BOTH); break; default: client->errorValue = stuff->mode; return BadValue; } return Success; } /** * Deactivate grabs from any device that has been grabbed by the client. */ void ReleaseActiveGrabs(ClientPtr client) { DeviceIntPtr dev; Bool done; /* XXX CloseDownClient should remove passive grabs before * releasing active grabs. */ do { done = TRUE; for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev->grab && SameClient(dev->grab, client)) { (*dev->DeactivateGrab)(dev); done = FALSE; } } } while (!done); } /************************************************************************** * The following procedures deal with delivering events * **************************************************************************/ /** * Deliver the given events to the given client. * * More than one event may be delivered at a time. This is the case with * DeviceMotionNotifies which may be followed by DeviceValuator events. * * TryClientEvents() is the last station before actually writing the events to * the socket. Anything that is not filtered here, will get delivered to the * client. * An event is only delivered if * - mask and filter match up. * - no other client has a grab on the device that caused the event. * * * @param client The target client to deliver to. * @param pEvents The events to be delivered. * @param count Number of elements in pEvents. * @param mask Event mask as set by the window. * @param filter Mask based on event type. * @param grab Possible grab on the device that caused the event. * * @return 1 if event was delivered, 0 if not or -1 if grab was not set by the * client. */ _X_EXPORT int TryClientEvents (ClientPtr client, xEvent *pEvents, int count, Mask mask, Mask filter, GrabPtr grab) { int i; int type; #ifdef DEBUG_EVENTS ErrorF("Event([%d, %d], mask=0x%x), client=%d", pEvents->u.u.type, pEvents->u.u.detail, mask, client->index); #endif if ((client) && (client != serverClient) && (!client->clientGone) && ((filter == CantBeFiltered) || (mask & filter))) { if (grab && !SameClient(grab, client)) return -1; /* don't send, but notify caller */ type = pEvents->u.u.type; if (type == MotionNotify) { if (mask & PointerMotionHintMask) { if (WID(inputInfo.pointer->valuator->motionHintWindow) == pEvents->u.keyButtonPointer.event) { #ifdef DEBUG_EVENTS ErrorF("\n"); ErrorF("motionHintWindow == keyButtonPointer.event\n"); #endif return 1; /* don't send, but pretend we did */ } pEvents->u.u.detail = NotifyHint; } else { pEvents->u.u.detail = NotifyNormal; } } #ifdef XINPUT else { if ((type == DeviceMotionNotify) && MaybeSendDeviceMotionNotifyHint ((deviceKeyButtonPointer*)pEvents, mask) != 0) return 1; } #endif type &= 0177; if (type != KeymapNotify) { /* all extension events must have a sequence number */ for (i = 0; i < count; i++) pEvents[i].u.u.sequenceNumber = client->sequence; } if (BitIsOn(criticalEvents, type)) { #ifdef SMART_SCHEDULE if (client->smart_priority < SMART_MAX_PRIORITY) client->smart_priority++; #endif SetCriticalOutputPending(); } WriteEventsToClient(client, count, pEvents); #ifdef DEBUG_EVENTS ErrorF( " delivered\n"); #endif return 1; } else { #ifdef DEBUG_EVENTS ErrorF("\n"); #endif return 0; } } /** * Deliver events to a window. At this point, we do not yet know if the event * actually needs to be delivered. May activate a grab if the event is a * button press. * * More than one event may be delivered at a time. This is the case with * DeviceMotionNotifies which may be followed by DeviceValuator events. * * @param pWin The window that would get the event. * @param pEvents The events to be delivered. * @param count Number of elements in pEvents. * @param filter Mask based on event type. * @param grab Possible grab on the device that caused the event. * @param mskidx Mask index, depending on device that caused event. * * @return Number of events delivered to various clients. */ int DeliverEventsToWindow(WindowPtr pWin, xEvent *pEvents, int count, Mask filter, GrabPtr grab, int mskidx) { int deliveries = 0, nondeliveries = 0; int attempt; InputClients *other; ClientPtr client = NullClient; Mask deliveryMask = 0; /* If a grab occurs due to a button press, then this mask is the mask of the grab. */ int type = pEvents->u.u.type; /* CantBeFiltered means only window owner gets the event */ if ((filter == CantBeFiltered) || !(type & EXTENSION_EVENT_BASE)) { /* if nobody ever wants to see this event, skip some work */ if (filter != CantBeFiltered && !((wOtherEventMasks(pWin)|pWin->eventMask) & filter)) return 0; if ( (attempt = TryClientEvents(wClient(pWin), pEvents, count, pWin->eventMask, filter, grab)) ) { if (attempt > 0) { deliveries++; client = wClient(pWin); deliveryMask = pWin->eventMask; } else nondeliveries--; } } if (filter != CantBeFiltered) { if (type & EXTENSION_EVENT_BASE) { OtherInputMasks *inputMasks; inputMasks = wOtherInputMasks(pWin); if (!inputMasks || !(inputMasks->inputEvents[mskidx] & filter)) return 0; other = inputMasks->inputClients; } else other = (InputClients *)wOtherClients(pWin); for (; other; other = other->next) { if ( (attempt = TryClientEvents(rClient(other), pEvents, count, other->mask[mskidx], filter, grab)) ) { if (attempt > 0) { deliveries++; client = rClient(other); deliveryMask = other->mask[mskidx]; } else nondeliveries--; } } } if ((type == ButtonPress) && deliveries && (!grab)) { GrabRec tempGrab; tempGrab.device = inputInfo.pointer; tempGrab.resource = client->clientAsMask; tempGrab.window = pWin; tempGrab.ownerEvents = (deliveryMask & OwnerGrabButtonMask) ? TRUE : FALSE; tempGrab.eventMask = deliveryMask; tempGrab.keyboardMode = GrabModeAsync; tempGrab.pointerMode = GrabModeAsync; tempGrab.confineTo = NullWindow; tempGrab.cursor = NullCursor; (*inputInfo.pointer->ActivateGrab)(inputInfo.pointer, &tempGrab, currentTime, TRUE); } else if ((type == MotionNotify) && deliveries) inputInfo.pointer->valuator->motionHintWindow = pWin; #ifdef XINPUT else { if (((type == DeviceMotionNotify) #ifdef XKB || (type == DeviceButtonPress) #endif ) && deliveries) CheckDeviceGrabAndHintWindow (pWin, type, (deviceKeyButtonPointer*) pEvents, grab, client, deliveryMask); } #endif if (deliveries) return deliveries; return nondeliveries; } /* If the event goes to dontClient, don't send it and return 0. if send works, return 1 or if send didn't work, return 2. Only works for core events. */ #ifdef PANORAMIX static int XineramaTryClientEventsResult( ClientPtr client, GrabPtr grab, Mask mask, Mask filter ){ if ((client) && (client != serverClient) && (!client->clientGone) && ((filter == CantBeFiltered) || (mask & filter))) { if (grab && !SameClient(grab, client)) return -1; else return 1; } return 0; } #endif /** * Try to deliver events to the interested parties. * * @param pWin The window that would get the event. * @param pEvents The events to be delivered. * @param count Number of elements in pEvents. * @param filter Mask based on event type. * @param dontClient Don't deliver to the dontClient. */ int MaybeDeliverEventsToClient(WindowPtr pWin, xEvent *pEvents, int count, Mask filter, ClientPtr dontClient) { OtherClients *other; if (pWin->eventMask & filter) { if (wClient(pWin) == dontClient) return 0; #ifdef PANORAMIX if(!noPanoramiXExtension && pWin->drawable.pScreen->myNum) return XineramaTryClientEventsResult( wClient(pWin), NullGrab, pWin->eventMask, filter); #endif return TryClientEvents(wClient(pWin), pEvents, count, pWin->eventMask, filter, NullGrab); } for (other = wOtherClients(pWin); other; other = other->next) { if (other->mask & filter) { if (SameClient(other, dontClient)) return 0; #ifdef PANORAMIX if(!noPanoramiXExtension && pWin->drawable.pScreen->myNum) return XineramaTryClientEventsResult( rClient(other), NullGrab, other->mask, filter); #endif return TryClientEvents(rClient(other), pEvents, count, other->mask, filter, NullGrab); } } return 2; } /** * Adjust event fields to comply with the window properties. * * @param xE Event to be modified in place * @param pWin The window to get the information from. * @param child Child window setting for event (if applicable) * @param calcChild If True, calculate the child window. */ static void FixUpEventFromWindow( xEvent *xE, WindowPtr pWin, Window child, Bool calcChild) { if (calcChild) { WindowPtr w=spriteTrace[spriteTraceGood-1]; /* If the search ends up past the root should the child field be set to none or should the value in the argument be passed through. It probably doesn't matter since everyone calls this function with child == None anyway. */ while (w) { /* If the source window is same as event window, child should be none. Don't bother going all all the way back to the root. */ if (w == pWin) { child = None; break; } if (w->parent == pWin) { child = w->drawable.id; break; } w = w->parent; } } XE_KBPTR.root = ROOT->drawable.id; XE_KBPTR.event = pWin->drawable.id; if (sprite.hot.pScreen == pWin->drawable.pScreen) { XE_KBPTR.sameScreen = xTrue; XE_KBPTR.child = child; XE_KBPTR.eventX = XE_KBPTR.rootX - pWin->drawable.x; XE_KBPTR.eventY = XE_KBPTR.rootY - pWin->drawable.y; } else { XE_KBPTR.sameScreen = xFalse; XE_KBPTR.child = None; XE_KBPTR.eventX = 0; XE_KBPTR.eventY = 0; } } /** * Deliver events caused by input devices. Called for all core input events * and XI events. No filtering of events happens before DeliverDeviceEvents(), * it will be called for any event that comes out of the event queue. * * For all core events, dev is either inputInfo.pointer or inputInfo.keyboard. * For all extension events, dev is the device that caused the event. * * @param pWin Window to deliver event to. * @param xE Events to deliver. * @param grab Possible grab on a device. * @param stopAt Don't recurse up to the root window. * @param dev The device that is responsible for the event. * @param count number of events in xE. * */ int DeliverDeviceEvents(WindowPtr pWin, xEvent *xE, GrabPtr grab, WindowPtr stopAt, DeviceIntPtr dev, int count) { Window child = None; int type = xE->u.u.type; Mask filter = filters[type]; int deliveries = 0; if (type & EXTENSION_EVENT_BASE) { OtherInputMasks *inputMasks; int mskidx = dev->id; inputMasks = wOtherInputMasks(pWin); if (inputMasks && !(filter & inputMasks->deliverableEvents[mskidx])) return 0; while (pWin) { if (inputMasks && (inputMasks->inputEvents[mskidx] & filter)) { FixUpEventFromWindow(xE, pWin, child, FALSE); deliveries = DeliverEventsToWindow(pWin, xE, count, filter, grab, mskidx); if (deliveries > 0) return deliveries; } if ((deliveries < 0) || (pWin == stopAt) || (inputMasks && (filter & inputMasks->dontPropagateMask[mskidx]))) return 0; child = pWin->drawable.id; pWin = pWin->parent; if (pWin) inputMasks = wOtherInputMasks(pWin); } } else { if (!(filter & pWin->deliverableEvents)) return 0; while (pWin) { if ((wOtherEventMasks(pWin)|pWin->eventMask) & filter) { FixUpEventFromWindow(xE, pWin, child, FALSE); deliveries = DeliverEventsToWindow(pWin, xE, count, filter, grab, 0); if (deliveries > 0) return deliveries; } if ((deliveries < 0) || (pWin == stopAt) || (filter & wDontPropagateMask(pWin))) return 0; child = pWin->drawable.id; pWin = pWin->parent; } } return 0; } /** * Deliver event to a window and it's immediate parent. Used for most window * events (CreateNotify, ConfigureNotify, etc.). Not useful for events that * propagate up the tree or extension events * * In case of a ReparentNotify event, the event will be delivered to the * otherParent as well. * * @param pWin Window to deliver events to. * @param xE Events to deliver. * @param count number of events in xE. * @param otherParent Used for ReparentNotify events. */ _X_EXPORT int DeliverEvents(WindowPtr pWin, xEvent *xE, int count, WindowPtr otherParent) { Mask filter; int deliveries; #ifdef PANORAMIX if(!noPanoramiXExtension && pWin->drawable.pScreen->myNum) return count; #endif if (!count) return 0; filter = filters[xE->u.u.type]; if ((filter & SubstructureNotifyMask) && (xE->u.u.type != CreateNotify)) xE->u.destroyNotify.event = pWin->drawable.id; if (filter != StructureAndSubMask) return DeliverEventsToWindow(pWin, xE, count, filter, NullGrab, 0); deliveries = DeliverEventsToWindow(pWin, xE, count, StructureNotifyMask, NullGrab, 0); if (pWin->parent) { xE->u.destroyNotify.event = pWin->parent->drawable.id; deliveries += DeliverEventsToWindow(pWin->parent, xE, count, SubstructureNotifyMask, NullGrab, 0); if (xE->u.u.type == ReparentNotify) { xE->u.destroyNotify.event = otherParent->drawable.id; deliveries += DeliverEventsToWindow(otherParent, xE, count, SubstructureNotifyMask, NullGrab, 0); } } return deliveries; } static Bool PointInBorderSize(WindowPtr pWin, int x, int y) { BoxRec box; if(POINT_IN_REGION(pWin->drawable.pScreen, &pWin->borderSize, x, y, &box)) return TRUE; #ifdef PANORAMIX if(!noPanoramiXExtension && XineramaSetWindowPntrs(pWin)) { int i; for(i = 1; i < PanoramiXNumScreens; i++) { if(POINT_IN_REGION(sprite.screen, &sprite.windows[i]->borderSize, x + panoramiXdataPtr[0].x - panoramiXdataPtr[i].x, y + panoramiXdataPtr[0].y - panoramiXdataPtr[i].y, &box)) return TRUE; } } #endif return FALSE; } /** * Traversed from the root window to the window at the position x/y. While * traversing, it sets up the traversal history in the spriteTrace array. * After completing, the spriteTrace history is set in the following way: * spriteTrace[0] ... root window * spriteTrace[1] ... top level window that encloses x/y * ... * spriteTrace[spriteTraceGood - 1] ... window at x/y * * @returns the window at the given coordinates. */ static WindowPtr XYToWindow(int x, int y) { WindowPtr pWin; BoxRec box; spriteTraceGood = 1; /* root window still there */ pWin = ROOT->firstChild; while (pWin) { if ((pWin->mapped) && (x >= pWin->drawable.x - wBorderWidth (pWin)) && (x < pWin->drawable.x + (int)pWin->drawable.width + wBorderWidth(pWin)) && (y >= pWin->drawable.y - wBorderWidth (pWin)) && (y < pWin->drawable.y + (int)pWin->drawable.height + wBorderWidth (pWin)) #ifdef SHAPE /* When a window is shaped, a further check * is made to see if the point is inside * borderSize */ && (!wBoundingShape(pWin) || PointInBorderSize(pWin, x, y)) && (!wInputShape(pWin) || POINT_IN_REGION(pWin->drawable.pScreen, wInputShape(pWin), x - pWin->drawable.x, y - pWin->drawable.y, &box)) #endif #ifdef ROOTLESS /* In rootless mode windows may be offscreen, even when * they're in X's stack. (E.g. if the native window system * implements some form of virtual desktop system). */ && !pWin->rootlessUnhittable #endif ) { if (spriteTraceGood >= spriteTraceSize) { spriteTraceSize += 10; Must_have_memory = TRUE; /* XXX */ spriteTrace = (WindowPtr *)xrealloc( spriteTrace, spriteTraceSize*sizeof(WindowPtr)); Must_have_memory = FALSE; /* XXX */ } spriteTrace[spriteTraceGood++] = pWin; pWin = pWin->firstChild; } else pWin = pWin->nextSib; } return spriteTrace[spriteTraceGood-1]; } /** * Update the sprite coordinates based on the event. Update the cursor * position, then update the event with the new coordinates that may have been * changed. If the window underneath the sprite has changed, change to new * cursor and send enter/leave events. */ static Bool CheckMotion(xEvent *xE) { WindowPtr prevSpriteWin = sprite.win; #ifdef PANORAMIX if(!noPanoramiXExtension) return XineramaCheckMotion(xE); #endif if (xE && !syncEvents.playingEvents) { if (sprite.hot.pScreen != sprite.hotPhys.pScreen) { sprite.hot.pScreen = sprite.hotPhys.pScreen; ROOT = WindowTable[sprite.hot.pScreen->myNum]; } sprite.hot.x = XE_KBPTR.rootX; sprite.hot.y = XE_KBPTR.rootY; if (sprite.hot.x < sprite.physLimits.x1) sprite.hot.x = sprite.physLimits.x1; else if (sprite.hot.x >= sprite.physLimits.x2) sprite.hot.x = sprite.physLimits.x2 - 1; if (sprite.hot.y < sprite.physLimits.y1) sprite.hot.y = sprite.physLimits.y1; else if (sprite.hot.y >= sprite.physLimits.y2) sprite.hot.y = sprite.physLimits.y2 - 1; #ifdef SHAPE if (sprite.hotShape) ConfineToShape(sprite.hotShape, &sprite.hot.x, &sprite.hot.y); #endif #ifdef XEVIE xeviehot.x = sprite.hot.x; xeviehot.y = sprite.hot.y; #endif sprite.hotPhys = sprite.hot; if ((sprite.hotPhys.x != XE_KBPTR.rootX) || (sprite.hotPhys.y != XE_KBPTR.rootY)) { (*sprite.hotPhys.pScreen->SetCursorPosition)( sprite.hotPhys.pScreen, sprite.hotPhys.x, sprite.hotPhys.y, FALSE); } XE_KBPTR.rootX = sprite.hot.x; XE_KBPTR.rootY = sprite.hot.y; } #ifdef XEVIE xeviewin = #endif sprite.win = XYToWindow(sprite.hot.x, sprite.hot.y); #ifdef notyet if (!(sprite.win->deliverableEvents & Motion_Filter(inputInfo.pointer->button)) !syncEvents.playingEvents) { /* XXX Do PointerNonInterestBox here */ } #endif if (sprite.win != prevSpriteWin) { if (prevSpriteWin != NullWindow) { if (!xE) UpdateCurrentTimeIf(); DoEnterLeaveEvents(prevSpriteWin, sprite.win, NotifyNormal); } PostNewCursor(); return FALSE; } return TRUE; } /** * Windows have restructured, we need to update the sprite position and the * sprite's cursor. */ _X_EXPORT void WindowsRestructured(void) { (void) CheckMotion((xEvent *)NULL); } #ifdef PANORAMIX /* This was added to support reconfiguration under Xdmx. The problem is * that if the 0th screen (i.e., WindowTable[0]) is moved to an origin * other than 0,0, the information in the private sprite structure must * be updated accordingly, or XYToWindow (and other routines) will not * compute correctly. */ void ReinitializeRootWindow(WindowPtr win, int xoff, int yoff) { GrabPtr grab; if (noPanoramiXExtension) return; sprite.hot.x -= xoff; sprite.hot.y -= yoff; sprite.hotPhys.x -= xoff; sprite.hotPhys.y -= yoff; sprite.hotLimits.x1 -= xoff; sprite.hotLimits.y1 -= yoff; sprite.hotLimits.x2 -= xoff; sprite.hotLimits.y2 -= yoff; if (REGION_NOTEMPTY(sprite.screen, &sprite.Reg1)) REGION_TRANSLATE(sprite.screen, &sprite.Reg1, xoff, yoff); if (REGION_NOTEMPTY(sprite.screen, &sprite.Reg2)) REGION_TRANSLATE(sprite.screen, &sprite.Reg2, xoff, yoff); /* FIXME: if we call ConfineCursorToWindow, must we do anything else? */ if ((grab = inputInfo.pointer->grab) && grab->confineTo) { if (grab->confineTo->drawable.pScreen != sprite.hotPhys.pScreen) sprite.hotPhys.x = sprite.hotPhys.y = 0; ConfineCursorToWindow(grab->confineTo, TRUE, TRUE); } else ConfineCursorToWindow(WindowTable[sprite.hotPhys.pScreen->myNum], TRUE, FALSE); } #endif /** * Set the given window to sane values, display the cursor in the center of * the screen. Called from main() with the root window on the first screen. */ void DefineInitialRootWindow(WindowPtr win) { ScreenPtr pScreen = win->drawable.pScreen; sprite.hotPhys.pScreen = pScreen; sprite.hotPhys.x = pScreen->width / 2; sprite.hotPhys.y = pScreen->height / 2; sprite.hot = sprite.hotPhys; sprite.hotLimits.x2 = pScreen->width; sprite.hotLimits.y2 = pScreen->height; #ifdef XEVIE xeviewin = #endif sprite.win = win; sprite.current = wCursor (win); sprite.current->refcnt++; spriteTraceGood = 1; ROOT = win; (*pScreen->CursorLimits) ( pScreen, sprite.current, &sprite.hotLimits, &sprite.physLimits); sprite.confined = FALSE; (*pScreen->ConstrainCursor) (pScreen, &sprite.physLimits); #ifndef ROOTLESS (*pScreen->SetCursorPosition) (pScreen, sprite.hot.x, sprite.hot.y, FALSE); #endif (*pScreen->DisplayCursor) (pScreen, sprite.current); #ifdef PANORAMIX if(!noPanoramiXExtension) { sprite.hotLimits.x1 = -panoramiXdataPtr[0].x; sprite.hotLimits.y1 = -panoramiXdataPtr[0].y; sprite.hotLimits.x2 = PanoramiXPixWidth - panoramiXdataPtr[0].x; sprite.hotLimits.y2 = PanoramiXPixHeight - panoramiXdataPtr[0].y; sprite.physLimits = sprite.hotLimits; sprite.confineWin = NullWindow; #ifdef SHAPE sprite.hotShape = NullRegion; #endif sprite.screen = pScreen; /* gotta UNINIT these someplace */ REGION_NULL(pScreen, &sprite.Reg1); REGION_NULL(pScreen, &sprite.Reg2); } #endif } /* * This does not take any shortcuts, and even ignores its argument, since * it does not happen very often, and one has to walk up the tree since * this might be a newly instantiated cursor for an intermediate window * between the one the pointer is in and the one that the last cursor was * instantiated from. */ void WindowHasNewCursor(WindowPtr pWin) { PostNewCursor(); } #ifdef XQUARTZ #include extern char *__crashreporter_info__; extern const char *__crashreporter_info__base; extern int __crashreporter_info__len; #endif _X_EXPORT void NewCurrentScreen(ScreenPtr newScreen, int x, int y) { sprite.hotPhys.x = x; sprite.hotPhys.y = y; #ifdef PANORAMIX if(!noPanoramiXExtension) { sprite.hotPhys.x += panoramiXdataPtr[newScreen->myNum].x - panoramiXdataPtr[0].x; sprite.hotPhys.y += panoramiXdataPtr[newScreen->myNum].y - panoramiXdataPtr[0].y; if (newScreen != sprite.screen) { sprite.screen = newScreen; /* Make sure we tell the DDX to update its copy of the screen */ if(sprite.confineWin) XineramaConfineCursorToWindow(sprite.confineWin, TRUE); else XineramaConfineCursorToWindow(WindowTable[0], TRUE); /* if the pointer wasn't confined, the DDX won't get told of the pointer warp so we reposition it here */ if(!syncEvents.playingEvents) (*sprite.screen->SetCursorPosition)(sprite.screen, sprite.hotPhys.x + panoramiXdataPtr[0].x - panoramiXdataPtr[sprite.screen->myNum].x, sprite.hotPhys.y + panoramiXdataPtr[0].y - panoramiXdataPtr[sprite.screen->myNum].y, FALSE); } } else #endif if (newScreen != sprite.hotPhys.pScreen) ConfineCursorToWindow(WindowTable[newScreen->myNum], TRUE, FALSE); } #ifdef PANORAMIX static Bool XineramaPointInWindowIsVisible( WindowPtr pWin, int x, int y ) { ScreenPtr pScreen = pWin->drawable.pScreen; BoxRec box; int i, xoff, yoff; if (!pWin->realized) return FALSE; if (POINT_IN_REGION(pScreen, &pWin->borderClip, x, y, &box)) return TRUE; if(!XineramaSetWindowPntrs(pWin)) return FALSE; xoff = x + panoramiXdataPtr[0].x; yoff = y + panoramiXdataPtr[0].y; for(i = 1; i < PanoramiXNumScreens; i++) { pWin = sprite.windows[i]; pScreen = pWin->drawable.pScreen; x = xoff - panoramiXdataPtr[i].x; y = yoff - panoramiXdataPtr[i].y; if(POINT_IN_REGION(pScreen, &pWin->borderClip, x, y, &box) && (!wInputShape(pWin) || POINT_IN_REGION(pWin->drawable.pScreen, wInputShape(pWin), x - pWin->drawable.x, y - pWin->drawable.y, &box))) return TRUE; } return FALSE; } static int XineramaWarpPointer(ClientPtr client) { WindowPtr dest = NULL; int x, y, rc; REQUEST(xWarpPointerReq); if (stuff->dstWid != None) { rc = dixLookupWindow(&dest, stuff->dstWid, client, DixReadAccess); if (rc != Success) return rc; } x = sprite.hotPhys.x; y = sprite.hotPhys.y; if (stuff->srcWid != None) { int winX, winY; XID winID = stuff->srcWid; WindowPtr source; rc = dixLookupWindow(&source, winID, client, DixReadAccess); if (rc != Success) return rc; winX = source->drawable.x; winY = source->drawable.y; if(source == WindowTable[0]) { winX -= panoramiXdataPtr[0].x; winY -= panoramiXdataPtr[0].y; } if (x < winX + stuff->srcX || y < winY + stuff->srcY || (stuff->srcWidth != 0 && winX + stuff->srcX + (int)stuff->srcWidth < x) || (stuff->srcHeight != 0 && winY + stuff->srcY + (int)stuff->srcHeight < y) || !XineramaPointInWindowIsVisible(source, x, y)) return Success; } if (dest) { x = dest->drawable.x; y = dest->drawable.y; if(dest == WindowTable[0]) { x -= panoramiXdataPtr[0].x; y -= panoramiXdataPtr[0].y; } } x += stuff->dstX; y += stuff->dstY; if (x < sprite.physLimits.x1) x = sprite.physLimits.x1; else if (x >= sprite.physLimits.x2) x = sprite.physLimits.x2 - 1; if (y < sprite.physLimits.y1) y = sprite.physLimits.y1; else if (y >= sprite.physLimits.y2) y = sprite.physLimits.y2 - 1; if (sprite.hotShape) ConfineToShape(sprite.hotShape, &x, &y); XineramaSetCursorPosition(x, y, TRUE); return Success; } #endif /** * Server-side protocol handling for WarpPointer request. * Warps the cursor position to the coordinates given in the request. */ int ProcWarpPointer(ClientPtr client) { WindowPtr dest = NULL; int x, y, rc; ScreenPtr newScreen; REQUEST(xWarpPointerReq); REQUEST_SIZE_MATCH(xWarpPointerReq); #ifdef PANORAMIX if(!noPanoramiXExtension) return XineramaWarpPointer(client); #endif if (stuff->dstWid != None) { rc = dixLookupWindow(&dest, stuff->dstWid, client, DixReadAccess); if (rc != Success) return rc; } x = sprite.hotPhys.x; y = sprite.hotPhys.y; if (stuff->srcWid != None) { int winX, winY; XID winID = stuff->srcWid; WindowPtr source; rc = dixLookupWindow(&source, winID, client, DixReadAccess); if (rc != Success) return rc; winX = source->drawable.x; winY = source->drawable.y; if (source->drawable.pScreen != sprite.hotPhys.pScreen || x < winX + stuff->srcX || y < winY + stuff->srcY || (stuff->srcWidth != 0 && winX + stuff->srcX + (int)stuff->srcWidth < x) || (stuff->srcHeight != 0 && winY + stuff->srcY + (int)stuff->srcHeight < y) || !PointInWindowIsVisible(source, x, y)) return Success; } if (dest) { x = dest->drawable.x; y = dest->drawable.y; newScreen = dest->drawable.pScreen; } else newScreen = sprite.hotPhys.pScreen; x += stuff->dstX; y += stuff->dstY; if (x < 0) x = 0; else if (x >= newScreen->width) x = newScreen->width - 1; if (y < 0) y = 0; else if (y >= newScreen->height) y = newScreen->height - 1; if (newScreen == sprite.hotPhys.pScreen) { if (x < sprite.physLimits.x1) x = sprite.physLimits.x1; else if (x >= sprite.physLimits.x2) x = sprite.physLimits.x2 - 1; if (y < sprite.physLimits.y1) y = sprite.physLimits.y1; else if (y >= sprite.physLimits.y2) y = sprite.physLimits.y2 - 1; #if defined(SHAPE) if (sprite.hotShape) ConfineToShape(sprite.hotShape, &x, &y); #endif (*newScreen->SetCursorPosition)(newScreen, x, y, TRUE); } else if (!PointerConfinedToScreen()) { NewCurrentScreen(newScreen, x, y); } return Success; } static Bool BorderSizeNotEmpty(WindowPtr pWin) { if(REGION_NOTEMPTY(sprite.hotPhys.pScreen, &pWin->borderSize)) return TRUE; #ifdef PANORAMIX if(!noPanoramiXExtension && XineramaSetWindowPntrs(pWin)) { int i; for(i = 1; i < PanoramiXNumScreens; i++) { if(REGION_NOTEMPTY(sprite.screen, &sprite.windows[i]->borderSize)) return TRUE; } } #endif return FALSE; } /** * "CheckPassiveGrabsOnWindow" checks to see if the event passed in causes a * passive grab set on the window to be activated. * If a passive grab is activated, the event will be delivered to the client. * * @param pWin The window that may be subject to a passive grab. * @param device Device that caused the event. * @param xE List of events (multiple ones for DeviceMotionNotify) * @count number of elements in xE. */ static Bool CheckPassiveGrabsOnWindow( WindowPtr pWin, DeviceIntPtr device, xEvent *xE, int count) { GrabPtr grab = wPassiveGrabs(pWin); GrabRec tempGrab; xEvent *dxE; if (!grab) return FALSE; tempGrab.window = pWin; tempGrab.device = device; tempGrab.type = xE->u.u.type; tempGrab.detail.exact = xE->u.u.detail; tempGrab.detail.pMask = NULL; tempGrab.modifiersDetail.pMask = NULL; for (; grab; grab = grab->next) { #ifdef XKB DeviceIntPtr gdev; XkbSrvInfoPtr xkbi; gdev= grab->modifierDevice; xkbi= gdev->key->xkbInfo; #endif tempGrab.modifierDevice = grab->modifierDevice; if ((device == grab->modifierDevice) && ((xE->u.u.type == KeyPress) #if defined(XINPUT) && defined(XKB) || (xE->u.u.type == DeviceKeyPress) #endif )) tempGrab.modifiersDetail.exact = #ifdef XKB (noXkbExtension?gdev->key->prev_state:xkbi->state.grab_mods); #else grab->modifierDevice->key->prev_state; #endif else tempGrab.modifiersDetail.exact = #ifdef XKB (noXkbExtension ? gdev->key->state : xkbi->state.grab_mods); #else grab->modifierDevice->key->state; #endif if (GrabMatchesSecond(&tempGrab, grab) && (!grab->confineTo || (grab->confineTo->realized && BorderSizeNotEmpty(grab->confineTo)))) { if (!XaceHook(XACE_DEVICE_ACCESS, wClient(pWin), device, FALSE)) return FALSE; #ifdef XKB if (!noXkbExtension) { XE_KBPTR.state &= 0x1f00; XE_KBPTR.state |= tempGrab.modifiersDetail.exact&(~0x1f00); } #endif (*device->ActivateGrab)(device, grab, currentTime, TRUE); FixUpEventFromWindow(xE, grab->window, None, TRUE); (void) TryClientEvents(rClient(grab), xE, count, filters[xE->u.u.type], filters[xE->u.u.type], grab); if (device->sync.state == FROZEN_NO_EVENT) { if (device->sync.evcount < count) { Must_have_memory = TRUE; /* XXX */ device->sync.event = (xEvent *)xrealloc(device->sync.event, count* sizeof(xEvent)); Must_have_memory = FALSE; /* XXX */ } device->sync.evcount = count; for (dxE = device->sync.event; --count >= 0; dxE++, xE++) *dxE = *xE; device->sync.state = FROZEN_WITH_EVENT; } return TRUE; } } return FALSE; } /** * CheckDeviceGrabs handles both keyboard and pointer events that may cause * a passive grab to be activated. * * If the event is a keyboard event, the ancestors of the focus window are * traced down and tried to see if they have any passive grabs to be * activated. If the focus window itself is reached and it's descendants * contain the pointer, the ancestors of the window that the pointer is in * are then traced down starting at the focus window, otherwise no grabs are * activated. * If the event is a pointer event, the ancestors of the window that the * pointer is in are traced down starting at the root until CheckPassiveGrabs * causes a passive grab to activate or all the windows are * tried. PRH * * If a grab is activated, the event has been sent to the client already! * * @param device The device that caused the event. * @param xE The event to handle (most likely {Device}ButtonPress). * @param count Number of events in list. * @return TRUE if a grab has been activated or false otherwise. */ Bool CheckDeviceGrabs(DeviceIntPtr device, xEvent *xE, int checkFirst, int count) { int i; WindowPtr pWin = NULL; FocusClassPtr focus = device->focus; if (((xE->u.u.type == ButtonPress) #if defined(XINPUT) && defined(XKB) || (xE->u.u.type == DeviceButtonPress) #endif ) && (device->button->buttonsDown != 1)) return FALSE; i = checkFirst; if (focus) { for (; i < focus->traceGood; i++) { pWin = focus->trace[i]; if (pWin->optional && CheckPassiveGrabsOnWindow(pWin, device, xE, count)) return TRUE; } if ((focus->win == NoneWin) || (i >= spriteTraceGood) || ((i > checkFirst) && (pWin != spriteTrace[i-1]))) return FALSE; } for (; i < spriteTraceGood; i++) { pWin = spriteTrace[i]; if (pWin->optional && CheckPassiveGrabsOnWindow(pWin, device, xE, count)) return TRUE; } return FALSE; } /** * Called for keyboard events to deliver event to whatever client owns the * focus. Event is delivered to the keyboard's focus window, the root window * or to the window owning the input focus. * * @param keybd The keyboard originating the event. * @param xE The event list. * @param window Window underneath the sprite. * @param count number of events in xE. */ void DeliverFocusedEvent(DeviceIntPtr keybd, xEvent *xE, WindowPtr window, int count) { WindowPtr focus = keybd->focus->win; int mskidx = 0; if (focus == FollowKeyboardWin) focus = inputInfo.keyboard->focus->win; if (!focus) return; if (focus == PointerRootWin) { DeliverDeviceEvents(window, xE, NullGrab, NullWindow, keybd, count); return; } if ((focus == window) || IsParent(focus, window)) { if (DeliverDeviceEvents(window, xE, NullGrab, focus, keybd, count)) return; } /* just deliver it to the focus window */ FixUpEventFromWindow(xE, focus, None, FALSE); if (xE->u.u.type & EXTENSION_EVENT_BASE) mskidx = keybd->id; (void)DeliverEventsToWindow(focus, xE, count, filters[xE->u.u.type], NullGrab, mskidx); } /** * Deliver an event from a device that is currently grabbed. Uses * DeliverDeviceEvents() for further delivery if a ownerEvents is set on the * grab. If not, TryClientEvents() is used. * * @param deactivateGrab True if the device's grab should be deactivated. */ void DeliverGrabbedEvent(xEvent *xE, DeviceIntPtr thisDev, Bool deactivateGrab, int count) { GrabPtr grab = thisDev->grab; int deliveries = 0; DeviceIntPtr dev; xEvent *dxE; if (grab->ownerEvents) { WindowPtr focus; if (thisDev->focus) { focus = thisDev->focus->win; if (focus == FollowKeyboardWin) focus = inputInfo.keyboard->focus->win; } else focus = PointerRootWin; if (focus == PointerRootWin) deliveries = DeliverDeviceEvents(sprite.win, xE, grab, NullWindow, thisDev, count); else if (focus && (focus == sprite.win || IsParent(focus, sprite.win))) deliveries = DeliverDeviceEvents(sprite.win, xE, grab, focus, thisDev, count); else if (focus) deliveries = DeliverDeviceEvents(focus, xE, grab, focus, thisDev, count); } if (!deliveries) { FixUpEventFromWindow(xE, grab->window, None, TRUE); deliveries = TryClientEvents(rClient(grab), xE, count, (Mask)grab->eventMask, filters[xE->u.u.type], grab); if (deliveries && (xE->u.u.type == MotionNotify #ifdef XINPUT || xE->u.u.type == DeviceMotionNotify #endif )) thisDev->valuator->motionHintWindow = grab->window; } if (deliveries && !deactivateGrab && (xE->u.u.type != MotionNotify #ifdef XINPUT && xE->u.u.type != DeviceMotionNotify #endif )) switch (thisDev->sync.state) { case FREEZE_BOTH_NEXT_EVENT: for (dev = inputInfo.devices; dev; dev = dev->next) { if (dev == thisDev) continue; FreezeThaw(dev, TRUE); if ((dev->sync.state == FREEZE_BOTH_NEXT_EVENT) && (CLIENT_BITS(dev->grab->resource) == CLIENT_BITS(thisDev->grab->resource))) dev->sync.state = FROZEN_NO_EVENT; else dev->sync.other = thisDev->grab; } /* fall through */ case FREEZE_NEXT_EVENT: thisDev->sync.state = FROZEN_WITH_EVENT; FreezeThaw(thisDev, TRUE); if (thisDev->sync.evcount < count) { Must_have_memory = TRUE; /* XXX */ thisDev->sync.event = (xEvent *)xrealloc(thisDev->sync.event, count*sizeof(xEvent)); Must_have_memory = FALSE; /* XXX */ } thisDev->sync.evcount = count; for (dxE = thisDev->sync.event; --count >= 0; dxE++, xE++) *dxE = *xE; break; } } /** * Main keyboard event processing function for core keyboard events. * Updates the events fields from the current pointer state and delivers the * event. * * For key events, xE will always be a single event. * * @param xE Event list * @param keybd The device that caused an event. * @param count Number of elements in xE. */ void #ifdef XKB CoreProcessKeyboardEvent (xEvent *xE, DeviceIntPtr keybd, int count) #else ProcessKeyboardEvent (xEvent *xE, DeviceIntPtr keybd, int count) #endif { int key, bit; BYTE *kptr; int i; CARD8 modifiers; CARD16 mask; GrabPtr grab = keybd->grab; Bool deactivateGrab = FALSE; KeyClassPtr keyc = keybd->key; #ifdef XEVIE static Window rootWin = 0; if(!xeviegrabState && xevieFlag && clients[xevieClientIndex] && (xevieMask & xevieFilters[xE->u.u.type])) { key = xE->u.u.detail; kptr = &keyc->down[key >> 3]; bit = 1 << (key & 7); if((xE->u.u.type == KeyPress && (*kptr & bit)) || (xE->u.u.type == KeyRelease && !(*kptr & bit))) {} else { #ifdef XKB if(!noXkbExtension) xevieKBEventSent = 1; #endif if(!xevieKBEventSent) { xeviekb = keybd; if(!rootWin) { rootWin = GetCurrentRootWindow()->drawable.id; } xE->u.keyButtonPointer.event = xeviewin->drawable.id; xE->u.keyButtonPointer.root = rootWin; xE->u.keyButtonPointer.child = (xeviewin->firstChild) ? xeviewin->firstChild-> drawable.id:0; xE->u.keyButtonPointer.rootX = xeviehot.x; xE->u.keyButtonPointer.rootY = xeviehot.y; xE->u.keyButtonPointer.state = keyc->state; WriteToClient(clients[xevieClientIndex], sizeof(xEvent), (char *)xE); #ifdef XKB if(noXkbExtension) #endif return; } else { xevieKBEventSent = 0; } } } #endif if (!syncEvents.playingEvents) { NoticeTime(xE); if (DeviceEventCallback) { DeviceEventInfoRec eventinfo; eventinfo.events = xE; eventinfo.count = count; CallCallbacks(&DeviceEventCallback, (pointer)&eventinfo); } } #ifdef XEVIE /* fix for bug5094030: don't change the state bit if the event is from XEvIE client */ if(!(!xeviegrabState && xevieFlag && clients[xevieClientIndex] && (xevieMask & xevieFilters[xE->u.u.type] #ifdef XKB && !noXkbExtension #endif ))) #endif XE_KBPTR.state = (keyc->state | inputInfo.pointer->button->state); XE_KBPTR.rootX = sprite.hot.x; XE_KBPTR.rootY = sprite.hot.y; key = xE->u.u.detail; kptr = &keyc->down[key >> 3]; bit = 1 << (key & 7); modifiers = keyc->modifierMap[key]; #if defined(XKB) && defined(XEVIE) if(!noXkbExtension && !xeviegrabState && xevieFlag && clients[xevieClientIndex] && (xevieMask & xevieFilters[xE->u.u.type])) { switch(xE->u.u.type) { case KeyPress: *kptr &= ~bit; break; case KeyRelease: *kptr |= bit; break; } } #endif switch (xE->u.u.type) { case KeyPress: if (*kptr & bit) /* allow ddx to generate multiple downs */ { if (!modifiers) { xE->u.u.type = KeyRelease; (*keybd->public.processInputProc)(xE, keybd, count); xE->u.u.type = KeyPress; /* release can have side effects, don't fall through */ (*keybd->public.processInputProc)(xE, keybd, count); } return; } inputInfo.pointer->valuator->motionHintWindow = NullWindow; *kptr |= bit; keyc->prev_state = keyc->state; for (i = 0, mask = 1; modifiers; i++, mask <<= 1) { if (mask & modifiers) { /* This key affects modifier "i" */ keyc->modifierKeyCount[i]++; keyc->state |= mask; modifiers &= ~mask; } } if (!grab && CheckDeviceGrabs(keybd, xE, 0, count)) { keybd->activatingKey = key; return; } break; case KeyRelease: if (!(*kptr & bit)) /* guard against duplicates */ return; inputInfo.pointer->valuator->motionHintWindow = NullWindow; *kptr &= ~bit; keyc->prev_state = keyc->state; for (i = 0, mask = 1; modifiers; i++, mask <<= 1) { if (mask & modifiers) { /* This key affects modifier "i" */ if (--keyc->modifierKeyCount[i] <= 0) { keyc->state &= ~mask; keyc->modifierKeyCount[i] = 0; } modifiers &= ~mask; } } if (keybd->fromPassiveGrab && (key == keybd->activatingKey)) deactivateGrab = TRUE; break; default: FatalError("Impossible keyboard event"); } if (grab) DeliverGrabbedEvent(xE, keybd, deactivateGrab, count); else DeliverFocusedEvent(keybd, xE, sprite.win, count); if (deactivateGrab) (*keybd->DeactivateGrab)(keybd); XaceHook(XACE_KEY_AVAIL, xE, keybd, count); } #ifdef XKB /* This function is used to set the key pressed or key released state - this is only used when the pressing of keys does not cause the device's processInputProc to be called, as in for example Mouse Keys. */ void FixKeyState (xEvent *xE, DeviceIntPtr keybd) { int key, bit; BYTE *kptr; KeyClassPtr keyc = keybd->key; key = xE->u.u.detail; kptr = &keyc->down[key >> 3]; bit = 1 << (key & 7); if (((xE->u.u.type==KeyPress)||(xE->u.u.type==KeyRelease)|| (xE->u.u.type==DeviceKeyPress)||(xE->u.u.type==DeviceKeyRelease)) ) { DebugF("FixKeyState: Key %d %s\n",key, (((xE->u.u.type==KeyPress)||(xE->u.u.type==DeviceKeyPress))?"down":"up")); } if (xE->u.u.type == KeyPress || xE->u.u.type == DeviceKeyPress) *kptr |= bit; else if (xE->u.u.type == KeyRelease || xE->u.u.type == DeviceKeyRelease) *kptr &= ~bit; else FatalError("Impossible keyboard event"); } #endif /** * Main pointer event processing function for core pointer events. * For motion events: update the sprite. * For all other events: Update the event fields based on the current sprite * state. * * For core pointer events, xE will always be a single event. * * @param xE Event list * @param mouse The device that caused an event. * @param count Number of elements in xE. */ void #ifdef XKB CoreProcessPointerEvent (xEvent *xE, DeviceIntPtr mouse, int count) #else ProcessPointerEvent (xEvent *xE, DeviceIntPtr mouse, int count) #endif { GrabPtr grab = mouse->grab; Bool deactivateGrab = FALSE; ButtonClassPtr butc = mouse->button; #ifdef XKB XkbSrvInfoPtr xkbi= inputInfo.keyboard->key->xkbInfo; #endif #ifdef XEVIE if(xevieFlag && clients[xevieClientIndex] && !xeviegrabState && (xevieMask & xevieFilters[xE->u.u.type])) { if(xevieEventSent) xevieEventSent = 0; else { xeviemouse = mouse; WriteToClient(clients[xevieClientIndex], sizeof(xEvent), (char *)xE); return; } } #endif if (!syncEvents.playingEvents) NoticeTime(xE) XE_KBPTR.state = (butc->state | ( #ifdef XKB (noXkbExtension ? inputInfo.keyboard->key->state : xkbi->state.grab_mods) #else inputInfo.keyboard->key->state #endif )); { NoticeTime(xE); if (DeviceEventCallback) { DeviceEventInfoRec eventinfo; /* see comment in EnqueueEvents regarding the next three lines */ if (xE->u.u.type == MotionNotify) XE_KBPTR.root = WindowTable[sprite.hotPhys.pScreen->myNum]->drawable.id; eventinfo.events = xE; eventinfo.count = count; CallCallbacks(&DeviceEventCallback, (pointer)&eventinfo); } } if (xE->u.u.type != MotionNotify) { int key; BYTE *kptr; int bit; XE_KBPTR.rootX = sprite.hot.x; XE_KBPTR.rootY = sprite.hot.y; key = xE->u.u.detail; kptr = &butc->down[key >> 3]; bit = 1 << (key & 7); switch (xE->u.u.type) { case ButtonPress: mouse->valuator->motionHintWindow = NullWindow; if (!(*kptr & bit)) butc->buttonsDown++; butc->motionMask = ButtonMotionMask; *kptr |= bit; if (xE->u.u.detail == 0) return; if (xE->u.u.detail <= 5) butc->state |= (Button1Mask >> 1) << xE->u.u.detail; filters[MotionNotify] = Motion_Filter(butc); if (!grab) if (CheckDeviceGrabs(mouse, xE, 0, count)) return; break; case ButtonRelease: mouse->valuator->motionHintWindow = NullWindow; if (*kptr & bit) --butc->buttonsDown; if (!butc->buttonsDown) butc->motionMask = 0; *kptr &= ~bit; if (xE->u.u.detail == 0) return; if (xE->u.u.detail <= 5) butc->state &= ~((Button1Mask >> 1) << xE->u.u.detail); filters[MotionNotify] = Motion_Filter(butc); if (!butc->state && mouse->fromPassiveGrab) deactivateGrab = TRUE; break; default: FatalError("bogus pointer event from ddx"); } } else if (!CheckMotion(xE)) return; if (grab) DeliverGrabbedEvent(xE, mouse, deactivateGrab, count); else DeliverDeviceEvents(sprite.win, xE, NullGrab, NullWindow, mouse, count); if (deactivateGrab) (*mouse->DeactivateGrab)(mouse); } #define AtMostOneClient \ (SubstructureRedirectMask | ResizeRedirectMask | ButtonPressMask) /** * Recalculate which events may be deliverable for the given window. * Recalculated mask is used for quicker determination which events may be * delivered to a window. * * The otherEventMasks on a WindowOptional is the combination of all event * masks set by all clients on the window. * deliverableEventMask is the combination of the eventMask and the * otherEventMask. * * Traverses to siblings and parents of the window. */ void RecalculateDeliverableEvents(pWin) WindowPtr pWin; { OtherClients *others; WindowPtr pChild; pChild = pWin; while (1) { if (pChild->optional) { pChild->optional->otherEventMasks = 0; for (others = wOtherClients(pChild); others; others = others->next) { pChild->optional->otherEventMasks |= others->mask; } } pChild->deliverableEvents = pChild->eventMask| wOtherEventMasks(pChild); if (pChild->parent) pChild->deliverableEvents |= (pChild->parent->deliverableEvents & ~wDontPropagateMask(pChild) & PropagateMask); if (pChild->firstChild) { pChild = pChild->firstChild; continue; } while (!pChild->nextSib && (pChild != pWin)) pChild = pChild->parent; if (pChild == pWin) break; pChild = pChild->nextSib; } } /** * * \param value must conform to DeleteType */ int OtherClientGone(pointer value, XID id) { OtherClientsPtr other, prev; WindowPtr pWin = (WindowPtr)value; prev = 0; for (other = wOtherClients(pWin); other; other = other->next) { if (other->resource == id) { if (prev) prev->next = other->next; else { if (!(pWin->optional->otherClients = other->next)) CheckWindowOptionalNeed (pWin); } xfree(other); RecalculateDeliverableEvents(pWin); return(Success); } prev = other; } FatalError("client not on event list"); /*NOTREACHED*/ return -1; /* make compiler happy */ } int EventSelectForWindow(WindowPtr pWin, ClientPtr client, Mask mask) { Mask check; OtherClients * others; if (mask & ~AllEventMasks) { client->errorValue = mask; return BadValue; } check = (mask & AtMostOneClient); if (check & (pWin->eventMask|wOtherEventMasks(pWin))) { /* It is illegal for two different clients to select on any of the events for AtMostOneClient. However, it is OK, for some client to continue selecting on one of those events. */ if ((wClient(pWin) != client) && (check & pWin->eventMask)) return BadAccess; for (others = wOtherClients (pWin); others; others = others->next) { if (!SameClient(others, client) && (check & others->mask)) return BadAccess; } } if (wClient (pWin) == client) { check = pWin->eventMask; pWin->eventMask = mask; } else { for (others = wOtherClients (pWin); others; others = others->next) { if (SameClient(others, client)) { check = others->mask; if (mask == 0) { FreeResource(others->resource, RT_NONE); return Success; } else others->mask = mask; goto maskSet; } } check = 0; if (!pWin->optional && !MakeWindowOptional (pWin)) return BadAlloc; others = (OtherClients *) xalloc(sizeof(OtherClients)); if (!others) return BadAlloc; others->mask = mask; others->resource = FakeClientID(client->index); others->next = pWin->optional->otherClients; pWin->optional->otherClients = others; if (!AddResource(others->resource, RT_OTHERCLIENT, (pointer)pWin)) return BadAlloc; } maskSet: if ((inputInfo.pointer->valuator->motionHintWindow == pWin) && (mask & PointerMotionHintMask) && !(check & PointerMotionHintMask) && !inputInfo.pointer->grab) inputInfo.pointer->valuator->motionHintWindow = NullWindow; RecalculateDeliverableEvents(pWin); return Success; } int EventSuppressForWindow(WindowPtr pWin, ClientPtr client, Mask mask, Bool *checkOptional) { int i, free; if (mask & ~PropagateMask) { client->errorValue = mask; return BadValue; } if (pWin->dontPropagate) DontPropagateRefCnts[pWin->dontPropagate]--; if (!mask) i = 0; else { for (i = DNPMCOUNT, free = 0; --i > 0; ) { if (!DontPropagateRefCnts[i]) free = i; else if (mask == DontPropagateMasks[i]) break; } if (!i && free) { i = free; DontPropagateMasks[i] = mask; } } if (i || !mask) { pWin->dontPropagate = i; if (i) DontPropagateRefCnts[i]++; if (pWin->optional) { pWin->optional->dontPropagateMask = mask; *checkOptional = TRUE; } } else { if (!pWin->optional && !MakeWindowOptional (pWin)) { if (pWin->dontPropagate) DontPropagateRefCnts[pWin->dontPropagate]++; return BadAlloc; } pWin->dontPropagate = 0; pWin->optional->dontPropagateMask = mask; } RecalculateDeliverableEvents(pWin); return Success; } /** * @return The window that is the first ancestor of both a and b. */ static WindowPtr CommonAncestor( WindowPtr a, WindowPtr b) { for (b = b->parent; b; b = b->parent) if (IsParent(b, a)) return b; return NullWindow; } /** * Assembles an EnterNotify or LeaveNotify and sends it event to the client. * The core devices are used to fill in the event fields. */ static void EnterLeaveEvent( int type, int mode, int detail, WindowPtr pWin, Window child) { xEvent event; DeviceIntPtr keybd = inputInfo.keyboard; WindowPtr focus; DeviceIntPtr mouse = inputInfo.pointer; GrabPtr grab = mouse->grab; Mask mask; if ((pWin == mouse->valuator->motionHintWindow) && (detail != NotifyInferior)) mouse->valuator->motionHintWindow = NullWindow; if (grab) { mask = (pWin == grab->window) ? grab->eventMask : 0; if (grab->ownerEvents) mask |= EventMaskForClient(pWin, rClient(grab)); } else { mask = pWin->eventMask | wOtherEventMasks(pWin); } if (mask & filters[type]) { event.u.u.type = type; event.u.u.detail = detail; event.u.enterLeave.time = currentTime.milliseconds; event.u.enterLeave.rootX = sprite.hot.x; event.u.enterLeave.rootY = sprite.hot.y; /* Counts on the same initial structure of crossing & button events! */ FixUpEventFromWindow(&event, pWin, None, FALSE); /* Enter/Leave events always set child */ event.u.enterLeave.child = child; event.u.enterLeave.flags = event.u.keyButtonPointer.sameScreen ? ELFlagSameScreen : 0; #ifdef XKB if (!noXkbExtension) { event.u.enterLeave.state = mouse->button->state & 0x1f00; event.u.enterLeave.state |= XkbGrabStateFromRec(&keybd->key->xkbInfo->state); } else #endif event.u.enterLeave.state = keybd->key->state | mouse->button->state; event.u.enterLeave.mode = mode; focus = keybd->focus->win; if ((focus != NoneWin) && ((pWin == focus) || (focus == PointerRootWin) || IsParent(focus, pWin))) event.u.enterLeave.flags |= ELFlagFocus; if (grab) (void)TryClientEvents(rClient(grab), &event, 1, mask, filters[type], grab); else (void)DeliverEventsToWindow(pWin, &event, 1, filters[type], NullGrab, 0); } if ((type == EnterNotify) && (mask & KeymapStateMask)) { xKeymapEvent ke; ClientPtr client = grab ? rClient(grab) : clients[CLIENT_ID(pWin->drawable.id)]; if (XaceHook(XACE_DEVICE_ACCESS, client, keybd, FALSE)) memmove((char *)&ke.map[0], (char *)&keybd->key->down[1], 31); else bzero((char *)&ke.map[0], 31); ke.type = KeymapNotify; if (grab) (void)TryClientEvents(rClient(grab), (xEvent *)&ke, 1, mask, KeymapStateMask, grab); else (void)DeliverEventsToWindow(pWin, (xEvent *)&ke, 1, KeymapStateMask, NullGrab, 0); } } /** * Send enter notifies to all parent windows up to ancestor. * This function recurses. */ static void EnterNotifies(WindowPtr ancestor, WindowPtr child, int mode, int detail) { WindowPtr parent = child->parent; if (ancestor == parent) return; EnterNotifies(ancestor, parent, mode, detail); EnterLeaveEvent(EnterNotify, mode, detail, parent, child->drawable.id); } /** * Send leave notifies to all parent windows up to ancestor. * This function recurses. */ static void LeaveNotifies(WindowPtr child, WindowPtr ancestor, int mode, int detail) { WindowPtr pWin; if (ancestor == child) return; for (pWin = child->parent; pWin != ancestor; pWin = pWin->parent) { EnterLeaveEvent(LeaveNotify, mode, detail, pWin, child->drawable.id); child = pWin; } } /** * Figure out if enter/leave events are necessary and send them to the * appropriate windows. * * @param fromWin Window the sprite moved out of. * @param toWin Window the sprite moved into. */ static void DoEnterLeaveEvents(WindowPtr fromWin, WindowPtr toWin, int mode) { if (fromWin == toWin) return; if (IsParent(fromWin, toWin)) { EnterLeaveEvent(LeaveNotify, mode, NotifyInferior, fromWin, None); EnterNotifies(fromWin, toWin, mode, NotifyVirtual); EnterLeaveEvent(EnterNotify, mode, NotifyAncestor, toWin, None); } else if (IsParent(toWin, fromWin)) { EnterLeaveEvent(LeaveNotify, mode, NotifyAncestor, fromWin, None); LeaveNotifies(fromWin, toWin, mode, NotifyVirtual); EnterLeaveEvent(EnterNotify, mode, NotifyInferior, toWin, None); } else { /* neither fromWin nor toWin is descendent of the other */ WindowPtr common = CommonAncestor(toWin, fromWin); /* common == NullWindow ==> different screens */ EnterLeaveEvent(LeaveNotify, mode, NotifyNonlinear, fromWin, None); LeaveNotifies(fromWin, common, mode, NotifyNonlinearVirtual); EnterNotifies(common, toWin, mode, NotifyNonlinearVirtual); EnterLeaveEvent(EnterNotify, mode, NotifyNonlinear, toWin, None); } } static void FocusEvent(DeviceIntPtr dev, int type, int mode, int detail, WindowPtr pWin) { xEvent event; #ifdef XINPUT if (dev != inputInfo.keyboard) { DeviceFocusEvent(dev, type, mode, detail, pWin); return; } #endif event.u.focus.mode = mode; event.u.u.type = type; event.u.u.detail = detail; event.u.focus.window = pWin->drawable.id; (void)DeliverEventsToWindow(pWin, &event, 1, filters[type], NullGrab, 0); if ((type == FocusIn) && ((pWin->eventMask | wOtherEventMasks(pWin)) & KeymapStateMask)) { xKeymapEvent ke; ClientPtr client = clients[CLIENT_ID(pWin->drawable.id)]; if (XaceHook(XACE_DEVICE_ACCESS, client, dev, FALSE)) memmove((char *)&ke.map[0], (char *)&dev->key->down[1], 31); else bzero((char *)&ke.map[0], 31); ke.type = KeymapNotify; (void)DeliverEventsToWindow(pWin, (xEvent *)&ke, 1, KeymapStateMask, NullGrab, 0); } } /* * recursive because it is easier * no-op if child not descended from ancestor */ static Bool FocusInEvents( DeviceIntPtr dev, WindowPtr ancestor, WindowPtr child, WindowPtr skipChild, int mode, int detail, Bool doAncestor) { if (child == NullWindow) return ancestor == NullWindow; if (ancestor == child) { if (doAncestor) FocusEvent(dev, FocusIn, mode, detail, child); return TRUE; } if (FocusInEvents(dev, ancestor, child->parent, skipChild, mode, detail, doAncestor)) { if (child != skipChild) FocusEvent(dev, FocusIn, mode, detail, child); return TRUE; } return FALSE; } /* dies horribly if ancestor is not an ancestor of child */ static void FocusOutEvents( DeviceIntPtr dev, WindowPtr child, WindowPtr ancestor, int mode, int detail, Bool doAncestor) { WindowPtr pWin; for (pWin = child; pWin != ancestor; pWin = pWin->parent) FocusEvent(dev, FocusOut, mode, detail, pWin); if (doAncestor) FocusEvent(dev, FocusOut, mode, detail, ancestor); } void DoFocusEvents(DeviceIntPtr dev, WindowPtr fromWin, WindowPtr toWin, int mode) { int out, in; /* for holding details for to/from PointerRoot/None */ int i; if (fromWin == toWin) return; out = (fromWin == NoneWin) ? NotifyDetailNone : NotifyPointerRoot; in = (toWin == NoneWin) ? NotifyDetailNone : NotifyPointerRoot; /* wrong values if neither, but then not referenced */ if ((toWin == NullWindow) || (toWin == PointerRootWin)) { if ((fromWin == NullWindow) || (fromWin == PointerRootWin)) { if (fromWin == PointerRootWin) FocusOutEvents(dev, sprite.win, ROOT, mode, NotifyPointer, TRUE); /* Notify all the roots */ #ifdef PANORAMIX if ( !noPanoramiXExtension ) FocusEvent(dev, FocusOut, mode, out, WindowTable[0]); else #endif for (i=0; iparent, NullWindow, mode, NotifyNonlinearVirtual, FALSE); } /* Notify all the roots */ #ifdef PANORAMIX if ( !noPanoramiXExtension ) FocusEvent(dev, FocusIn, mode, in, WindowTable[0]); else #endif for (i=0; iparent != NullWindow) (void)FocusInEvents(dev, ROOT, toWin, toWin, mode, NotifyNonlinearVirtual, TRUE); FocusEvent(dev, FocusIn, mode, NotifyNonlinear, toWin); if (IsParent(toWin, sprite.win)) (void)FocusInEvents(dev, toWin, sprite.win, NullWindow, mode, NotifyPointer, FALSE); } else { if (IsParent(toWin, fromWin)) { FocusEvent(dev, FocusOut, mode, NotifyAncestor, fromWin); FocusOutEvents(dev, fromWin->parent, toWin, mode, NotifyVirtual, FALSE); FocusEvent(dev, FocusIn, mode, NotifyInferior, toWin); if ((IsParent(toWin, sprite.win)) && (sprite.win != fromWin) && (!IsParent(fromWin, sprite.win)) && (!IsParent(sprite.win, fromWin))) (void)FocusInEvents(dev, toWin, sprite.win, NullWindow, mode, NotifyPointer, FALSE); } else if (IsParent(fromWin, toWin)) { if ((IsParent(fromWin, sprite.win)) && (sprite.win != fromWin) && (!IsParent(toWin, sprite.win)) && (!IsParent(sprite.win, toWin))) FocusOutEvents(dev, sprite.win, fromWin, mode, NotifyPointer, FALSE); FocusEvent(dev, FocusOut, mode, NotifyInferior, fromWin); (void)FocusInEvents(dev, fromWin, toWin, toWin, mode, NotifyVirtual, FALSE); FocusEvent(dev, FocusIn, mode, NotifyAncestor, toWin); } else { /* neither fromWin or toWin is child of other */ WindowPtr common = CommonAncestor(toWin, fromWin); /* common == NullWindow ==> different screens */ if (IsParent(fromWin, sprite.win)) FocusOutEvents(dev, sprite.win, fromWin, mode, NotifyPointer, FALSE); FocusEvent(dev, FocusOut, mode, NotifyNonlinear, fromWin); if (fromWin->parent != NullWindow) FocusOutEvents(dev, fromWin->parent, common, mode, NotifyNonlinearVirtual, FALSE); if (toWin->parent != NullWindow) (void)FocusInEvents(dev, common, toWin, toWin, mode, NotifyNonlinearVirtual, FALSE); FocusEvent(dev, FocusIn, mode, NotifyNonlinear, toWin); if (IsParent(toWin, sprite.win)) (void)FocusInEvents(dev, toWin, sprite.win, NullWindow, mode, NotifyPointer, FALSE); } } } } /** * Set the input focus to the given window. Subsequent keyboard events will be * delivered to the given window. * * Usually called from ProcSetInputFocus as result of a client request. If so, * the device is the inputInfo.keyboard. * If called from ProcXSetInputFocus as result of a client xinput request, the * device is set to the device specified by the client. * * @param client Client that requested input focus change. * @param dev Focus device. * @param focusID The window to obtain the focus. Can be PointerRoot or None. * @param revertTo Specifies where the focus reverts to when window becomes * unviewable. * @param ctime Specifies the time. * @param followOK True if pointer is allowed to follow the keyboard. */ int SetInputFocus( ClientPtr client, DeviceIntPtr dev, Window focusID, CARD8 revertTo, Time ctime, Bool followOK) { FocusClassPtr focus; WindowPtr focusWin; int mode, rc; TimeStamp time; UpdateCurrentTime(); if ((revertTo != RevertToParent) && (revertTo != RevertToPointerRoot) && (revertTo != RevertToNone) && ((revertTo != RevertToFollowKeyboard) || !followOK)) { client->errorValue = revertTo; return BadValue; } time = ClientTimeToServerTime(ctime); if ((focusID == None) || (focusID == PointerRoot)) focusWin = (WindowPtr)(long)focusID; else if ((focusID == FollowKeyboard) && followOK) focusWin = inputInfo.keyboard->focus->win; else { rc = dixLookupWindow(&focusWin, focusID, client, DixReadAccess); if (rc != Success) return rc; /* It is a match error to try to set the input focus to an unviewable window. */ if(!focusWin->realized) return(BadMatch); } focus = dev->focus; if ((CompareTimeStamps(time, currentTime) == LATER) || (CompareTimeStamps(time, focus->time) == EARLIER)) return Success; mode = (dev->grab) ? NotifyWhileGrabbed : NotifyNormal; if (focus->win == FollowKeyboardWin) DoFocusEvents(dev, inputInfo.keyboard->focus->win, focusWin, mode); else DoFocusEvents(dev, focus->win, focusWin, mode); focus->time = time; focus->revert = revertTo; if (focusID == FollowKeyboard) focus->win = FollowKeyboardWin; else focus->win = focusWin; if ((focusWin == NoneWin) || (focusWin == PointerRootWin)) focus->traceGood = 0; else { int depth = 0; WindowPtr pWin; for (pWin = focusWin; pWin; pWin = pWin->parent) depth++; if (depth > focus->traceSize) { focus->traceSize = depth+1; Must_have_memory = TRUE; /* XXX */ focus->trace = (WindowPtr *)xrealloc(focus->trace, focus->traceSize * sizeof(WindowPtr)); Must_have_memory = FALSE; /* XXX */ } focus->traceGood = depth; for (pWin = focusWin, depth--; pWin; pWin = pWin->parent, depth--) focus->trace[depth] = pWin; } return Success; } /** * Server-side protocol handling for SetInputFocus request. * * Sets the input focus for the virtual core keyboard. */ int ProcSetInputFocus(client) ClientPtr client; { REQUEST(xSetInputFocusReq); REQUEST_SIZE_MATCH(xSetInputFocusReq); if (!XaceHook(XACE_DEVICE_ACCESS, client, inputInfo.keyboard, TRUE)) return Success; return SetInputFocus(client, inputInfo.keyboard, stuff->focus, stuff->revertTo, stuff->time, FALSE); } /** * Server-side protocol handling for GetInputFocus request. * * Sends the current input focus for the virtual core keyboard back to the * client. */ int ProcGetInputFocus(ClientPtr client) { xGetInputFocusReply rep; /* REQUEST(xReq); */ FocusClassPtr focus = inputInfo.keyboard->focus; REQUEST_SIZE_MATCH(xReq); rep.type = X_Reply; rep.length = 0; rep.sequenceNumber = client->sequence; if (focus->win == NoneWin) rep.focus = None; else if (focus->win == PointerRootWin) rep.focus = PointerRoot; else rep.focus = focus->win->drawable.id; rep.revertTo = focus->revert; WriteReplyToClient(client, sizeof(xGetInputFocusReply), &rep); return Success; } /** * Server-side protocol handling for Grabpointer request. * * Sets an active grab on the inputInfo.pointer and returns success status to * client. */ int ProcGrabPointer(ClientPtr client) { xGrabPointerReply rep; DeviceIntPtr device = inputInfo.pointer; GrabPtr grab; WindowPtr pWin, confineTo; CursorPtr cursor, oldCursor; REQUEST(xGrabPointerReq); TimeStamp time; int rc; REQUEST_SIZE_MATCH(xGrabPointerReq); UpdateCurrentTime(); if ((stuff->pointerMode != GrabModeSync) && (stuff->pointerMode != GrabModeAsync)) { client->errorValue = stuff->pointerMode; return BadValue; } if ((stuff->keyboardMode != GrabModeSync) && (stuff->keyboardMode != GrabModeAsync)) { client->errorValue = stuff->keyboardMode; return BadValue; } if ((stuff->ownerEvents != xFalse) && (stuff->ownerEvents != xTrue)) { client->errorValue = stuff->ownerEvents; return BadValue; } if (stuff->eventMask & ~PointerGrabMask) { client->errorValue = stuff->eventMask; return BadValue; } rc = dixLookupWindow(&pWin, stuff->grabWindow, client, DixReadAccess); if (rc != Success) return rc; if (stuff->confineTo == None) confineTo = NullWindow; else { rc = dixLookupWindow(&confineTo, stuff->confineTo, client, DixReadAccess); if (rc != Success) return rc; } if (stuff->cursor == None) cursor = NullCursor; else { cursor = (CursorPtr)SecurityLookupIDByType(client, stuff->cursor, RT_CURSOR, DixReadAccess); if (!cursor) { client->errorValue = stuff->cursor; return BadCursor; } } /* at this point, some sort of reply is guaranteed. */ time = ClientTimeToServerTime(stuff->time); rep.type = X_Reply; rep.sequenceNumber = client->sequence; rep.length = 0; grab = device->grab; if ((grab) && !SameClient(grab, client)) rep.status = AlreadyGrabbed; else if ((!pWin->realized) || (confineTo && !(confineTo->realized && BorderSizeNotEmpty(confineTo)))) rep.status = GrabNotViewable; else if (device->sync.frozen && device->sync.other && !SameClient(device->sync.other, client)) rep.status = GrabFrozen; else if ((CompareTimeStamps(time, currentTime) == LATER) || (CompareTimeStamps(time, device->grabTime) == EARLIER)) rep.status = GrabInvalidTime; else { GrabRec tempGrab; oldCursor = NullCursor; if (grab) { if (grab->confineTo && !confineTo) ConfineCursorToWindow(ROOT, FALSE, FALSE); oldCursor = grab->cursor; } tempGrab.cursor = cursor; tempGrab.resource = client->clientAsMask; tempGrab.ownerEvents = stuff->ownerEvents; tempGrab.eventMask = stuff->eventMask; tempGrab.confineTo = confineTo; tempGrab.window = pWin; tempGrab.keyboardMode = stuff->keyboardMode; tempGrab.pointerMode = stuff->pointerMode; tempGrab.device = device; (*device->ActivateGrab)(device, &tempGrab, time, FALSE); if (oldCursor) FreeCursor (oldCursor, (Cursor)0); rep.status = GrabSuccess; } WriteReplyToClient(client, sizeof(xGrabPointerReply), &rep); return Success; } /** * Server-side protocol handling for ChangeActivePointerGrab request. * * Changes properties of the grab hold by the client. If the client does not * hold an active grab on the device, nothing happens. * * Works on the core pointer only. */ int ProcChangeActivePointerGrab(ClientPtr client) { DeviceIntPtr device = inputInfo.pointer; GrabPtr grab = device->grab; CursorPtr newCursor, oldCursor; REQUEST(xChangeActivePointerGrabReq); TimeStamp time; REQUEST_SIZE_MATCH(xChangeActivePointerGrabReq); if (stuff->eventMask & ~PointerGrabMask) { client->errorValue = stuff->eventMask; return BadValue; } if (stuff->cursor == None) newCursor = NullCursor; else { newCursor = (CursorPtr)SecurityLookupIDByType(client, stuff->cursor, RT_CURSOR, DixReadAccess); if (!newCursor) { client->errorValue = stuff->cursor; return BadCursor; } } if (!grab) return Success; if (!SameClient(grab, client)) return Success; time = ClientTimeToServerTime(stuff->time); if ((CompareTimeStamps(time, currentTime) == LATER) || (CompareTimeStamps(time, device->grabTime) == EARLIER)) return Success; oldCursor = grab->cursor; grab->cursor = newCursor; if (newCursor) newCursor->refcnt++; PostNewCursor(); if (oldCursor) FreeCursor(oldCursor, (Cursor)0); grab->eventMask = stuff->eventMask; return Success; } /** * Server-side protocol handling for UngrabPointer request. * * Deletes the pointer grab on the core pointer device. */ int ProcUngrabPointer(ClientPtr client) { DeviceIntPtr device = inputInfo.pointer; GrabPtr grab; TimeStamp time; REQUEST(xResourceReq); REQUEST_SIZE_MATCH(xResourceReq); UpdateCurrentTime(); grab = device->grab; time = ClientTimeToServerTime(stuff->id); if ((CompareTimeStamps(time, currentTime) != LATER) && (CompareTimeStamps(time, device->grabTime) != EARLIER) && (grab) && SameClient(grab, client)) (*device->DeactivateGrab)(device); return Success; } /** * Sets a grab on the given device. * * Called from ProcGrabKeyboard to work on the inputInfo.keyboard. * Called from ProcXGrabDevice to work on the device specified by the client. * * The parameters this_mode and other_mode represent the keyboard_mode and * pointer_mode parameters of XGrabKeyboard(). * See man page for details on all the parameters * * @param client Client that owns the grab. * @param dev The device to grab. * @param this_mode GrabModeSync or GrabModeAsync * @param other_mode GrabModeSync or GrabModeAsync * @param status Return code to be returned to the caller. * * @returns Success or BadValue. */ int GrabDevice(ClientPtr client, DeviceIntPtr dev, unsigned this_mode, unsigned other_mode, Window grabWindow, unsigned ownerEvents, Time ctime, Mask mask, CARD8 *status) { WindowPtr pWin; GrabPtr grab; TimeStamp time; int rc; UpdateCurrentTime(); if ((this_mode != GrabModeSync) && (this_mode != GrabModeAsync)) { client->errorValue = this_mode; return BadValue; } if ((other_mode != GrabModeSync) && (other_mode != GrabModeAsync)) { client->errorValue = other_mode; return BadValue; } if ((ownerEvents != xFalse) && (ownerEvents != xTrue)) { client->errorValue = ownerEvents; return BadValue; } rc = dixLookupWindow(&pWin, grabWindow, client, DixReadAccess); if (rc != Success) return rc; time = ClientTimeToServerTime(ctime); grab = dev->grab; if (grab && !SameClient(grab, client)) *status = AlreadyGrabbed; else if (!pWin->realized) *status = GrabNotViewable; else if ((CompareTimeStamps(time, currentTime) == LATER) || (CompareTimeStamps(time, dev->grabTime) == EARLIER)) *status = GrabInvalidTime; else if (dev->sync.frozen && dev->sync.other && !SameClient(dev->sync.other, client)) *status = GrabFrozen; else { GrabRec tempGrab; tempGrab.window = pWin; tempGrab.resource = client->clientAsMask; tempGrab.ownerEvents = ownerEvents; tempGrab.keyboardMode = this_mode; tempGrab.pointerMode = other_mode; tempGrab.eventMask = mask; tempGrab.device = dev; (*dev->ActivateGrab)(dev, &tempGrab, time, FALSE); *status = GrabSuccess; } return Success; } /** * Server-side protocol handling for GrabKeyboard request. * * Grabs the inputInfo.keyboad and returns success status to client. */ int ProcGrabKeyboard(ClientPtr client) { xGrabKeyboardReply rep; REQUEST(xGrabKeyboardReq); int result; REQUEST_SIZE_MATCH(xGrabKeyboardReq); if (XaceHook(XACE_DEVICE_ACCESS, client, inputInfo.keyboard, TRUE)) result = GrabDevice(client, inputInfo.keyboard, stuff->keyboardMode, stuff->pointerMode, stuff->grabWindow, stuff->ownerEvents, stuff->time, KeyPressMask | KeyReleaseMask, &rep.status); else { result = Success; rep.status = AlreadyGrabbed; } if (result != Success) return result; rep.type = X_Reply; rep.sequenceNumber = client->sequence; rep.length = 0; WriteReplyToClient(client, sizeof(xGrabKeyboardReply), &rep); return Success; } /** * Server-side protocol handling for UngrabKeyboard request. * * Deletes a possible grab on the inputInfo.keyboard. */ int ProcUngrabKeyboard(ClientPtr client) { DeviceIntPtr device = inputInfo.keyboard; GrabPtr grab; TimeStamp time; REQUEST(xResourceReq); REQUEST_SIZE_MATCH(xResourceReq); UpdateCurrentTime(); grab = device->grab; time = ClientTimeToServerTime(stuff->id); if ((CompareTimeStamps(time, currentTime) != LATER) && (CompareTimeStamps(time, device->grabTime) != EARLIER) && (grab) && SameClient(grab, client)) (*device->DeactivateGrab)(device); return Success; } /** * Server-side protocol handling for QueryPointer request. * * Returns the current state and position of the core pointer to the client. */ int ProcQueryPointer(ClientPtr client) { xQueryPointerReply rep; WindowPtr pWin, t; REQUEST(xResourceReq); DeviceIntPtr mouse = inputInfo.pointer; int rc; REQUEST_SIZE_MATCH(xResourceReq); rc = dixLookupWindow(&pWin, stuff->id, client, DixReadAccess); if (rc != Success) return rc; if (mouse->valuator->motionHintWindow) MaybeStopHint(mouse, client); rep.type = X_Reply; rep.sequenceNumber = client->sequence; rep.mask = mouse->button->state | inputInfo.keyboard->key->state; rep.length = 0; rep.root = (ROOT)->drawable.id; rep.rootX = sprite.hot.x; rep.rootY = sprite.hot.y; rep.child = None; if (sprite.hot.pScreen == pWin->drawable.pScreen) { rep.sameScreen = xTrue; rep.winX = sprite.hot.x - pWin->drawable.x; rep.winY = sprite.hot.y - pWin->drawable.y; for (t = sprite.win; t; t = t->parent) if (t->parent == pWin) { rep.child = t->drawable.id; break; } } else { rep.sameScreen = xFalse; rep.winX = 0; rep.winY = 0; } #ifdef PANORAMIX if(!noPanoramiXExtension) { rep.rootX += panoramiXdataPtr[0].x; rep.rootY += panoramiXdataPtr[0].y; if(stuff->id == rep.root) { rep.winX += panoramiXdataPtr[0].x; rep.winY += panoramiXdataPtr[0].y; } } #endif WriteReplyToClient(client, sizeof(xQueryPointerReply), &rep); return(Success); } /** * Initializes the device list and the DIX sprite to sane values. Allocates * trace memory used for quick window traversal. */ void InitEvents(void) { int i; sprite.hot.pScreen = sprite.hotPhys.pScreen = (ScreenPtr)NULL; inputInfo.numDevices = 0; inputInfo.devices = (DeviceIntPtr)NULL; inputInfo.off_devices = (DeviceIntPtr)NULL; inputInfo.keyboard = (DeviceIntPtr)NULL; inputInfo.pointer = (DeviceIntPtr)NULL; if (spriteTraceSize == 0) { spriteTraceSize = 32; spriteTrace = (WindowPtr *)xalloc(32*sizeof(WindowPtr)); if (!spriteTrace) FatalError("failed to allocate spriteTrace"); } spriteTraceGood = 0; lastEventMask = OwnerGrabButtonMask; filters[MotionNotify] = PointerMotionMask; #ifdef XEVIE xeviewin = #endif sprite.win = NullWindow; sprite.current = NullCursor; sprite.hotLimits.x1 = 0; sprite.hotLimits.y1 = 0; sprite.hotLimits.x2 = 0; sprite.hotLimits.y2 = 0; sprite.confined = FALSE; syncEvents.replayDev = (DeviceIntPtr)NULL; syncEvents.replayWin = NullWindow; while (syncEvents.pending) { QdEventPtr next = syncEvents.pending->next; xfree(syncEvents.pending); syncEvents.pending = next; } syncEvents.pendtail = &syncEvents.pending; syncEvents.playingEvents = FALSE; syncEvents.time.months = 0; syncEvents.time.milliseconds = 0; /* hardly matters */ currentTime.months = 0; currentTime.milliseconds = GetTimeInMillis(); lastDeviceEventTime = currentTime; for (i = 0; i < DNPMCOUNT; i++) { DontPropagateMasks[i] = 0; DontPropagateRefCnts[i] = 0; } } void CloseDownEvents(void) { xfree(spriteTrace); spriteTrace = NULL; spriteTraceSize = 0; } /** * Server-side protocol handling for SendEvent request. * * Locates the window to send the event to and forwards the event. */ int ProcSendEvent(ClientPtr client) { WindowPtr pWin; WindowPtr effectiveFocus = NullWindow; /* only set if dest==InputFocus */ REQUEST(xSendEventReq); REQUEST_SIZE_MATCH(xSendEventReq); /* The client's event type must be a core event type or one defined by an extension. */ if ( ! ((stuff->event.u.u.type > X_Reply && stuff->event.u.u.type < LASTEvent) || (stuff->event.u.u.type >= EXTENSION_EVENT_BASE && stuff->event.u.u.type < (unsigned)lastEvent))) { client->errorValue = stuff->event.u.u.type; return BadValue; } if (stuff->event.u.u.type == ClientMessage && stuff->event.u.u.detail != 8 && stuff->event.u.u.detail != 16 && stuff->event.u.u.detail != 32) { client->errorValue = stuff->event.u.u.detail; return BadValue; } if (stuff->eventMask & ~AllEventMasks) { client->errorValue = stuff->eventMask; return BadValue; } if (stuff->destination == PointerWindow) pWin = sprite.win; else if (stuff->destination == InputFocus) { WindowPtr 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 propogate up to root. */ if (inputFocus == PointerRootWin) inputFocus = ROOT; if (IsParent(inputFocus, sprite.win)) { effectiveFocus = inputFocus; pWin = sprite.win; } else effectiveFocus = pWin = inputFocus; } else dixLookupWindow(&pWin, stuff->destination, client, DixReadAccess); if (!pWin) return BadWindow; if ((stuff->propagate != xFalse) && (stuff->propagate != xTrue)) { client->errorValue = stuff->propagate; return BadValue; } stuff->event.u.u.type |= 0x80; if (stuff->propagate) { for (;pWin; pWin = pWin->parent) { if (DeliverEventsToWindow(pWin, &stuff->event, 1, stuff->eventMask, NullGrab, 0)) return Success; if (pWin == effectiveFocus) return Success; stuff->eventMask &= ~wDontPropagateMask(pWin); if (!stuff->eventMask) break; } } else (void)DeliverEventsToWindow(pWin, &stuff->event, 1, stuff->eventMask, NullGrab, 0); return Success; } /** * Server-side protocol handling for UngrabKey request. * * Deletes a passive grab for the given key. Only works on the * inputInfo.keyboard. */ int ProcUngrabKey(ClientPtr client) { REQUEST(xUngrabKeyReq); WindowPtr pWin; GrabRec tempGrab; DeviceIntPtr keybd = inputInfo.keyboard; int rc; REQUEST_SIZE_MATCH(xUngrabKeyReq); rc = dixLookupWindow(&pWin, stuff->grabWindow, client, DixReadAccess); if (rc != Success) return rc; if (((stuff->key > keybd->key->curKeySyms.maxKeyCode) || (stuff->key < keybd->key->curKeySyms.minKeyCode)) && (stuff->key != AnyKey)) { client->errorValue = stuff->key; return BadValue; } if ((stuff->modifiers != AnyModifier) && (stuff->modifiers & ~AllModifiersMask)) { client->errorValue = stuff->modifiers; return BadValue; } tempGrab.resource = client->clientAsMask; tempGrab.device = keybd; tempGrab.window = pWin; tempGrab.modifiersDetail.exact = stuff->modifiers; tempGrab.modifiersDetail.pMask = NULL; tempGrab.modifierDevice = inputInfo.keyboard; tempGrab.type = KeyPress; tempGrab.detail.exact = stuff->key; tempGrab.detail.pMask = NULL; if (!DeletePassiveGrabFromList(&tempGrab)) return(BadAlloc); return(Success); } /** * Server-side protocol handling for GrabKey request. * * Creates a grab for the inputInfo.keyboard and adds it to the list of * passive grabs. */ int ProcGrabKey(ClientPtr client) { WindowPtr pWin; REQUEST(xGrabKeyReq); GrabPtr grab; DeviceIntPtr keybd = inputInfo.keyboard; int rc; REQUEST_SIZE_MATCH(xGrabKeyReq); if ((stuff->ownerEvents != xTrue) && (stuff->ownerEvents != xFalse)) { client->errorValue = stuff->ownerEvents; return(BadValue); } if ((stuff->pointerMode != GrabModeSync) && (stuff->pointerMode != GrabModeAsync)) { client->errorValue = stuff->pointerMode; return BadValue; } if ((stuff->keyboardMode != GrabModeSync) && (stuff->keyboardMode != GrabModeAsync)) { client->errorValue = stuff->keyboardMode; return BadValue; } if (((stuff->key > keybd->key->curKeySyms.maxKeyCode) || (stuff->key < keybd->key->curKeySyms.minKeyCode)) && (stuff->key != AnyKey)) { client->errorValue = stuff->key; return BadValue; } if ((stuff->modifiers != AnyModifier) && (stuff->modifiers & ~AllModifiersMask)) { client->errorValue = stuff->modifiers; return BadValue; } rc = dixLookupWindow(&pWin, stuff->grabWindow, client, DixReadAccess); if (rc != Success) return rc; grab = CreateGrab(client->index, keybd, pWin, (Mask)(KeyPressMask | KeyReleaseMask), (Bool)stuff->ownerEvents, (Bool)stuff->keyboardMode, (Bool)stuff->pointerMode, keybd, stuff->modifiers, KeyPress, stuff->key, NullWindow, NullCursor); if (!grab) return BadAlloc; return AddPassiveGrabToList(grab); } /** * Server-side protocol handling for GrabButton request. * * Creates a grab for the inputInfo.pointer and adds it as a passive grab to * the list. */ int ProcGrabButton(ClientPtr client) { WindowPtr pWin, confineTo; REQUEST(xGrabButtonReq); CursorPtr cursor; GrabPtr grab; int rc; REQUEST_SIZE_MATCH(xGrabButtonReq); if ((stuff->pointerMode != GrabModeSync) && (stuff->pointerMode != GrabModeAsync)) { client->errorValue = stuff->pointerMode; return BadValue; } if ((stuff->keyboardMode != GrabModeSync) && (stuff->keyboardMode != GrabModeAsync)) { client->errorValue = stuff->keyboardMode; return BadValue; } if ((stuff->modifiers != AnyModifier) && (stuff->modifiers & ~AllModifiersMask)) { client->errorValue = stuff->modifiers; return BadValue; } if ((stuff->ownerEvents != xFalse) && (stuff->ownerEvents != xTrue)) { client->errorValue = stuff->ownerEvents; return BadValue; } if (stuff->eventMask & ~PointerGrabMask) { client->errorValue = stuff->eventMask; return BadValue; } rc = dixLookupWindow(&pWin, stuff->grabWindow, client, DixReadAccess); if (rc != Success) return rc; if (stuff->confineTo == None) confineTo = NullWindow; else { rc = dixLookupWindow(&confineTo, stuff->confineTo, client, DixReadAccess); if (rc != Success) return rc; } if (stuff->cursor == None) cursor = NullCursor; else { cursor = (CursorPtr)SecurityLookupIDByType(client, stuff->cursor, RT_CURSOR, DixReadAccess); if (!cursor) { client->errorValue = stuff->cursor; return BadCursor; } } grab = CreateGrab(client->index, inputInfo.pointer, pWin, (Mask)stuff->eventMask, (Bool)stuff->ownerEvents, (Bool) stuff->keyboardMode, (Bool)stuff->pointerMode, inputInfo.keyboard, stuff->modifiers, ButtonPress, stuff->button, confineTo, cursor); if (!grab) return BadAlloc; return AddPassiveGrabToList(grab); } /** * Server-side protocol handling for UngrabButton request. * * Deletes a passive grab on the inputInfo.pointer from the list. */ int ProcUngrabButton(ClientPtr client) { REQUEST(xUngrabButtonReq); WindowPtr pWin; GrabRec tempGrab; int rc; REQUEST_SIZE_MATCH(xUngrabButtonReq); if ((stuff->modifiers != AnyModifier) && (stuff->modifiers & ~AllModifiersMask)) { client->errorValue = stuff->modifiers; return BadValue; } rc = dixLookupWindow(&pWin, stuff->grabWindow, client, DixReadAccess); if (rc != Success) return rc; tempGrab.resource = client->clientAsMask; tempGrab.device = inputInfo.pointer; tempGrab.window = pWin; tempGrab.modifiersDetail.exact = stuff->modifiers; tempGrab.modifiersDetail.pMask = NULL; tempGrab.modifierDevice = inputInfo.keyboard; tempGrab.type = ButtonPress; tempGrab.detail.exact = stuff->button; tempGrab.detail.pMask = NULL; if (!DeletePassiveGrabFromList(&tempGrab)) return(BadAlloc); return(Success); } /** * Deactivate any grab that may be on the window, remove the focus. * Delete any XInput extension events from the window too. Does not change the * window mask. Use just before the window is deleted. * * If freeResources is set, passive grabs on the window are deleted. * * @param pWin The window to delete events from. * @param freeResources True if resources associated with the window should be * deleted. */ void DeleteWindowFromAnyEvents(WindowPtr pWin, Bool freeResources) { WindowPtr parent; DeviceIntPtr mouse = inputInfo.pointer; DeviceIntPtr keybd = inputInfo.keyboard; FocusClassPtr focus = keybd->focus; OtherClientsPtr oc; GrabPtr passive; /* Deactivate any grabs performed on this window, before making any input focus changes. */ if (mouse->grab && ((mouse->grab->window == pWin) || (mouse->grab->confineTo == pWin))) (*mouse->DeactivateGrab)(mouse); /* Deactivating a keyboard grab should cause focus events. */ if (keybd->grab && (keybd->grab->window == pWin)) (*keybd->DeactivateGrab)(keybd); /* If the focus window is a root window (ie. has no parent) then don't delete the focus from it. */ if ((pWin == focus->win) && (pWin->parent != NullWindow)) { int focusEventMode = NotifyNormal; /* If a grab is in progress, then alter the mode of focus events. */ if (keybd->grab) focusEventMode = NotifyWhileGrabbed; switch (focus->revert) { case RevertToNone: DoFocusEvents(keybd, pWin, NoneWin, focusEventMode); focus->win = NoneWin; focus->traceGood = 0; break; case RevertToParent: parent = pWin; do { parent = parent->parent; focus->traceGood--; } while (!parent->realized /* This would be a good protocol change -- windows being reparented during SaveSet processing would cause the focus to revert to the nearest enclosing window which will survive the death of the exiting client, instead of ending up reverting to a dying window and thence to None */ #ifdef NOTDEF || clients[CLIENT_ID(parent->drawable.id)]->clientGone #endif ); DoFocusEvents(keybd, pWin, parent, focusEventMode); focus->win = parent; focus->revert = RevertToNone; break; case RevertToPointerRoot: DoFocusEvents(keybd, pWin, PointerRootWin, focusEventMode); focus->win = PointerRootWin; focus->traceGood = 0; break; } } if (mouse->valuator->motionHintWindow == pWin) mouse->valuator->motionHintWindow = NullWindow; if (freeResources) { if (pWin->dontPropagate) DontPropagateRefCnts[pWin->dontPropagate]--; while ( (oc = wOtherClients(pWin)) ) FreeResource(oc->resource, RT_NONE); while ( (passive = wPassiveGrabs(pWin)) ) FreeResource(passive->resource, RT_NONE); } #ifdef XINPUT DeleteWindowFromAnyExtEvents(pWin, freeResources); #endif } /** * Call this whenever some window at or below pWin has changed geometry. If * there is a grab on the window, the cursor will be re-confined into the * window. */ _X_EXPORT void CheckCursorConfinement(WindowPtr pWin) { GrabPtr grab = inputInfo.pointer->grab; WindowPtr confineTo; #ifdef PANORAMIX if(!noPanoramiXExtension && pWin->drawable.pScreen->myNum) return; #endif if (grab && (confineTo = grab->confineTo)) { if (!BorderSizeNotEmpty(confineTo)) (*inputInfo.pointer->DeactivateGrab)(inputInfo.pointer); else if ((pWin == confineTo) || IsParent(pWin, confineTo)) ConfineCursorToWindow(confineTo, TRUE, TRUE); } } Mask EventMaskForClient(WindowPtr pWin, ClientPtr client) { OtherClientsPtr other; if (wClient (pWin) == client) return pWin->eventMask; for (other = wOtherClients(pWin); other; other = other->next) { if (SameClient(other, client)) return other->mask; } return 0; } /** * Server-side protocol handling for RecolorCursor request. */ int ProcRecolorCursor(ClientPtr client) { CursorPtr pCursor; int nscr; ScreenPtr pscr; Bool displayed; REQUEST(xRecolorCursorReq); REQUEST_SIZE_MATCH(xRecolorCursorReq); pCursor = (CursorPtr)SecurityLookupIDByType(client, stuff->cursor, RT_CURSOR, DixWriteAccess); if ( !pCursor) { client->errorValue = stuff->cursor; return (BadCursor); } pCursor->foreRed = stuff->foreRed; pCursor->foreGreen = stuff->foreGreen; pCursor->foreBlue = stuff->foreBlue; pCursor->backRed = stuff->backRed; pCursor->backGreen = stuff->backGreen; pCursor->backBlue = stuff->backBlue; for (nscr = 0; nscr < screenInfo.numScreens; nscr++) { pscr = screenInfo.screens[nscr]; #ifdef PANORAMIX if(!noPanoramiXExtension) displayed = (pscr == sprite.screen); else #endif displayed = (pscr == sprite.hotPhys.pScreen); ( *pscr->RecolorCursor)(pscr, pCursor, (pCursor == sprite.current) && displayed); } return (Success); } /** * Write the given events to a client, swapping the byte order if necessary. * To swap the byte ordering, a callback is called that has to be set up for * the given event type. * * In the case of DeviceMotionNotify trailed by DeviceValuators, the events * can be more than one. Usually it's just one event. * * Do not modify the event structure passed in. See comment below. * * @param pClient Client to send events to. * @param count Number of events. * @param events The event list. */ _X_EXPORT void WriteEventsToClient(ClientPtr pClient, int count, xEvent *events) { #ifdef PANORAMIX xEvent eventCopy; #endif xEvent eventTo, *eventFrom; int i; #ifdef XKB if ((!noXkbExtension)&&(!XkbFilterEvents(pClient, count, events))) return; #endif #ifdef PANORAMIX if(!noPanoramiXExtension && (panoramiXdataPtr[0].x || panoramiXdataPtr[0].y)) { switch(events->u.u.type) { case MotionNotify: case ButtonPress: case ButtonRelease: case KeyPress: case KeyRelease: case EnterNotify: case LeaveNotify: /* When multiple clients want the same event DeliverEventsToWindow passes the same event structure multiple times so we can't modify the one passed to us */ count = 1; /* should always be 1 */ memcpy(&eventCopy, events, sizeof(xEvent)); eventCopy.u.keyButtonPointer.rootX += panoramiXdataPtr[0].x; eventCopy.u.keyButtonPointer.rootY += panoramiXdataPtr[0].y; if(eventCopy.u.keyButtonPointer.event == eventCopy.u.keyButtonPointer.root) { eventCopy.u.keyButtonPointer.eventX += panoramiXdataPtr[0].x; eventCopy.u.keyButtonPointer.eventY += panoramiXdataPtr[0].y; } events = &eventCopy; break; default: break; } } #endif if (EventCallback) { EventInfoRec eventinfo; eventinfo.client = pClient; eventinfo.events = events; eventinfo.count = count; CallCallbacks(&EventCallback, (pointer)&eventinfo); } #ifdef XSERVER_DTRACE if (XSERVER_SEND_EVENT_ENABLED()) { for (i = 0; i < count; i++) { XSERVER_SEND_EVENT(pClient->index, events[i].u.u.type, &events[i]); } } #endif if(pClient->swapped) { for(i = 0; i < count; i++) { eventFrom = &events[i]; /* Remember to strip off the leading bit of type in case this event was sent with "SendEvent." */ (*EventSwapVector[eventFrom->u.u.type & 0177]) (eventFrom, &eventTo); (void)WriteToClient(pClient, sizeof(xEvent), (char *)&eventTo); } } else { (void)WriteToClient(pClient, count * sizeof(xEvent), (char *) events); } }